arduino-audio-tools
Loading...
Searching...
No Matches
I2SDriverRP2040.h
Go to the documentation of this file.
1#pragma once
2
5#if defined(RP2040_HOWER)
6#include <I2S.h>
7
8#define IS_I2S_IMPLEMENTED
9
10namespace audio_tools {
11
19class I2SDriverRP2040 : public I2SDriverBase {
20 friend class I2SStream;
21
22 public:
24 I2SConfigStd defaultConfig(RxTxMode mode) {
25 I2SConfigStd c(mode);
26 return c;
27 }
29 bool setAudioInfo(AudioInfo info) {
30 if (info.sample_rate != cfg.sample_rate && !i2s.setFrequency(info.sample_rate)) {
31 LOGI("i2s.setFrequency %d failed", info.sample_rate);
32 return false;
33 }
34 if (info.bits_per_sample != cfg.bits_per_sample && !i2s.setBitsPerSample(info.bits_per_sample)) {
35 LOGI("i2s.setBitsPerSample %d failed", info.bits_per_sample);
36 return false;
37 }
38 cfg.sample_rate = info.sample_rate;
39 cfg.bits_per_sample = info.bits_per_sample;
40 cfg.channels = info.channels;
41 return true;
42 }
43
45 bool begin(RxTxMode mode = TX_MODE) {
46 TRACED();
47 return begin(defaultConfig(mode));
48 }
49
51 bool begin(I2SConfigStd cfg) {
52 TRACEI();
53 // prevent multiple begins w/o calling end
54 if (is_active) end();
55
56 has_input[0] = has_input[1] = false;
57 this->cfg = cfg;
58 cfg.logInfo();
59
60 if (!setupMode()) return false;
61 if (!setupSlaveMode()) return false;
62 if (!setupClockPins()) return false;
63 if (!setupDataPins()) return false;
64 if (!setupMCK()) return false;
65 if (!setupAudioParameters()) return false;
66
67 if (!i2s.begin(cfg.sample_rate)) {
68 LOGE("Could not start I2S");
69 return false;
70 }
71 is_active = true;
72 return true;
73 }
74
76 void end() {
77 flush();
78 i2s.end();
79 is_active = false;
80 }
81
83 I2SConfigStd config() { return cfg; }
84
86 size_t writeBytes(const void *src, size_t size_bytes) {
87 LOGD("writeBytes(%d)", size_bytes);
88 size_t result = 0;
89
90 if (cfg.channels == 1) {
91 result = writeExpandChannel(src, size_bytes);
92 } else if (cfg.channels == 2) {
93 const uint8_t *p = (const uint8_t *)src;
94 while (size_bytes >= sizeof(int32_t)) {
95 bool justWritten = i2s.write(
96 *(int32_t *)p,
97 true); // I2S::write(int32,bool) actually only returns 0 or 1
98 if (justWritten) {
99 size_bytes -= sizeof(int32_t);
100 p += sizeof(int32_t);
101 result += sizeof(int32_t);
102 } else
103 return result;
104 }
105 }
106 return result;
107 }
108
109 size_t readBytes(void *dest, size_t size_bytes) {
110 TRACED();
111 switch (cfg.channels) {
112 case 1:
113 return read1Channel(dest, size_bytes);
114 case 2:
115 return read2Channels(dest, size_bytes);
116 }
117 return 0;
118 }
119
120 int availableForWrite() {
121 if (cfg.channels == 1) {
122 return cfg.buffer_size;
123 } else {
124 return i2s.availableForWrite();
125 }
126 }
127
128 int available() { return min(i2s.available(), cfg.buffer_size); }
129
130 void flush() { i2s.flush(); }
131
132 bool getOverUnderflow() {
133 return i2s.getOverUnderflow() ;
134 }
135
136 protected:
137 I2SConfigStd cfg;
138 I2S i2s;
139 bool has_input[2];
140 bool is_active = false;
141
143 bool setupMode() {
144 switch (cfg.rx_tx_mode) {
145 case TX_MODE:
146 i2s = I2S(OUTPUT);
147 break;
148 case RX_MODE:
149 i2s = I2S(INPUT);
150 break;
151 case RXTX_MODE:
152 i2s = I2S(INPUT_PULLUP);
153 break;
154 default:
155 LOGE("Unsupported mode");
156 return false;
157 }
158 return true;
159 }
160
162 bool setupSlaveMode() {
163#if (ARDUINO_PICO_MAJOR >= 5 && ARDUINO_PICO_MINOR >= 3)
164 if (!cfg.is_master) {
165 if (!i2s.setSlave()) {
166 LOGE("Could not set slave mode");
167 return false;
168 }
169 }
170#endif
171 return true;
172 }
173
175 bool setupClockPins() {
176 if (cfg.pin_ws == cfg.pin_bck + 1) { // normal pin order
177 if (!i2s.setBCLK(cfg.pin_bck)) {
178 LOGE("Could not set bck pin: %d", cfg.pin_bck);
179 return false;
180 }
181 } else if (cfg.pin_ws == cfg.pin_bck - 1) { // reverse pin order
182 if (!i2s.swapClocks() ||
183 !i2s.setBCLK(
184 cfg.pin_ws)) { // setBCLK() actually sets the lower pin of bck/ws
185 LOGE("Could not set bck pin: %d", cfg.pin_bck);
186 return false;
187 }
188 } else {
189 LOGE("pins bck: '%d' and ws: '%d' must be next to each other",
190 cfg.pin_bck, cfg.pin_ws);
191 return false;
192 }
193 return true;
194 }
195
197 bool setupDataPins() {
198 if (cfg.rx_tx_mode == RXTX_MODE) {
199 if (!i2s.setDOUT(cfg.pin_data)) {
200 LOGE("Could not set pin_data: %d with setDOUT()", cfg.pin_data);
201 return false;
202 }
203 if (!i2s.setDIN(cfg.pin_data_rx)) {
204 LOGE("Could not set pin_data_rx: %d with setDIN()", cfg.pin_data);
205 return false;
206 }
207 } else {
208 // use pin_data (if not set use pin_data_rx)
209 int pin = cfg.pin_data;
210 if (pin == -1) pin = cfg.pin_data_rx;
211 if (!i2s.setDATA(pin)) {
212 LOGE("Could not set pin_data: %d with pin_data()", pin);
213 return false;
214 }
215 }
216 return true;
217 }
218
220 bool setupMCK() {
221 if (cfg.pin_mck != -1) {
222 LOGI("Using MCK pin: %d with multiplier %d", cfg.pin_mck,
223 cfg.mck_multiplier);
224 i2s.setMCLKmult(cfg.mck_multiplier);
225 if (!i2s.setMCLK(cfg.pin_mck)) {
226 LOGE("Could not set data pin: %d", cfg.pin_mck);
227 return false;
228 }
229 }
230 return true;
231 }
232
234 bool setupAudioParameters() {
235 if (cfg.bits_per_sample == 8 ||
236 !i2s.setBitsPerSample(cfg.bits_per_sample)) {
237 LOGE("Could not set bits per sample: %d", cfg.bits_per_sample);
238 return false;
239 }
240
241 if (!i2s.setBuffers(cfg.buffer_count, cfg.buffer_size)) {
242 LOGE("Could not set buffers: Count: '%d', size: '%d'", cfg.buffer_count,
243 cfg.buffer_size);
244 return false;
245 }
246
247 // setup format
248 if (cfg.i2s_format == I2S_STD_FORMAT ||
249 cfg.i2s_format == I2S_PHILIPS_FORMAT) {
250 // default setting: do nothing
251 } else if (cfg.i2s_format == I2S_LEFT_JUSTIFIED_FORMAT ||
252 cfg.i2s_format == I2S_LSB_FORMAT) {
253 if (!i2s.setLSBJFormat()) {
254 LOGE("Could not set LSB Format")
256 }
257 } else {
258 LOGE("Unsupported I2S format");
259 return false;
260 }
261
262 if (cfg.signal_type != TDM && (cfg.channels < 1 || cfg.channels > 2)) {
263 LOGE("Unsupported channels: '%d'", cfg.channels);
264 return false;
265 }
266
267 if (cfg.signal_type == TDM) {
268 i2s.setTDMFormat();
269 i2s.setTDMChannels(cfg.channels);
270 }
271 return true;
272 }
273
275 // returns amount of bytes written from src to i2s
276 size_t writeExpandChannel(const void *src, size_t size_bytes) {
277 switch (cfg.bits_per_sample) {
278 // case 8: {
279 // int8_t *pt8 = (int8_t*) src;
280 // int16_t sample16 = static_cast<int16_t>(*pt8) << 8;
281 // for (int j=0;j<size_bytes;j++){
282 // // 8 bit does not work
283 // i2s.write8(pt8[j], pt8[j]);
284 // //LOGI("%d", pt8[j]);
285 // }
286 // } break;
287 case 16: {
288 int16_t *pt16 = (int16_t *)src;
289 for (int j = 0; j < size_bytes / sizeof(int16_t); j++) {
290 i2s.write16(pt16[j], pt16[j]);
291 }
292 } break;
293 case 24: {
294 int32_t *pt24 = (int32_t *)src;
295 for (int j = 0; j < size_bytes / sizeof(int32_t); j++) {
296 i2s.write24(pt24[j], pt24[j]);
297 }
298 } break;
299 case 32: {
300 int32_t *pt32 = (int32_t *)src;
301 for (int j = 0; j < size_bytes / sizeof(int32_t); j++) {
302 i2s.write32(pt32[j], pt32[j]);
303 }
304 } break;
305 }
306 return size_bytes;
307 }
308
310 size_t read2Channels(void *dest, size_t size_bytes) {
311 TRACED();
312 size_t result = 0;
313 switch (cfg.bits_per_sample) {
314 // case 8:{
315 // int8_t *data = (int8_t*)dest;
316 // for (int j=0;j<size_bytes;j+=2){
317 // if (i2s.read8(data+j, data+j+1)){
318 // result+=2;;
319 // } else {
320 // return result;
321 // }
322 // }
323 // }break;
324
325 case 16: {
326 int16_t *data = (int16_t *)dest;
327 for (int j = 0; j < size_bytes / sizeof(int16_t); j += 2) {
328 if (i2s.read16(data + j, data + j + 1)) {
329 result += 4;
330 } else {
331 return result;
332 }
333 }
334 } break;
335
336 case 24: {
337 int32_t *data = (int32_t *)dest;
338 for (int j = 0; j < size_bytes / sizeof(int32_t); j += 2) {
339 if (i2s.read24(data + j, data + j + 1)) {
340 result += 8;
341 } else {
342 return result;
343 }
344 }
345 } break;
346
347 case 32: {
348 int32_t *data = (int32_t *)dest;
349 for (int j = 0; j < size_bytes / sizeof(int32_t); j += 2) {
350 if (i2s.read32(data + j, data + j + 1)) {
351 result += 8;
352 } else {
353 return result;
354 }
355 }
356
357 } break;
358 }
359 return result;
360 }
361
363 size_t read1Channel(void *dest, size_t size_bytes) {
364 TRACED();
365 size_t result = 0;
366 switch (cfg.bits_per_sample) {
367 // case 8:{
368 // int8_t tmp[2];
369 // int8_t *data = (int8_t*)dest;
370 // for (int j=0;j<size_bytes;j++){
371 // if (i2s.read8(tmp, tmp+1)){
372 // data[j] = mix(tmp[0], tmp[1]);
373 // result++;;
374 // } else {
375 // return result;
376 // }
377 // }
378 // }break;
379
380 case 16: {
381 int16_t tmp[2];
382 int16_t *data = (int16_t *)dest;
383 for (int j = 0; j < size_bytes / sizeof(int16_t); j++) {
384 if (i2s.read16(tmp, tmp + 1)) {
385 data[j] = mix(tmp[0], tmp[1]);
386 result += 2;
387 } else {
388 return result;
389 }
390 }
391 } break;
392
393 case 24: {
394 int32_t tmp[2];
395 int32_t *data = (int32_t *)dest;
396 for (int j = 0; j < size_bytes / sizeof(int32_t); j++) {
397 if (i2s.read24(tmp, tmp + 1)) {
398 data[j] = mix(tmp[0], tmp[1]);
399 result += 4;
400 } else {
401 return result;
402 }
403 }
404 } break;
405
406 case 32: {
407 int32_t tmp[2];
408 int32_t *data = (int32_t *)dest;
409 for (int j = 0; j < size_bytes / sizeof(int32_t); j++) {
410 if (i2s.read32(tmp, tmp + 1)) {
411 data[j] = mix(tmp[0], tmp[1]);
412 result += 4;
413 } else {
414 return result;
415 }
416 }
417 } break;
418 }
419 return result;
420 }
421
422 // we just provide the avg of both samples
423 template <class T>
424 T mix(T left, T right) {
425 if (left != 0) has_input[0] = true;
426 if (right != 0) has_input[1] = true;
427
428 // if right is always empty we return left
429 if (has_input[0] && !has_input[1]) {
430 return left;
431 }
432
433 // if left is always empty we return right
434 if (!has_input[0] && has_input[1]) {
435 return right;
436 }
437
438 return (left / 2) + (right / 2);
439 }
440};
441
443
444} // namespace audio_tools
445
446#endif
#define INPUT
Definition Arduino.h:34
#define OUTPUT
Definition Arduino.h:38
#define INPUT_PULLUP
Definition Arduino.h:42
#define TRACEI()
Definition AudioLoggerIDF.h:32
#define TRACED()
Definition AudioLoggerIDF.h:31
#define LOGI(...)
Definition AudioLoggerIDF.h:28
#define LOGD(...)
Definition AudioLoggerIDF.h:27
#define LOGE(...)
Definition AudioLoggerIDF.h:30
RxTxMode
The Microcontroller is the Audio Source (TX_MODE) or Audio Sink (RX_MODE). RXTX_MODE is Source and Si...
Definition AudioTypes.h:26
@ RXTX_MODE
Definition AudioTypes.h:26
@ TX_MODE
Definition AudioTypes.h:26
@ RX_MODE
Definition AudioTypes.h:26
Generic Implementation of sound input and output for desktop environments using portaudio.
Definition AudioCodecsBase.h:10
@ TDM
Definition AudioTypes.h:441
@ I2S_STD_FORMAT
Definition AudioTypes.h:417
@ I2S_PHILIPS_FORMAT
Definition AudioTypes.h:420
@ I2S_LSB_FORMAT
Definition AudioTypes.h:418
@ I2S_LEFT_JUSTIFIED_FORMAT
Definition AudioTypes.h:422
I2SDriverESP32 I2SDriver
Definition I2SDriverESP32.h:403
size_t writeData(Print *p_out, T *data, int samples, int maxSamples=512)
Definition AudioTypes.h:508
constexpr const _Ep * end(initializer_list< _Ep > __il) noexcept
Definition InitializerList.h:63
constexpr const _Ep * begin(initializer_list< _Ep > __il) noexcept
Definition InitializerList.h:55