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 int pin = cfg.pin_data;
209 // use tx pin for tx mode
210 if (cfg.rx_tx_mode == RX_MODE && cfg.pin_data_rx != -1) {
211 pin = cfg.pin_data_rx;
212 }
213 // use rx pin for rx mode
214 if (cfg.rx_tx_mode == TX_MODE && cfg.pin_data != -1) {
215 pin = cfg.pin_data;
216 }
217 // default to pin_data if no specific pin is set
218 if (pin == -1) pin = cfg.pin_data;
219
220 if (!i2s.setDATA(pin)) {
221 LOGE("Could not set pin_data: %d with pin_data()", pin);
222 return false;
223 }
224 }
225 return true;
226 }
227
229 bool setupMCK() {
230 if (cfg.pin_mck != -1) {
231 LOGI("Using MCK pin: %d with multiplier %d", cfg.pin_mck,
232 cfg.mck_multiplier);
233 i2s.setMCLKmult(cfg.mck_multiplier);
234 if (!i2s.setMCLK(cfg.pin_mck)) {
235 LOGE("Could not set data pin: %d", cfg.pin_mck);
236 return false;
237 }
238 }
239 return true;
240 }
241
243 bool setupAudioParameters() {
244 if (cfg.bits_per_sample == 8 ||
245 !i2s.setBitsPerSample(cfg.bits_per_sample)) {
246 LOGE("Could not set bits per sample: %d", cfg.bits_per_sample);
247 return false;
248 }
249
250 if (!i2s.setBuffers(cfg.buffer_count, cfg.buffer_size)) {
251 LOGE("Could not set buffers: Count: '%d', size: '%d'", cfg.buffer_count,
252 cfg.buffer_size);
253 return false;
254 }
255
256 // setup format
257 if (cfg.i2s_format == I2S_STD_FORMAT ||
258 cfg.i2s_format == I2S_PHILIPS_FORMAT) {
259 // default setting: do nothing
260 } else if (cfg.i2s_format == I2S_LEFT_JUSTIFIED_FORMAT ||
261 cfg.i2s_format == I2S_LSB_FORMAT) {
262 if (!i2s.setLSBJFormat()) {
263 LOGE("Could not set LSB Format")
264 return false;
265 }
266 } else {
267 LOGE("Unsupported I2S format");
268 return false;
269 }
270
271 if (cfg.signal_type != TDM && (cfg.channels < 1 || cfg.channels > 2)) {
272 LOGE("Unsupported channels: '%d'", cfg.channels);
273 return false;
274 }
275
276 if (cfg.signal_type == TDM) {
277 i2s.setTDMFormat();
278 i2s.setTDMChannels(cfg.channels);
279 }
280 return true;
281 }
282
284 // returns amount of bytes written from src to i2s
285 size_t writeExpandChannel(const void *src, size_t size_bytes) {
286 switch (cfg.bits_per_sample) {
287 // case 8: {
288 // int8_t *pt8 = (int8_t*) src;
289 // int16_t sample16 = static_cast<int16_t>(*pt8) << 8;
290 // for (int j=0;j<size_bytes;j++){
291 // // 8 bit does not work
292 // i2s.write8(pt8[j], pt8[j]);
293 // //LOGI("%d", pt8[j]);
294 // }
295 // } break;
296 case 16: {
297 int16_t *pt16 = (int16_t *)src;
298 for (int j = 0; j < size_bytes / sizeof(int16_t); j++) {
299 i2s.write16(pt16[j], pt16[j]);
300 }
301 } break;
302 case 24: {
303 int32_t *pt24 = (int32_t *)src;
304 for (int j = 0; j < size_bytes / sizeof(int32_t); j++) {
305 i2s.write24(pt24[j], pt24[j]);
306 }
307 } break;
308 case 32: {
309 int32_t *pt32 = (int32_t *)src;
310 for (int j = 0; j < size_bytes / sizeof(int32_t); j++) {
311 i2s.write32(pt32[j], pt32[j]);
312 }
313 } break;
314 }
315 return size_bytes;
316 }
317
319 size_t read2Channels(void *dest, size_t size_bytes) {
320 TRACED();
321 size_t result = 0;
322 switch (cfg.bits_per_sample) {
323 // case 8:{
324 // int8_t *data = (int8_t*)dest;
325 // for (int j=0;j<size_bytes;j+=2){
326 // if (i2s.read8(data+j, data+j+1)){
327 // result+=2;;
328 // } else {
329 // return result;
330 // }
331 // }
332 // }break;
333
334 case 16: {
335 int16_t *data = (int16_t *)dest;
336 for (int j = 0; j < size_bytes / sizeof(int16_t); j += 2) {
337 if (i2s.read16(data + j, data + j + 1)) {
338 result += 4;
339 } else {
340 return result;
341 }
342 }
343 } break;
344
345 case 24: {
346 int32_t *data = (int32_t *)dest;
347 for (int j = 0; j < size_bytes / sizeof(int32_t); j += 2) {
348 if (i2s.read24(data + j, data + j + 1)) {
349 result += 8;
350 } else {
351 return result;
352 }
353 }
354 } break;
355
356 case 32: {
357 int32_t *data = (int32_t *)dest;
358 for (int j = 0; j < size_bytes / sizeof(int32_t); j += 2) {
359 if (i2s.read32(data + j, data + j + 1)) {
360 result += 8;
361 } else {
362 return result;
363 }
364 }
365
366 } break;
367 }
368 return result;
369 }
370
372 size_t read1Channel(void *dest, size_t size_bytes) {
373 TRACED();
374 size_t result = 0;
375 switch (cfg.bits_per_sample) {
376 // case 8:{
377 // int8_t tmp[2];
378 // int8_t *data = (int8_t*)dest;
379 // for (int j=0;j<size_bytes;j++){
380 // if (i2s.read8(tmp, tmp+1)){
381 // data[j] = mix(tmp[0], tmp[1]);
382 // result++;;
383 // } else {
384 // return result;
385 // }
386 // }
387 // }break;
388
389 case 16: {
390 int16_t tmp[2];
391 int16_t *data = (int16_t *)dest;
392 for (int j = 0; j < size_bytes / sizeof(int16_t); j++) {
393 if (i2s.read16(tmp, tmp + 1)) {
394 data[j] = mix(tmp[0], tmp[1]);
395 result += 2;
396 } else {
397 return result;
398 }
399 }
400 } break;
401
402 case 24: {
403 int32_t tmp[2];
404 int32_t *data = (int32_t *)dest;
405 for (int j = 0; j < size_bytes / sizeof(int32_t); j++) {
406 if (i2s.read24(tmp, tmp + 1)) {
407 data[j] = mix(tmp[0], tmp[1]);
408 result += 4;
409 } else {
410 return result;
411 }
412 }
413 } break;
414
415 case 32: {
416 int32_t tmp[2];
417 int32_t *data = (int32_t *)dest;
418 for (int j = 0; j < size_bytes / sizeof(int32_t); j++) {
419 if (i2s.read32(tmp, tmp + 1)) {
420 data[j] = mix(tmp[0], tmp[1]);
421 result += 4;
422 } else {
423 return result;
424 }
425 }
426 } break;
427 }
428 return result;
429 }
430
431 // we just provide the avg of both samples
432 template <class T>
433 T mix(T left, T right) {
434 if (left != 0) has_input[0] = true;
435 if (right != 0) has_input[1] = true;
436
437 // if right is always empty we return left
438 if (has_input[0] && !has_input[1]) {
439 return left;
440 }
441
442 // if left is always empty we return right
443 if (!has_input[0] && has_input[1]) {
444 return right;
445 }
446
447 return (left / 2) + (right / 2);
448 }
449};
450
451using I2SDriver = I2SDriverRP2040;
452
453} // namespace audio_tools
454
455#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
@ 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 LMSEchoCancellationStream.h:6
I2SDriverESP32 I2SDriver
Definition I2SDriverESP32.h:403
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