Skip to content

Commit

Permalink
SPIRAM buffer support from @armSeb
Browse files Browse the repository at this point in the history
* Initial SPIRAM buffering support

Signed-off-by: sebastien <armseb@MacBook-de-sebastien.local>

* SPI RAM Buffering fill & read.

Signed-off-by: sebastien <armseb@MacBook-de-sebastien.local>

* First "working" version...

Signed-off-by: sebastien <armseb@MacBook-de-sebastien.local>

* Improved version of SPIRAM buffering

Signed-off-by: Sebastien Decourriere <sebtx452@gmail.com>

* Fixed spiram.h path

Signed-off-by: Sebastien Decourriere <sebtx452@gmail.com>

* Buffer fill optimization

Signed-off-by: Sebastien Decourriere <sebtx452@gmail.com>

* Modified read block min to avoid too little buffering attempts

Signed-off-by: Sebastien Decourriere <sebtx452@gmail.com>

* Temps buffer size decrease from 2048 to 1600 Bytes

Signed-off-by: Sebastien Decourriere <sebtx452@gmail.com>

* Decrease the temp buffer to 256Bytes

Signed-off-by: Sebastien Decourriere <sebtx452@gmail.com>

* Local buffer variable

Signed-off-by: Sebastien Decourriere <sebtx452@gmail.com>

* Added loop function to SPIRAMBuffer that refill it automatically

Signed-off-by: sebastien <armseb@MacBook-de-sebastien.local>

* Compile issue

Signed-off-by: sebastien <armseb@MacBook-de-sebastien.local>

* Add possibility to define Chip Select pin from Sketch

Signed-off-by: Sebastien Decourriere <sebtx452@gmail.com>

* Externalize libspiram

Signed-off-by: sebastien <armseb@MacBook-de-sebastien.local>

* Copyright fix

Signed-off-by: sebastien <armseb@MacBook-de-sebastien.local>

* Deleted embedded libspiram

Signed-off-by: sebastien <armseb@MacBook-de-sebastien.local>

* Added comments, example and keyword

Signed-off-by: sebastien <armseb@MacBook-de-sebastien.local>

* README modification

Signed-off-by: sebastien <armseb@MacBook-de-sebastien.local>

* Correction

Signed-off-by: sebastien <armseb@MacBook-de-sebastien.local>

* Schema example

Signed-off-by: sebastien <armseb@MacBook-de-sebastien.local>

* README correction

Signed-off-by: sebastien <armseb@MacBook-de-sebastien.local>
  • Loading branch information
armSeb authored and earlephilhower committed Dec 6, 2017
1 parent 4710347 commit 97c05c9
Show file tree
Hide file tree
Showing 6 changed files with 283 additions and 0 deletions.
6 changes: 6 additions & 0 deletions README.md
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -144,6 +144,12 @@ USB-5V -- Speaker + Terminal


Basically the transistor acts as a switch and requires only a drive of 1/beta (~1/1000 for the transistor specified) times the speaker current. As shown you've got a max current of (5-0.7)/8=540mA and a power of 0.54^2 * 8 = ~2.3W into the speaker. Basically the transistor acts as a switch and requires only a drive of 1/beta (~1/1000 for the transistor specified) times the speaker current. As shown you've got a max current of (5-0.7)/8=540mA and a power of 0.54^2 * 8 = ~2.3W into the speaker.


## Using external SPI RAM to increase buffer
A class allows you to use a 23lc1024 SPI RAM from Microchip as input buffer. This chip connects to ESP8266 HSPI port and use external SPI RAM library (https://github.com/Gianbacchio/ESP8266_Spiram).
You need to choose another pin than GPIO15 for Cs as this pin is already used by the I2S port. Here is an example with the Cs pin plugged to GPIO00 on NodeMCU board.

![Example of SPIRAM Schematic](examples/StreamMP3FromHTTP_SPIRAM/Schema_Spiram.png)

## Notes for using SD cards and ESP8266Audio on Wemos shields ## Notes for using SD cards and ESP8266Audio on Wemos shields
I've been told the Wemos SD card shield uses GPIO15 as the SD chip select. This needs to be changed because GPIO15 == I2SBCLK, and is driven even if you're using the NoDAC option. Once you move the CS to another pin and update your program it should work fine. I've been told the Wemos SD card shield uses GPIO15 as the SD chip select. This needs to be changed because GPIO15 == I2SBCLK, and is driven even if you're using the NoDAC option. Once you move the CS to another pin and update your program it should work fine.


Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
62 changes: 62 additions & 0 deletions examples/StreamMP3FromHTTP_SPIRAM/StreamMP3FromHTTP_SPIRAM.ino
Original file line number Original file line Diff line number Diff line change
@@ -0,0 +1,62 @@
#include <Arduino.h>
#include <ESP8266WiFi.h>
#include "AudioFileSourceHTTPStream.h"
#include "AudioFileSourceSPIRAMBuffer.h"
#include "AudioGeneratorMP3.h"
#include "AudioOutputI2SNoDAC.h"

// To run, set your ESP8266 build to 160MHz, update the SSID info, and upload.

// Enter your WiFi setup here:
const char *SSID = ".....";
const char *PASSWORD = ".....";

// Randomly picked URL
const char *URL="http://streaming.shoutcast.com/80sPlanet?lang=en-US";

AudioGeneratorMP3 *mp3;
AudioFileSourceHTTPStream *file;
AudioFileSourceSPIRAMBuffer *buff;
AudioOutputI2SNoDAC *out;

void setup()
{
Serial.begin(115200);
delay(1000);
Serial.println("Connecting to WiFi");

WiFi.disconnect();
WiFi.softAPdisconnect(true);
WiFi.mode(WIFI_STA);

WiFi.hostname("melody");

byte zero[] = {0,0,0,0};
WiFi.config(zero, zero, zero, zero);

WiFi.begin(SSID, PASSWORD);

// Try forever
while (WiFi.status() != WL_CONNECTED) {
Serial.println("...Connecting to WiFi");
delay(1000);
}

file = new AudioFileSourceHTTPStream(URL);
// Initialize 23LC1024 SPI RAM buffer with chip select ion GPIO4 and ram size of 128KByte
buff = new AudioFileSourceSPIRAMBuffer(file, 4, 131072);
out = new AudioOutputI2SNoDAC();
mp3 = new AudioGeneratorMP3();
mp3->begin(buff, out);
}

void loop()
{
if (mp3->isRunning()) {
if (!mp3->loop()) mp3->stop();
} else {
Serial.printf("MP3 done\n");
delay(1000);
}
}

1 change: 1 addition & 0 deletions keywords.txt
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ AudioFileSourceICYStream KEYWORD1
AudioFileSourceID3 KEYWORD1 AudioFileSourceID3 KEYWORD1
AudioFileSourceSD KEYWORD1 AudioFileSourceSD KEYWORD1
AudioFileSourceBuffer KEYWORD1 AudioFileSourceBuffer KEYWORD1
AudioFileSourceSPIRAMBuffer KEYWORD1
AudioGenerator KEYWORD1 AudioGenerator KEYWORD1
AudioGeneratorAAC KEYWORD1 AudioGeneratorAAC KEYWORD1
AudioGeneratorFLAC KEYWORD1 AudioGeneratorFLAC KEYWORD1
Expand Down
153 changes: 153 additions & 0 deletions src/AudioFileSourceSPIRAMBuffer.cpp
Original file line number Original file line Diff line number Diff line change
@@ -0,0 +1,153 @@
/*
AudioFileSourceSPIRAMBuffer
Buffered file source in external SPI RAM
Copyright (C) 2017 Sebastien Decourriere
Based on AudioFileSourceBuffer class from 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 <http://www.gnu.org/licenses/>.
*/

#include <Arduino.h>
#include "AudioFileSourceSPIRAMBuffer.h"

#pragma GCC optimize ("O3")

AudioFileSourceSPIRAMBuffer::AudioFileSourceSPIRAMBuffer(AudioFileSource *source, uint8_t csPin, uint32_t buffSizeBytes)
{
Spiram = new ESP8266Spiram(csPin, 40e6);
Spiram->begin();
Spiram->setSeqMode();
ramSize = buffSizeBytes;
writePtr = 0;
readPtr = 0;
src = source;
length = 0;
filled = false;
bytesAvailable = 0;
Serial.printf("SPI RAM buffer size: %u Bytes\n", ramSize);
}

AudioFileSourceSPIRAMBuffer::~AudioFileSourceSPIRAMBuffer()
{
}

bool AudioFileSourceSPIRAMBuffer::seek(int32_t pos, int dir)
{
// Invalidate
readPtr = 0;
writePtr = 0;
length = 0;
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();
}

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) {
uint8_t buffer[256];
writePtr = readPtr = 0;
uint16_t toRead = sizeof(buffer);
// Fill up completely before returning any data at all
Serial.println("Buffering...");
while (bytesAvailable!=ramSize) {
length = src->read(buffer, toRead);
if(length>0) {
Spiram->write(writePtr, buffer, length);
bytesAvailable+=length;
writePtr = bytesAvailable % ramSize;
if ((ramSize-bytesAvailable)<toRead) {
toRead=ramSize-bytesAvailable;
}
}
}
writePtr = bytesAvailable % ramSize;
filled = true;
Serial.println("Filling Done !");
}

// Serial.printf("Buffer: %u%\n", bytesAvailable*100/ramSize);

uint8_t *ptr = reinterpret_cast<uint8_t*>(data);
uint32_t toReadFromBuffer = (len < bytesAvailable) ? len : bytesAvailable;
if (toReadFromBuffer>0) {
// Pull from buffer until we've got none left or we've satisfied the request
Spiram->read(readPtr, ptr, toReadFromBuffer);
readPtr = (readPtr+toReadFromBuffer) % ramSize;
bytes = toReadFromBuffer;
bytesAvailable-=toReadFromBuffer;
len-=toReadFromBuffer;
ptr += toReadFromBuffer;
}

// If len>O 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(ptr, len);
bytesAvailable = 0;
filled = false;
}
return bytes;
}

void AudioFileSourceSPIRAMBuffer::fill()
{
// Make sure the buffer is pre-filled before make partial fill.
if (!filled) return;

// Now trying to refill SPI RAM Buffer
uint8_t buffer[128];
// Make sure there is at least buffer size free in RAM
if ((ramSize - bytesAvailable)<sizeof(buffer)) {
return;
}
uint16_t cnt = src->readNonBlock(buffer, sizeof(buffer));
if (cnt) {
Spiram->write(writePtr, buffer, cnt);
bytesAvailable+=cnt;
writePtr = (writePtr + cnt) % ramSize;
#ifdef SPIBUF_DEBUG
Serial.printf("SockRead: %u | RamAvail: %u\n", cnt, bytesAvailable);
#endif
}
return;
}

bool AudioFileSourceSPIRAMBuffer::loop()
{
if (!src->loop()) return false;
fill();
return true;
}
61 changes: 61 additions & 0 deletions src/AudioFileSourceSPIRAMBuffer.h
Original file line number Original file line Diff line number Diff line change
@@ -0,0 +1,61 @@
/*
AudioFileSourceSPIRAMBuffer
Buffered file source in external SPI RAM
Copyright (C) 2017 Sebastien Decourriere
Based on AudioFileSourceBuffer class from 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 <http://www.gnu.org/licenses/>.
*/

#ifndef _AUDIOFILESOURCESPIRAMBUFFER_H
#define _AUDIOFILESOURCESPIRAMBUFFER_H

#include "AudioFileSource.h"
#include <SPI.h>
#include <ESP8266Spiram.h> // https://github.com/Gianbacchio/ESP8266_Spiram

// #define SPIBUF_DEBUG

class AudioFileSourceSPIRAMBuffer : public AudioFileSource
{
public:
AudioFileSourceSPIRAMBuffer(AudioFileSource *in, uint8_t csPin, uint32_t bufferBytes);
virtual ~AudioFileSourceSPIRAMBuffer() override;

virtual uint32_t read(void *data, uint32_t len) override;
virtual bool seek(int32_t pos, int dir) override;
virtual bool close() override;
virtual bool isOpen() override;
virtual uint32_t getSize() override;
virtual uint32_t getPos() override;
virtual bool loop() override;

private:
virtual void fill();

private:
AudioFileSource *src;
ESP8266Spiram *Spiram;
uint32_t ramSize;
uint32_t writePtr;
uint32_t readPtr;
uint16_t length;
uint32_t bytesAvailable;
bool filled;
};


#endif

0 comments on commit 97c05c9

Please sign in to comment.