Skip to content

Commit a426fbf

Browse files
authored
Add buffer read to AudioBufferManager and I2S (earlephilhower#2777)
* Added buffer read to `AudioBufferManager` and `I2S`. Example and documentation included. * Update type of words to unsigned in example. * Improve buffered loopback example. * Remove const from read buffer.
1 parent e133147 commit a426fbf

File tree

6 files changed

+91
-2
lines changed

6 files changed

+91
-2
lines changed

docs/i2s.rst

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,7 @@ size_t write(const uint8_t \*buffer, size_t size)
164164
Transfers number of bytes from an application buffer to the I2S output buffer.
165165
Be aware that ``size`` is in *bytes** and not samples. Size must be a multiple
166166
of **4 bytes**. Will not block, so check the return value to find out how
167-
many bytes were actually written.
167+
many 32-bit words were actually written.
168168

169169
int availableForWrite()
170170
~~~~~~~~~~~~~~~~~~~~~~~
@@ -181,6 +181,13 @@ int peek()
181181
Returns the next sample to be read from the I2S buffer (without actually
182182
removing it).
183183

184+
size_t read(uint8_t \*buffer, size_t size)
185+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
186+
Transfers number of bytes from the I2S input buffer to an application buffer.
187+
Be aware that ``size`` is in *bytes** and not samples. Size must be a multiple
188+
of **4 bytes**. Will not block, so check the return value to find out how
189+
many 32-bit words were actually read.
190+
184191
void onTransmit(void (\*fn)(void))
185192
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
186193
Sets a callback to be called when an I2S DMA buffer is fully transmitted.

libraries/AudioBufferManager/src/AudioBufferManager.cpp

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,6 @@ size_t AudioBufferManager::write(const uint32_t *v, size_t words, bool sync) {
204204
/* noop busy wait */
205205
}
206206
}
207-
208207
}
209208
size_t availToWriteThisBuff = _wordsPerBuffer - _userOff;
210209
size_t toWrite = std::min(availToWriteThisBuff, words);
@@ -245,6 +244,38 @@ bool AudioBufferManager::read(uint32_t *v, bool sync) {
245244
return true;
246245
}
247246

247+
size_t AudioBufferManager::read(uint32_t *v, size_t words, bool sync) {
248+
size_t read = 0;
249+
250+
if (!_running || _isOutput) {
251+
return 0;
252+
}
253+
while (words) {
254+
AudioBuffer ** volatile p = (AudioBuffer ** volatile)&_filled;
255+
if (!*p) {
256+
if (!sync) {
257+
return read;
258+
} else {
259+
while (!*p) {
260+
/* noop busy wait */
261+
}
262+
}
263+
}
264+
size_t availToReadThisBuff = _wordsPerBuffer - _userOff;
265+
size_t toRead = std::min(availToReadThisBuff, words);
266+
memcpy((void *)v, &((*p)->buff[_userOff]), toRead * sizeof(uint32_t));
267+
v += toRead;
268+
read += toRead;
269+
_userOff += toRead;
270+
words -= toRead;
271+
if (_userOff == _wordsPerBuffer) {
272+
_addToList(&_empty, _takeFromList(p));
273+
_userOff = 0;
274+
}
275+
}
276+
return read;
277+
}
278+
248279
bool AudioBufferManager::getOverUnderflow() {
249280
bool hold = _overunderflow;
250281
_overunderflow = false;

libraries/AudioBufferManager/src/AudioBufferManager.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ class AudioBufferManager {
3636
bool write(uint32_t v, bool sync = true);
3737
size_t write(const uint32_t *v, size_t words, bool sync = true);
3838
bool read(uint32_t *v, bool sync = true);
39+
size_t read(uint32_t *v, size_t words, bool sync = true);
3940
void flush();
4041

4142
bool getOverUnderflow();
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/*
2+
I2S bi-directional input and output buffered loopback example
3+
Released to the Public Domain by Cooper Dalrymple
4+
*/
5+
6+
#include <I2S.h>
7+
8+
I2S i2s(INPUT_PULLUP);
9+
10+
#define SIZE 256
11+
int16_t buffer[SIZE];
12+
13+
void setup() {
14+
Serial.begin(115200);
15+
16+
i2s.setDOUT(0);
17+
i2s.setDIN(1);
18+
i2s.setBCLK(2); // Note: LRCLK = BCLK + 1
19+
i2s.setBitsPerSample(16);
20+
i2s.setFrequency(22050);
21+
i2s.setBuffers(6, SIZE * sizeof(int16_t) / sizeof(uint32_t));
22+
i2s.begin();
23+
24+
size_t count, index;
25+
while (1) {
26+
count = i2s.read((uint8_t *)&buffer, SIZE * sizeof(int16_t)) * sizeof(uint32_t) / sizeof(int16_t);
27+
index = 0;
28+
while (index < count) {
29+
// Reduce volume by half
30+
buffer[index++] >>= 1; // right
31+
buffer[index++] >>= 1; // left
32+
}
33+
i2s.write((const uint8_t *)&buffer, count * sizeof(int16_t));
34+
}
35+
}
36+
37+
void loop() {
38+
/* Nothing here */
39+
}

libraries/I2S/src/I2S.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -559,6 +559,14 @@ bool I2S::read32(int32_t *l, int32_t *r) {
559559
return true;
560560
}
561561

562+
size_t I2S::read(uint8_t *buffer, size_t size) {
563+
// We can only read 32-bit chunks here
564+
if (size & 0x3 || !_running || !_isInput) {
565+
return 0;
566+
}
567+
return _arbInput->read((uint32_t *)buffer, size / sizeof(uint32_t), false);
568+
}
569+
562570
size_t I2S::write(const uint8_t *buffer, size_t size) {
563571
// We can only write 32-bit chunks here
564572
if (size & 0x3 || !_running || !_isOutput) {

libraries/I2S/src/I2S.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,9 @@ class I2S : public Stream, public AudioOutputBase {
127127
bool read24(int32_t *l, int32_t *r); // Note that 24b reads will be left-aligned (see above)
128128
bool read32(int32_t *l, int32_t *r);
129129

130+
// Read samples into buffer
131+
size_t read(uint8_t *buffer, size_t size);
132+
130133
// Note that these callback are called from **INTERRUPT CONTEXT** and hence
131134
// should be in RAM, not FLASH, and should be quick to execute.
132135
void onTransmit(void(*)(void));

0 commit comments

Comments
 (0)