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