Skip to content

Commit

Permalink
Add buffer read to AudioBufferManager and I2S (#2777)
Browse files Browse the repository at this point in the history
* 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.
  • Loading branch information
relic-se authored Jan 26, 2025
1 parent e133147 commit a426fbf
Show file tree
Hide file tree
Showing 6 changed files with 91 additions and 2 deletions.
9 changes: 8 additions & 1 deletion docs/i2s.rst
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ size_t write(const uint8_t \*buffer, size_t size)
Transfers number of bytes from an application buffer to the I2S output buffer.
Be aware that ``size`` is in *bytes** and not samples. Size must be a multiple
of **4 bytes**. Will not block, so check the return value to find out how
many bytes were actually written.
many 32-bit words were actually written.

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

size_t read(uint8_t \*buffer, size_t size)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Transfers number of bytes from the I2S input buffer to an application buffer.
Be aware that ``size`` is in *bytes** and not samples. Size must be a multiple
of **4 bytes**. Will not block, so check the return value to find out how
many 32-bit words were actually read.

void onTransmit(void (\*fn)(void))
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Sets a callback to be called when an I2S DMA buffer is fully transmitted.
Expand Down
33 changes: 32 additions & 1 deletion libraries/AudioBufferManager/src/AudioBufferManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,6 @@ size_t AudioBufferManager::write(const uint32_t *v, size_t words, bool sync) {
/* noop busy wait */
}
}

}
size_t availToWriteThisBuff = _wordsPerBuffer - _userOff;
size_t toWrite = std::min(availToWriteThisBuff, words);
Expand Down Expand Up @@ -245,6 +244,38 @@ bool AudioBufferManager::read(uint32_t *v, bool sync) {
return true;
}

size_t AudioBufferManager::read(uint32_t *v, size_t words, bool sync) {
size_t read = 0;

if (!_running || _isOutput) {
return 0;
}
while (words) {
AudioBuffer ** volatile p = (AudioBuffer ** volatile)&_filled;
if (!*p) {
if (!sync) {
return read;
} else {
while (!*p) {
/* noop busy wait */
}
}
}
size_t availToReadThisBuff = _wordsPerBuffer - _userOff;
size_t toRead = std::min(availToReadThisBuff, words);
memcpy((void *)v, &((*p)->buff[_userOff]), toRead * sizeof(uint32_t));
v += toRead;
read += toRead;
_userOff += toRead;
words -= toRead;
if (_userOff == _wordsPerBuffer) {
_addToList(&_empty, _takeFromList(p));
_userOff = 0;
}
}
return read;
}

bool AudioBufferManager::getOverUnderflow() {
bool hold = _overunderflow;
_overunderflow = false;
Expand Down
1 change: 1 addition & 0 deletions libraries/AudioBufferManager/src/AudioBufferManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ class AudioBufferManager {
bool write(uint32_t v, bool sync = true);
size_t write(const uint32_t *v, size_t words, bool sync = true);
bool read(uint32_t *v, bool sync = true);
size_t read(uint32_t *v, size_t words, bool sync = true);
void flush();

bool getOverUnderflow();
Expand Down
39 changes: 39 additions & 0 deletions libraries/I2S/examples/I2SLoopback_Buffer/I2SLoopback_Buffer.ino
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/*
I2S bi-directional input and output buffered loopback example
Released to the Public Domain by Cooper Dalrymple
*/

#include <I2S.h>

I2S i2s(INPUT_PULLUP);

#define SIZE 256
int16_t buffer[SIZE];

void setup() {
Serial.begin(115200);

i2s.setDOUT(0);
i2s.setDIN(1);
i2s.setBCLK(2); // Note: LRCLK = BCLK + 1
i2s.setBitsPerSample(16);
i2s.setFrequency(22050);
i2s.setBuffers(6, SIZE * sizeof(int16_t) / sizeof(uint32_t));
i2s.begin();

size_t count, index;
while (1) {
count = i2s.read((uint8_t *)&buffer, SIZE * sizeof(int16_t)) * sizeof(uint32_t) / sizeof(int16_t);
index = 0;
while (index < count) {
// Reduce volume by half
buffer[index++] >>= 1; // right
buffer[index++] >>= 1; // left
}
i2s.write((const uint8_t *)&buffer, count * sizeof(int16_t));
}
}

void loop() {
/* Nothing here */
}
8 changes: 8 additions & 0 deletions libraries/I2S/src/I2S.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -559,6 +559,14 @@ bool I2S::read32(int32_t *l, int32_t *r) {
return true;
}

size_t I2S::read(uint8_t *buffer, size_t size) {
// We can only read 32-bit chunks here
if (size & 0x3 || !_running || !_isInput) {
return 0;
}
return _arbInput->read((uint32_t *)buffer, size / sizeof(uint32_t), false);
}

size_t I2S::write(const uint8_t *buffer, size_t size) {
// We can only write 32-bit chunks here
if (size & 0x3 || !_running || !_isOutput) {
Expand Down
3 changes: 3 additions & 0 deletions libraries/I2S/src/I2S.h
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,9 @@ class I2S : public Stream, public AudioOutputBase {
bool read24(int32_t *l, int32_t *r); // Note that 24b reads will be left-aligned (see above)
bool read32(int32_t *l, int32_t *r);

// Read samples into buffer
size_t read(uint8_t *buffer, size_t size);

// Note that these callback are called from **INTERRUPT CONTEXT** and hence
// should be in RAM, not FLASH, and should be quick to execute.
void onTransmit(void(*)(void));
Expand Down

0 comments on commit a426fbf

Please sign in to comment.