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