Tasmota/lib/lib_audio/ESP8266Audio/src/AudioFileSourceSPIRAMBuffer.cpp
2021-12-27 14:24:58 +01:00

172 lines
4.8 KiB
C++

/*
AudioFileSourceSPIRAMBuffer
Buffered file source in external SPI RAM
Copyright (C) 2017 Sebastien Decourriere
Based on AudioFileSourceBuffer class from Earle F. Philhower, III
Copyright (C) 2020 Earle F. Philhower, III
Rewritten for speed and functionality
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#if defined(ESP32) || defined(ESP8266)
#include <Arduino.h>
#include "AudioFileSourceSPIRAMBuffer.h"
#pragma GCC optimize ("O3")
AudioFileSourceSPIRAMBuffer::AudioFileSourceSPIRAMBuffer(AudioFileSource *source, uint8_t csPin, uint32_t buffSizeBytes)
{
ram.begin(40, csPin);
ramSize = buffSizeBytes;
writePtr = 0;
readPtr = 0;
filled = false;
src = source;
audioLogger->printf_P(PSTR("SPI RAM buffer size: %u Bytes\n"), ramSize);
}
AudioFileSourceSPIRAMBuffer::~AudioFileSourceSPIRAMBuffer()
{
ram.end();
}
bool AudioFileSourceSPIRAMBuffer::seek(int32_t pos, int dir)
{
// Invalidate
readPtr = 0;
writePtr = 0;
filled = false;
return src->seek(pos, dir);
}
bool AudioFileSourceSPIRAMBuffer::close()
{
return src->close();
}
bool AudioFileSourceSPIRAMBuffer::isOpen()
{
return src->isOpen();
}
uint32_t AudioFileSourceSPIRAMBuffer::getSize()
{
return src->getSize();
}
uint32_t AudioFileSourceSPIRAMBuffer::getPos()
{
return src->getPos() - (writePtr - readPtr);
}
uint32_t AudioFileSourceSPIRAMBuffer::read(void *data, uint32_t len)
{
uint32_t bytes = 0;
// Check if the buffer isn't empty, otherwise we try to fill completely
if (!filled) {
cb.st(999, PSTR("Filling buffer..."));
uint8_t buffer[256];
writePtr = 0;
readPtr = 0;
// Fill up completely before returning any data at all
do {
int toRead = std::min(ramSize - (writePtr - readPtr), sizeof(buffer));
int length = src->read(buffer, toRead);
if (length > 0) {
#ifdef FAKERAM
for (size_t i=0; i<length; i++) fakeRAM[(i+writePtr)%ramSize] = buffer[i];
#else
ram.writeBytes(writePtr % ramSize, buffer, length);
#endif
writePtr += length;
} else {
// EOF, break out of read loop
break;
}
} while ((writePtr - readPtr) < ramSize);
filled = true;
cb.st(999, PSTR("Buffer filled..."));
}
// Read up to the entire buffer from RAM
uint32_t toReadFromBuffer = std::min(len, (uint32_t)(writePtr - readPtr));
uint8_t *ptr = reinterpret_cast<uint8_t*>(data);
if (toReadFromBuffer > 0) {
#ifdef FAKERAM
for (size_t i=0; i<toReadFromBuffer; i++) ptr[i] = fakeRAM[(i+readPtr)%ramSize];
#else
ram.readBytes(readPtr % ramSize, ptr, toReadFromBuffer);
#endif
readPtr += toReadFromBuffer;
ptr += toReadFromBuffer;
bytes += toReadFromBuffer;
len -= toReadFromBuffer;
}
// If len>0 there is no data left in buffer and we try to read more directly from source.
// Then, we trigger a complete buffer refill
if (len) {
bytes += src->read(data, len);
filled = false;
}
return bytes;
}
void AudioFileSourceSPIRAMBuffer::fill()
{
// Make sure the buffer is pre-filled before make partial fill.
if (!filled) return;
for (auto i=0; i<5; i++) {
// Make sure there is at least buffer size free in RAM
uint8_t buffer[128];
if ((ramSize - (writePtr - readPtr)) < sizeof(buffer)) {
return;
}
int cnt = src->readNonBlock(buffer, sizeof(buffer));
if (cnt) {
#ifdef FAKERAM
for (size_t i=0; i<cnt; i++) fakeRAM[(i+writePtr)%ramSize] = buffer[i];
#else
ram.writeBytes(writePtr % ramSize, buffer, cnt);
#endif
writePtr += cnt;
}
}
}
bool AudioFileSourceSPIRAMBuffer::loop()
{
static uint32_t last = 0;
if (!src->loop()) return false;
fill();
if ((ESP.getCycleCount() - last) > microsecondsToClockCycles(1000000)) {
last = ESP.getCycleCount();
char str[65];
memset(str, '#', 64);
str[64] = 0;
str[((writePtr - readPtr) * 64)/ramSize] = 0;
cb.st(((writePtr - readPtr) * 100)/ramSize, str);
}
return true;
}
#endif