/* AudioFileSourceBuffer Double-buffered file source using system RAM Copyright (C) 2017 Earle F. Philhower, III 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 . */ #include #include "AudioFileSourceBuffer.h" #pragma GCC optimize ("O3") AudioFileSourceBuffer::AudioFileSourceBuffer(AudioFileSource *source, uint32_t buffSizeBytes) { buffSize = buffSizeBytes; buffer = (uint8_t*)malloc(sizeof(uint8_t) * buffSize); if (!buffer) audioLogger->printf_P(PSTR("Unable to allocate AudioFileSourceBuffer::buffer[]\n")); deallocateBuffer = true; writePtr = 0; readPtr = 0; src = source; length = 0; filled = false; } AudioFileSourceBuffer::AudioFileSourceBuffer(AudioFileSource *source, void *inBuff, uint32_t buffSizeBytes) { buffSize = buffSizeBytes; buffer = (uint8_t*)inBuff; deallocateBuffer = false; writePtr = 0; readPtr = 0; src = source; length = 0; filled = false; } AudioFileSourceBuffer::~AudioFileSourceBuffer() { if (deallocateBuffer) free(buffer); buffer = NULL; } bool AudioFileSourceBuffer::seek(int32_t pos, int dir) { if(dir == SEEK_CUR && (readPtr+pos) < length) { readPtr += pos; return true; } else { // Invalidate readPtr = 0; writePtr = 0; length = 0; return src->seek(pos, dir); } } bool AudioFileSourceBuffer::close() { if (deallocateBuffer) free(buffer); buffer = NULL; return src->close(); } bool AudioFileSourceBuffer::isOpen() { return src->isOpen(); } uint32_t AudioFileSourceBuffer::getSize() { return src->getSize(); } uint32_t AudioFileSourceBuffer::getPos() { return src->getPos(); } uint32_t AudioFileSourceBuffer::getFillLevel() { return length; } uint32_t AudioFileSourceBuffer::read(void *data, uint32_t len) { if (!buffer) return src->read(data, len); uint32_t bytes = 0; if (!filled) { // Fill up completely before returning any data at all cb.st(STATUS_FILLING, PSTR("Refilling buffer")); length = src->read(buffer, buffSize); writePtr = length % buffSize; filled = true; } // Pull from buffer until we've got none left or we've satisfied the request uint8_t *ptr = reinterpret_cast(data); uint32_t toReadFromBuffer = (len < length) ? len : length; if ( (toReadFromBuffer > 0) && (readPtr >= writePtr) ) { uint32_t toReadToEnd = (toReadFromBuffer < (uint32_t)(buffSize - readPtr)) ? toReadFromBuffer : (buffSize - readPtr); memcpy(ptr, &buffer[readPtr], toReadToEnd); readPtr = (readPtr + toReadToEnd) % buffSize; len -= toReadToEnd; length -= toReadToEnd; ptr += toReadToEnd; bytes += toReadToEnd; toReadFromBuffer -= toReadToEnd; } if (toReadFromBuffer > 0) { // We know RP < WP at this point memcpy(ptr, &buffer[readPtr], toReadFromBuffer); readPtr = (readPtr + toReadFromBuffer) % buffSize; len -= toReadFromBuffer; length -= toReadFromBuffer; ptr += toReadFromBuffer; bytes += toReadFromBuffer; toReadFromBuffer -= toReadFromBuffer; } if (len) { // Still need more, try direct read from src bytes += src->read(ptr, len); // We're out of buffered data, need to force a complete refill. Thanks, @armSeb readPtr = 0; writePtr = 0; length = 0; filled = false; cb.st(STATUS_UNDERFLOW, PSTR("Buffer underflow")); } fill(); return bytes; } void AudioFileSourceBuffer::fill() { if (!buffer) return; if (length < buffSize) { // Now try and opportunistically fill the buffer if (readPtr > writePtr) { if (readPtr == writePtr+1) return; uint32_t bytesAvailMid = readPtr - writePtr - 1; int cnt = src->readNonBlock(&buffer[writePtr], bytesAvailMid); length += cnt; writePtr = (writePtr + cnt) % buffSize; return; } if (buffSize > writePtr) { uint32_t bytesAvailEnd = buffSize - writePtr; int cnt = src->readNonBlock(&buffer[writePtr], bytesAvailEnd); length += cnt; writePtr = (writePtr + cnt) % buffSize; if (cnt != (int)bytesAvailEnd) return; } if (readPtr > 1) { uint32_t bytesAvailStart = readPtr - 1; int cnt = src->readNonBlock(&buffer[writePtr], bytesAvailStart); length += cnt; writePtr = (writePtr + cnt) % buffSize; } } } bool AudioFileSourceBuffer::loop() { if (!src->loop()) return false; fill(); return true; }