arduino-audio-tools
I2SESP32V1.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 "AudioI2S/I2SConfig.h"
9 #include "driver/i2s_pdm.h"
10 #include "driver/i2s_std.h"
11 #include "driver/i2s_tdm.h"
12 #include "esp_system.h"
13 
14 namespace audio_tools {
15 
24  public:
27  I2SConfigESP32V1 c(mode);
28  return c;
29  }
31  bool setAudioInfo(AudioInfo info) {
32  // nothing to do
33  if (is_started) {
34  if (info.equals(cfg)) return true;
35  if (info.equalsExSampleRate(cfg)) {
36  cfg.sample_rate = info.sample_rate;
37  LOGI("i2s_set_sample_rates: %d", (int) info.sample_rate);
38  return getDriver(cfg).changeSampleRate(cfg, rx_chan, tx_chan);
39  }
40  } else {
41  LOGE("not started");
42  }
43  return false;
44  }
45 
47  bool begin(RxTxMode mode) { return begin(defaultConfig(mode)); }
48 
51  bool begin() { return (!is_started) ? begin(cfg) : true; }
52 
54  bool begin(I2SConfigESP32V1 cfg) {
55  TRACED();
56  this->cfg = cfg;
57 
58  // stop if it is already open
59  if (is_started) end();
60 
61  switch (cfg.rx_tx_mode) {
62  case TX_MODE:
63  return begin(cfg, cfg.pin_data, I2S_GPIO_UNUSED);
64  case RX_MODE:
65  // usually we expet cfg.pin_data but if the used assinged rx we might
66  // consider this one
67  return begin(cfg, I2S_GPIO_UNUSED,
68  cfg.pin_data_rx != I2S_GPIO_UNUSED ? cfg.pin_data_rx
69  : cfg.pin_data);
70  default:
71  return begin(cfg, cfg.pin_data, cfg.pin_data_rx);
72  }
73  LOGE("Did not expect go get here");
74  }
75 
77  int available() { return I2S_BUFFER_COUNT * I2S_BUFFER_SIZE; }
78 
80  int availableForWrite() { return I2S_BUFFER_COUNT * I2S_BUFFER_SIZE; }
81 
83  void end() {
84  TRACED();
85  if (rx_chan != nullptr) {
86  i2s_channel_disable(rx_chan);
87  i2s_del_channel(rx_chan);
88  rx_chan = nullptr;
89  }
90  if (tx_chan != nullptr) {
91  i2s_channel_disable(tx_chan);
92  i2s_del_channel(tx_chan);
93  tx_chan = nullptr;
94  }
95 
96  is_started = false;
97  }
98 
100  I2SConfigESP32V1 config() { return cfg; }
101 
103  size_t writeBytes(const void *src, size_t size_bytes) {
104  TRACED();
105  size_t result;
106  if (i2s_channel_write(tx_chan, src, size_bytes, &result,
107  ticks_to_wait_write) != ESP_OK) {
108  TRACEE();
109  }
110  return result;
111  }
112 
113  size_t readBytes(void *dest, size_t size_bytes) {
114  size_t result = 0;
115  if (i2s_channel_read(rx_chan, dest, size_bytes, &result,
116  ticks_to_wait_read) != ESP_OK) {
117  TRACEE();
118  }
119  return result;
120  }
121 
122  void setWaitTimeReadMs(TickType_t ms) {
123  ticks_to_wait_read = pdMS_TO_TICKS(ms);
124  }
125  void setWaitTimeWriteMs(TickType_t ms) {
126  ticks_to_wait_write = pdMS_TO_TICKS(ms);
127  }
128 
129  protected:
130  I2SConfigESP32V1 cfg = defaultConfig(RX_MODE);
131  i2s_std_config_t i2s_config;
132  i2s_chan_handle_t tx_chan = nullptr; // I2S tx channel handler
133  i2s_chan_handle_t rx_chan = nullptr; // I2S rx channel handler
134  bool is_started = false;
135  TickType_t ticks_to_wait_read = portMAX_DELAY;
136  TickType_t ticks_to_wait_write = portMAX_DELAY;
137 
138  struct DriverCommon {
139  virtual i2s_chan_config_t getChannelConfig(I2SConfigESP32V1 &cfg) = 0;
140  virtual bool startChannels(I2SConfigESP32V1 &cfg,
141  i2s_chan_handle_t &tx_chan,
142  i2s_chan_handle_t &rx_chan, int txPin,
143  int rxPin) = 0;
144  // changes the sample rate
145  virtual bool changeSampleRate(I2SConfigESP32V1 &cfg,
146  i2s_chan_handle_t &tx_chan,
147  i2s_chan_handle_t &rx_chan) {
148  return false;
149  }
150  };
151 
152  struct DriverI2S : public DriverCommon {
153  i2s_std_slot_config_t getSlotConfig(I2SConfigESP32V1 &cfg) {
154  TRACED();
155  switch (cfg.i2s_format) {
156  case I2S_RIGHT_JUSTIFIED_FORMAT:
157  case I2S_LSB_FORMAT:
158  case I2S_PHILIPS_FORMAT:
159  case I2S_STD_FORMAT:
160  return I2S_STD_PHILIPS_SLOT_DEFAULT_CONFIG(
161  (i2s_data_bit_width_t)cfg.bits_per_sample,
162  (i2s_slot_mode_t)cfg.channels);
163  case I2S_LEFT_JUSTIFIED_FORMAT:
164  case I2S_MSB_FORMAT:
165  return I2S_STD_MSB_SLOT_DEFAULT_CONFIG(
166  (i2s_data_bit_width_t)cfg.bits_per_sample,
167  (i2s_slot_mode_t)cfg.channels);
168  case I2S_PCM:
169  return I2S_STD_PCM_SLOT_DEFAULT_CONFIG(
170  (i2s_data_bit_width_t)cfg.bits_per_sample,
171  (i2s_slot_mode_t)cfg.channels);
172  }
173  // use default config
174  TRACEE();
175  return I2S_STD_PHILIPS_SLOT_DEFAULT_CONFIG(
176  (i2s_data_bit_width_t)cfg.bits_per_sample,
177  (i2s_slot_mode_t)cfg.channels);
178  }
179 
180  i2s_chan_config_t getChannelConfig(I2SConfigESP32V1 &cfg) {
181  TRACED();
182  return I2S_CHANNEL_DEFAULT_CONFIG(
183  (i2s_port_t)cfg.port_no,
184  cfg.is_master ? I2S_ROLE_MASTER : I2S_ROLE_SLAVE);
185  }
186 
187  i2s_std_clk_config_t getClockConfig(I2SConfigESP32V1 &cfg) {
188  TRACED();
189  i2s_std_clk_config_t clk_cfg =
190  I2S_STD_CLK_DEFAULT_CONFIG((uint32_t)cfg.sample_rate);
191  if (cfg.bits_per_sample == 24) {
192  // mclk_multiple' should be the multiple of 3 while using 24-bit
193  clk_cfg.mclk_multiple = I2S_MCLK_MULTIPLE_384;
194  }
195  return clk_cfg;
196  }
197 
198  bool startChannels(I2SConfigESP32V1 &cfg, i2s_chan_handle_t &tx_chan,
199  i2s_chan_handle_t &rx_chan, int txPin, int rxPin) {
200  TRACED();
201  LOGI("tx: %d, rx: %d", txPin, rxPin);
202  i2s_std_config_t std_cfg = {
203  .clk_cfg = getClockConfig(cfg),
204  .slot_cfg = getSlotConfig(cfg),
205  .gpio_cfg =
206  {
207  .mclk = (gpio_num_t)cfg.pin_mck,
208  .bclk = (gpio_num_t)cfg.pin_bck,
209  .ws = (gpio_num_t)cfg.pin_ws,
210  .dout = (gpio_num_t)txPin,
211  .din = (gpio_num_t)rxPin,
212  .invert_flags =
213  {
214  .mclk_inv = false,
215  .bclk_inv = false,
216  .ws_inv = false,
217  },
218  },
219  };
220 
221  if (cfg.rx_tx_mode == RXTX_MODE || cfg.rx_tx_mode == TX_MODE) {
222  if (i2s_channel_init_std_mode(tx_chan, &std_cfg) != ESP_OK) {
223  LOGE("i2s_channel_init_std_mode %s", "tx");
224  return false;
225  }
226  if (i2s_channel_enable(tx_chan) != ESP_OK) {
227  LOGE("i2s_channel_enable %s", "tx");
228  return false;
229  }
230  }
231 
232  if (cfg.rx_tx_mode == RXTX_MODE || cfg.rx_tx_mode == RX_MODE) {
233  if (i2s_channel_init_std_mode(rx_chan, &std_cfg) != ESP_OK) {
234  LOGE("i2s_channel_init_std_mode %s", "rx");
235  return false;
236  }
237  if (i2s_channel_enable(rx_chan) != ESP_OK) {
238  LOGE("i2s_channel_enable %s", "rx");
239  return false;
240  }
241  }
242 
243  LOGD("%s - %s", __func__, "started");
244  return true;
245  }
246 
247  bool changeSampleRate(I2SConfigESP32V1 &cfg, i2s_chan_handle_t &tx_chan,
248  i2s_chan_handle_t &rx_chan) override {
249  bool rc = false;
250  auto clock_cfg = getClockConfig(cfg);
251  if (tx_chan != nullptr) {
252  i2s_channel_disable(tx_chan);
253  rc = i2s_channel_reconfig_std_clock(tx_chan, &clock_cfg) == ESP_OK;
254  i2s_channel_enable(tx_chan);
255  }
256  if (rx_chan != nullptr) {
257  i2s_channel_disable(rx_chan);
258  rc = i2s_channel_reconfig_std_clock(rx_chan, &clock_cfg) == ESP_OK;
259  i2s_channel_enable(rx_chan);
260  }
261  return rc;
262  }
263 
264  } i2s;
265 
266 #ifdef USE_PDM
267 
268  struct DriverPDM : public DriverCommon {
269  i2s_pdm_tx_slot_config_t getTxSlotConfig(I2SConfigESP32V1 &cfg) {
270  return I2S_PDM_TX_SLOT_DEFAULT_CONFIG(
271  (i2s_data_bit_width_t)cfg.bits_per_sample,
272  (i2s_slot_mode_t)cfg.channels);
273  }
274 
275  i2s_chan_config_t getChannelConfig(I2SConfigESP32V1 &cfg) {
276  return I2S_CHANNEL_DEFAULT_CONFIG(
277  (i2s_port_t)cfg.port_no,
278  cfg.is_master ? I2S_ROLE_MASTER : I2S_ROLE_SLAVE);
279  }
280 
281  i2s_pdm_tx_clk_config_t getTxClockConfig(I2SConfigESP32V1 &cfg) {
282  return I2S_PDM_TX_CLK_DEFAULT_CONFIG((uint32_t)cfg.sample_rate);
283  }
284 
285  bool startChannels(I2SConfigESP32V1 &cfg, i2s_chan_handle_t &tx_chan,
286  i2s_chan_handle_t &rx_chan, int txPin, int rxPin) {
287  if (cfg.rx_tx_mode == TX_MODE) {
288  i2s_pdm_tx_config_t pdm_tx_cfg = {
289  .clk_cfg = getTxClockConfig(cfg),
290  .slot_cfg = getTxSlotConfig(cfg),
291  .gpio_cfg =
292  {
293  .clk = (gpio_num_t)cfg.pin_bck,
294  .dout = (gpio_num_t)txPin,
295  .invert_flags =
296  {
297  .clk_inv = false,
298  },
299  },
300  };
301 
302  if (i2s_channel_init_pdm_tx_mode(tx_chan, &pdm_tx_cfg) != ESP_OK) {
303  LOGE("i2s_channel_init_pdm_tx_mode %s", "tx");
304  return false;
305  }
306  if (i2s_channel_enable(tx_chan) != ESP_OK) {
307  LOGE("i2s_channel_enable %s", "tx");
308  return false;
309  }
310  } else {
311  LOGE("Only TX supported for PDM");
312  return false;
313  }
314  return true;
315  }
316  } pdm;
317 
318 #endif
319 
320 #ifdef USE_TDM
321  // example at
322  // https://github.com/espressif/esp-idf/blob/v5.3-dev/examples/peripherals/i2s/i2s_basic/i2s_tdm/main/i2s_tdm_example_main.c
323  struct DriverTDM : public DriverCommon {
324  i2s_tdm_slot_config_t getSlotConfig(I2SConfigESP32V1 &cfg) {
325  int slots = 0;
326  for (int j = 0; j < cfg.channels; j++) {
327  slots |= 1 << j;
328  }
329  return I2S_TDM_MSB_SLOT_DEFAULT_CONFIG(
330  (i2s_data_bit_width_t)cfg.bits_per_sample, I2S_SLOT_MODE_STEREO,
331  (i2s_tdm_slot_mask_t)slots);
332  }
333 
334  i2s_chan_config_t getChannelConfig(I2SConfigESP32V1 &cfg) {
335  return I2S_CHANNEL_DEFAULT_CONFIG(
336  (i2s_port_t)cfg.port_no,
337  cfg.is_master ? I2S_ROLE_MASTER : I2S_ROLE_SLAVE);
338  }
339 
340  i2s_tdm_clk_config_t getClockConfig(I2SConfigESP32V1 &cfg) {
341  return I2S_TDM_CLK_DEFAULT_CONFIG((uint32_t)cfg.sample_rate);
342  }
343 
344  bool startChannels(I2SConfigESP32V1 &cfg, i2s_chan_handle_t &tx_chan,
345  i2s_chan_handle_t &rx_chan, int txPin, int rxPin) {
346  i2s_tdm_config_t tdm_cfg = {
347  .clk_cfg = getClockConfig(cfg),
348  .slot_cfg = getSlotConfig(cfg),
349  .gpio_cfg =
350  {
351  .mclk = (gpio_num_t)cfg.pin_mck,
352  .bclk = (gpio_num_t)cfg.pin_bck,
353  .ws = (gpio_num_t)cfg.pin_ws,
354  .dout = (gpio_num_t)txPin,
355  .din = (gpio_num_t)rxPin,
356  .invert_flags =
357  {
358  .mclk_inv = false,
359  .bclk_inv = false,
360  .ws_inv = false,
361  },
362  },
363  };
364 
365  if (cfg.rx_tx_mode == TX_MODE) {
366  if (i2s_channel_init_tdm_mode(tx_chan, &tdm_cfg) != ESP_OK) {
367  LOGE("i2s_channel_init_tdm_tx_mode %s", "tx");
368  return false;
369  }
370  }
371  if (cfg.rx_tx_mode == RX_MODE) {
372  if (i2s_channel_init_tdm_mode(rx_chan, &tdm_cfg) != ESP_OK) {
373  LOGE("i2s_channel_init_tdm_tx_mode %s", "rx");
374  return false;
375  }
376  return true;
377  }
378  return false;
379  }
380  } tdm;
381 
382 #endif
383 
385 
387  bool begin(I2SConfigESP32V1 cfg, int txPin, int rxPin) {
388  TRACED();
389  cfg.logInfo();
390  this->cfg = cfg;
391  if (cfg.channels <= 0 || cfg.channels > 2) {
392  LOGE("invalid channels: %d", cfg.channels);
393  return false;
394  }
395 
396  DriverCommon &driver = getDriver(cfg);
397  if (!newChannels(cfg, driver)) {
398  end();
399  return false;
400  }
401 
402  is_started = driver.startChannels(cfg, tx_chan, rx_chan, txPin, rxPin);
403  if (!is_started) {
404  end();
405  LOGE("Channels not started");
406  }
407  return is_started;
408  }
409 
410  bool newChannels(I2SConfigESP32V1 &cfg, DriverCommon &driver) {
411  i2s_chan_config_t chan_cfg = driver.getChannelConfig(cfg);
412  switch (cfg.rx_tx_mode) {
413  case RX_MODE:
414  if (i2s_new_channel(&chan_cfg, NULL, &rx_chan) != ESP_OK) {
415  LOGE("i2s_channel");
416  return false;
417  }
418  break;
419  case TX_MODE:
420  if (i2s_new_channel(&chan_cfg, &tx_chan, NULL) != ESP_OK) {
421  LOGE("i2s_channel");
422  return false;
423  }
424  break;
425  default:
426  if (i2s_new_channel(&chan_cfg, &tx_chan, &rx_chan) != ESP_OK) {
427  LOGE("i2s_channel");
428  return false;
429  }
430  }
431  return true;
432  }
433 
434  DriverCommon &getDriver(I2SConfigESP32V1 &cfg) {
435  switch (cfg.signal_type) {
436  case Digital:
437  return i2s;
438 #ifdef USE_PDM
439  case Analog:
440  case PDM:
441  return pdm;
442 #endif
443 #ifdef USE_TDM
444  case TDM:
445  return tdm;
446 #endif
447  default:
448  break;
449  }
450  LOGE("Unsupported singal_type");
451  return i2s;
452  }
453 };
454 
455 using I2SDriver = I2SDriverESP32V1;
456 
457 } // namespace audio_tools
458 
459 #endif
Configuration for ESP32 i2s for IDF > 5.0.
Definition: I2SConfigESP32V1.h:17
RxTxMode rx_tx_mode
public settings
Definition: I2SConfigESP32V1.h:50
Basic I2S API for the ESP32 (using the new API). https://docs.espressif.com/projects/esp-idf/en/v5....
Definition: I2SESP32V1.h:23
I2SConfigESP32V1 defaultConfig(RxTxMode mode)
Provides the default configuration.
Definition: I2SESP32V1.h:26
bool begin(I2SConfigESP32V1 cfg)
starts the DAC
Definition: I2SESP32V1.h:54
int available()
we assume the data is already available in the buffer
Definition: I2SESP32V1.h:77
bool begin()
Definition: I2SESP32V1.h:51
bool setAudioInfo(AudioInfo info)
Potentially updates the sample rate (if supported)
Definition: I2SESP32V1.h:31
int availableForWrite()
We limit the write size to the buffer size.
Definition: I2SESP32V1.h:80
I2SConfigESP32V1 config()
provides the actual configuration
Definition: I2SESP32V1.h:100
void end()
stops the I2C and unistalls the driver
Definition: I2SESP32V1.h:83
bool begin(RxTxMode mode)
starts the DAC with the default config
Definition: I2SESP32V1.h:47
bool begin(I2SConfigESP32V1 cfg, int txPin, int rxPin)
-> protected methods from I2SDriverESP32V1
Definition: I2SESP32V1.h:387
size_t writeBytes(const void *src, size_t size_bytes)
writes the data to the I2S interface
Definition: I2SESP32V1.h:103
RxTxMode
The Microcontroller is the Audio Source (TX_MODE) or Audio Sink (RX_MODE). RXTX_MODE is Source and Si...
Definition: AudioTypes.h:26
Generic Implementation of sound input and output for desktop environments using portaudio.
Definition: AnalogAudio.h:10
Basic Audio information which drives e.g. I2S.
Definition: AudioTypes.h:50
sample_rate_t sample_rate
Sample Rate: e.g 44100.
Definition: AudioTypes.h:53
bool equals(AudioInfo alt)
Returns true if alt values are the same like the current values.
Definition: AudioTypes.h:83
uint16_t channels
Number of channels: 2=stereo, 1=mono.
Definition: AudioTypes.h:55
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:57
Definition: I2SESP32V1.h:152