Hi there,
I've adapted the ResamplingSdReader from @Moo to the serial flash raw player,
it works but with 8 players running I'm getting very high values when measuring the CPU with AudioProcessorUsageMax(), using teensy 4.1 btw,
any suggestion about how to reduce the CPU load?
here's the code:
play_serialflash_raw2.h
play_serialflash_raw2.cpp
resamplingFlashReader.h
resamplingFlashReader.cpp
I've adapted the ResamplingSdReader from @Moo to the serial flash raw player,
it works but with 8 players running I'm getting very high values when measuring the CPU with AudioProcessorUsageMax(), using teensy 4.1 btw,
any suggestion about how to reduce the CPU load?
here's the code:
play_serialflash_raw2.h
Code:
#ifndef play_serial_raw2_h_
#define play_serial_raw2_h_
#include "Arduino.h"
#include <AudioStream.h>
#include <SerialFlash.h>
#include "stdint.h"
#include "ResamplingFlashReader.h"
class AudioPlaySerialflashRaw2 : public AudioStream
{
public:
AudioPlaySerialflashRaw2(void) :
AudioStream(0, NULL),
rawReader()
{
begin();
}
void begin(void);
bool play(const char *filename);
void stop(void);
bool isPlaying(void) { return playing; }
uint32_t positionMillis(void);
uint32_t lengthMillis(void);
virtual void update(void);
void setPlaybackRate(float f) {
rawReader.setPlaybackRate(f);
}
float playbackRate(float f) {
return rawReader.playbackRate();
}
bool interpolationEnabled() {
return rawReader.interpolationEnabled();
}
void setInterpolationEnabled(bool enableInterpolation) {
rawReader.setInterpolationEnabled(enableInterpolation);
}
private:
uint32_t file_size;
volatile uint32_t file_offset;
volatile bool playing;
ResamplingFlashReader rawReader;
};
#endif
Code:
#include <Arduino.h>
#include "play_serialflash_raw2.h"
#include "spi_interrupt.h"
void AudioPlaySerialflashRaw2::begin(void)
{
playing = false;
file_offset = 0;
file_size = 0;
}
bool AudioPlaySerialflashRaw2::play(const char *filename)
{
stop();
playing = rawReader.play(filename);
return playing;
}
void AudioPlaySerialflashRaw2::stop(void)
{
rawReader.stop();
}
void AudioPlaySerialflashRaw2::update(void)
{
unsigned int i, n;
audio_block_t *block;
// only update if we're playing
if (!playing) return;
// allocate the audio blocks to transmit
block = allocate();
if (block == NULL) return;
if (rawReader.available()) {
// we can read more data from the file...
n = rawReader.read(block->data, AUDIO_BLOCK_SAMPLES*2);
file_offset += n;
for (i=n/2; i < AUDIO_BLOCK_SAMPLES; i++) {
block->data[i] = 0;
}
transmit(block);
} else {
rawReader.close();
AudioStopUsingSPI();
playing = false;
//Serial.println("Finished playing sample"); //TODO
}
release(block);
}
#define B2M (uint32_t)((double)4294967296000.0 / AUDIO_SAMPLE_RATE_EXACT / 2.0) // 97352592
uint32_t AudioPlaySerialflashRaw2::positionMillis(void)
{
return ((uint64_t)file_offset * B2M) >> 32;
}
uint32_t AudioPlaySerialflashRaw2::lengthMillis(void)
{
return ((uint64_t)file_size * B2M) >> 32;
}
Code:
#ifndef TEENSYAUDIOLIBRARY_RESAMPLINGFLASHREADER_H
#define TEENSYAUDIOLIBRARY_RESAMPLINGFLASHREADER_H
#include "stdint.h"
#include <SD.h>
#include <SerialFlash.h>
#include <AudioStream.h>
#include "../spi_interrupt.h"
const unsigned int ResamplingFlashReader_NUM_BUFFERS = 4;
class ResamplingFlashReader {
public:
ResamplingFlashReader() {
for (uint i=0; i<ResamplingFlashReader_NUM_BUFFERS; i++ ) {
_buffers[i] = NULL;
}
}
void begin(void);
bool play(const char *filename);
void stop(void);
bool isPlaying(void) { return _playing; }
int read(void *buf, uint16_t nbyte);
bool readNextValue(int16_t *value);
void setPlaybackRate(double f) {
_playbackRate = f;
}
float playbackRate() {
return _playbackRate;
}
int available(void);
void close(void);
bool interpolationEnabled() {
return _enable_interpolation;
}
void setInterpolationEnabled(bool enableInterpolation) {
_enable_interpolation = enableInterpolation;
}
private:
volatile bool _playing = false;
volatile uint32_t _file_offset;
volatile int32_t _last_read_offset = 0;
bool _enable_interpolation = true;
uint32_t _file_size;
double _playbackRate = 1.0;
double _remainder = 0.0;
uint _bufferPosition = 0;
int16_t *_buffers[AUDIO_BLOCK_SAMPLES];
unsigned int _bufferLength[ResamplingFlashReader_NUM_BUFFERS];
bool bufferIsAvailableForRead[ResamplingFlashReader_NUM_BUFFERS];
volatile signed char _readBuffer = -1;
volatile signed char _playBuffer = -1;
signed char _numBuffers = 0;
SerialFlashFile _file;
void updateBuffers(void);
bool isInRange(void);
};
#endif //TEENSYAUDIOLIBRARY_RESAMPLINGSDREADER_H
Code:
#include "ResamplingFlashReader.h"
int ResamplingFlashReader::read(void *buf, uint16_t nbyte) {
if (!_playing) return 0;
unsigned int count = 0;
int16_t *index = (int16_t*)buf;
for (int i=0; i< nbyte/2; i++) {
if (readNextValue(index))
count+=2;
else
return count;
index++;
}
return count;
}
bool ResamplingFlashReader::readNextValue(int16_t *value) {
if (_bufferLength[_playBuffer] == 0)
return false;
if (_playbackRate > 0 ) {
//forward playback
if (_numBuffers == 1 && _last_read_offset + _bufferPosition > _file_size)
return false;
//Serial.printf("readNextValue: %d/%d \n", _bufferPosition, _bufferLength[_playBuffer]);
if (_bufferPosition >= _bufferLength[_playBuffer]) {
bufferIsAvailableForRead[_playBuffer] = true;
//Serial.printf("\ndiscard buffer: %d\n", _playBuffer);
if (_numBuffers ==1 && _file_offset >= _file_size)
return false;
_numBuffers--;
//Serial.printf("num buffers: %d\n", _numBuffers);
if (_playBuffer < _numBuffers) {
_readBuffer = _playBuffer;
//Serial.printf("switch read buffer to: %d\n", _readBuffer);
}
_bufferPosition -= _bufferLength[_playBuffer];
_playBuffer++;
if (_playBuffer >= ResamplingFlashReader_NUM_BUFFERS) {
_playBuffer = 0;
}
//Serial.printf("switch play buffer to: %d\n", _playBuffer);
updateBuffers();
}
} else if (_playbackRate < 0) {
if (_numBuffers == 1)
if (_file_offset + (AUDIO_BLOCK_SAMPLES * 2) + _bufferPosition < 0)
return false;
// reverse playback
if (_bufferPosition < 0) {
if (_numBuffers == 0)
return false;
bufferIsAvailableForRead[_playBuffer] = true;
_readBuffer = _playBuffer;
_numBuffers--;
_playBuffer ++;
if (_playBuffer >= ResamplingFlashReader_NUM_BUFFERS) {
_playBuffer = 0;
}
_bufferPosition += _bufferLength[_playBuffer];
updateBuffers();
} else if (_bufferPosition > (AUDIO_BLOCK_SAMPLES-1) * 2) {
_bufferPosition = (AUDIO_BLOCK_SAMPLES-1) * 2;
}
}
int16_t *_buffer = _buffers[_playBuffer];
int16_t result = _buffer[_bufferPosition/2];
//Serial.printf("r: %d,", result);
if (_enable_interpolation) {
if (_remainder != 0.0) {
if (_playbackRate > 0.0) {
int16_t next;
if (_bufferPosition < _bufferLength[_playBuffer]-2) {
next =_buffer[(_bufferPosition/2)+1];
} else {
int nextPlayBuffer = _playBuffer == ResamplingFlashReader_NUM_BUFFERS - 1? 0 : _playBuffer+1;
if (_last_read_offset + _bufferPosition < _file_size-2) // second interpolation point is before the end of the file, don't int
next = _buffers[nextPlayBuffer][0];
else
next = result; // dont interpolate past the end of the file
}
int16_t interpolated = static_cast<int16_t>( (((1-_remainder) * 1000.0 * result) + (_remainder * 1000.0 * next)) / 1000.0 );
result = interpolated;
} else if (_playbackRate < 0.0) {
int16_t prev;
if (_bufferPosition >= 2) {
prev = _buffer[(_bufferPosition/2)-1];
} else {
if (_file_offset > 2) {
int nextPlayBuffer = _playBuffer == ResamplingFlashReader_NUM_BUFFERS - 1 ? 0 : _playBuffer + 1;
int nextBufLength = _bufferLength[nextPlayBuffer] / 2;
prev = _buffers[nextPlayBuffer][nextBufLength - 1];
} else
prev = result;
}
int16_t interpolated = static_cast<int16_t>( (((1 + _remainder) * 1000.0 * result) + (-_remainder * 1000.0 * prev)) / 1000.0 );
result = interpolated;
}
}
}
_remainder += _playbackRate;
auto delta = static_cast<signed int>(_remainder);
_remainder -= static_cast<double>(delta);
_bufferPosition += 2 * delta;
*value = result;
return true;
}
void ResamplingFlashReader::begin(void)
{
_playing = false;
_file_offset = 0;
_file_size = 0;
}
bool ResamplingFlashReader::play(const char *filename)
{
stop();
AudioStartUsingSPI();
__disable_irq();
_file = SerialFlash.open(filename);
__enable_irq();
if (!_file) {
AudioStopUsingSPI();
return false;
}
__disable_irq();
_file_size = _file.size();
__enable_irq();
if (_playbackRate < 0) {
// reverse playback - forward _file_offset to last audio block in file
_file_offset = _file_size - AUDIO_BLOCK_SAMPLES * 2;
} else {
// forward playabck - set _file_offset to first audio block in file
_file_offset = 0;
}
for (char i=0; i<ResamplingFlashReader_NUM_BUFFERS; i++ ) {
_buffers[i] = (int16_t*)malloc( AUDIO_BLOCK_SAMPLES * 2);
bufferIsAvailableForRead[i] = true;
}
_readBuffer = 0;
_numBuffers = 0;
updateBuffers();
_playBuffer = 0;
if (_numBuffers == 0)
return false;
_playing = true;
if (_playbackRate < 0) {
_bufferPosition = _bufferLength[_playBuffer]-2;
}
return true;
}
void ResamplingFlashReader::stop()
{
__disable_irq();
if (_playing) {
_playing = false;
__enable_irq();
_file.close();
AudioStopUsingSPI();
//StopUsingSPI();
for (uint i=0; i<ResamplingFlashReader_NUM_BUFFERS; i++ ) {
if (_buffers[i] != NULL) {
free(_buffers[i]);
_buffers[i] = NULL;
}
}
} else {
__enable_irq();
}
}
int ResamplingFlashReader::available(void) {
return _file.available() / _playbackRate;
}
void ResamplingFlashReader::close(void) {
if (_playing)
stop();
_file.close();
}
void ResamplingFlashReader::updateBuffers() {
bool forward = (_playbackRate >= 0.0);
while (_readBuffer >= 0
&& isInRange()
&& bufferIsAvailableForRead[_readBuffer]) {
//Serial.printf("read buffer: %d (_numBuffers:%d) \n",_readBuffer, _numBuffers);
int16_t *buffer = _buffers[_readBuffer];
bufferIsAvailableForRead[_readBuffer] = false;
_last_read_offset = _file_offset;
uint16_t numberOfBytesToRead = AUDIO_BLOCK_SAMPLES * 2;
if (!forward) {
if (_file_offset < 0) {
// reverse playback, last buffer, only read partial remaining buffer that hasn't already played
numberOfBytesToRead = _file_offset + AUDIO_BLOCK_SAMPLES * 2;
}
_file.seek(_file_offset < 0? 0 : _file_offset);
}
//Serial.printf("\nreading %d bytes, starting at:%d (into readbuff %d) - _file_offset:%d\n", numberOfBytesToRead, _file.position(), _readBuffer, _file_offset);
int numRead = _file.read(buffer, numberOfBytesToRead);
_bufferLength[_readBuffer] = numRead;
//Serial.printf("read %d bytes\n", numRead);
if (_playbackRate < 0) {
_file_offset -= numRead;
} else
_file_offset += numRead;
if (numRead > 0) {
_readBuffer ++;
if (_readBuffer >= ResamplingFlashReader_NUM_BUFFERS)
_readBuffer = -1;
_numBuffers++;
} else {
_readBuffer = -1;
//Serial.printf("\n Not able to read anymore buffers for the moment...\n", numRead);
}
}
}
bool ResamplingFlashReader::isInRange() {
if (_playbackRate >= 0.0) {
return _file_offset < _file_size;
} else {
return _file_offset + AUDIO_BLOCK_SAMPLES * 2 > 0;
}
}