Permalink
Browse files

Add callbacks for metadata and status reports

All objects can have a metadata and/or status report callback once created.
They will call the source program with the parsed ID3 tag, ICY StreamTitle,
or text and numeric error ID of any interesting event.  It is up to the end
application to figure out what to do with this.  Normally, you'd just print
it to the LCD or Serial port.  But it is possible to detect, say, a buffer
overflow and then switch to a lower bitrate stream for example.
  • Loading branch information...
earlephilhower committed Dec 7, 2017
1 parent 1ab9348 commit 9c120a777a4132fe8a951f921260ea5293d7ff1a
@@ -16,21 +16,22 @@ AudioFileSourceSPIFFS *file;
AudioOutputI2SNoDAC *out;
AudioFileSourceID3 *id3;
void ID3Callback(const char *type, bool unicode, int len, AudioFileSource *src)
// Called when a metadata event occurs (i.e. an ID3 tag, an ICY block, etc.
void MDCallback(void *cbData, const char *type, bool isUnicode, Stream *stream)
{
Serial.printf("ID3 callback for: %s = [%d bytes] '", type, len);
if (unicode && len > 2) {
char ign[2];
src->read(&ign, 2);
len -= 2;
(void)cbData;
Serial.printf("ID3 callback for: %s = '", type);
if (isUnicode) {
// Skip byte order marker
stream->read();
stream->read();
}
while (len) {
char a, b;
src->read(&a, 1);
len--;
if (unicode && len) {
src->read(&b, 1);
len--;
while (stream->available()) {
char a = stream->read();
if (isUnicode) {
stream->read();
}
Serial.printf("%c", a);
}
@@ -48,7 +49,7 @@ void setup()
Serial.printf("Sample MP3 playback begins...\n");
file = new AudioFileSourceSPIFFS("/pno-cs.mp3");
id3 = new AudioFileSourceID3(file);
id3->setCallback(ID3Callback);
id3->RegisterMetadataCB(MDCallback, (void*)"ID3TAG");
out = new AudioOutputI2SNoDAC();
mp3 = new AudioGeneratorMP3();
mp3->begin(id3, out);
@@ -1,4 +1,5 @@
#include <Arduino.h>
#include <ESP8266WiFi.h>
#include "AudioFileSourceICYStream.h"
#include "AudioFileSourceBuffer.h"
@@ -8,8 +9,8 @@
// 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 = ".....";
const char *SSID = "....";
const char *PASSWORD = "....";
// Randomly picked URL
const char *URL="http://streaming.shoutcast.com/80sPlanet?lang=en-US";
@@ -19,12 +20,24 @@ AudioFileSourceICYStream *file;
AudioFileSourceBuffer *buff;
AudioOutputI2SNoDAC *out;
void ICYCallback(const char *type, const char *value)
// Called when a metadata event occurs (i.e. an ID3 tag, an ICY block, etc.
void MDCallback(void *cbData, const char *type, bool isUnicode, Stream *stream)
{
Serial.printf("ICY MD: '%s' = '%s'\n", type, value);
const char *ptr = reinterpret_cast<const char *>(cbData);
(void) isUnicode; // Punt this ball for now
Serial.printf("METADATA(%s) '%s' = '%s'\n", ptr, type, stream->readString().c_str());
Serial.flush();
}
// Called when there's a warning or error (like a buffer underflow or decode hiccup)
void StatusCallback(void *cbData, int code, const char *string)
{
const char *ptr = reinterpret_cast<const char *>(cbData);
Serial.printf("STATUS(%s) '%d' = '%s'\n", ptr, code, string);
Serial.flush();
}
void setup()
{
Serial.begin(115200);
@@ -47,15 +60,19 @@ void setup()
Serial.println("...Connecting to WiFi");
delay(1000);
}
Serial.println("Connected");
file = new AudioFileSourceICYStream(URL);
file->setCallback(ICYCallback);
file->RegisterMetadataCB(MDCallback, (void*)"ICY");
buff = new AudioFileSourceBuffer(file, 2048);
buff->RegisterStatusCB(StatusCallback, (void*)"buffer");
out = new AudioOutputI2SNoDAC();
mp3 = new AudioGeneratorMP3();
mp3->RegisterStatusCB(StatusCallback, (void*)"mp3");
mp3->begin(buff, out);
}
void loop()
{
static int lastms = 0;
@@ -1,6 +1,6 @@
#include <Arduino.h>
#include <ESP8266WiFi.h>
#include "AudioFileSourceHTTPStream.h"
#include "AudioFileSourceICYStream.h"
#include "AudioFileSourceSPIRAMBuffer.h"
#include "AudioGeneratorMP3.h"
#include "AudioOutputI2SNoDAC.h"
@@ -15,10 +15,27 @@ const char *PASSWORD = ".....";
const char *URL="http://streaming.shoutcast.com/80sPlanet?lang=en-US";
AudioGeneratorMP3 *mp3;
AudioFileSourceHTTPStream *file;
AudioFileSourceICYStream *file;
AudioFileSourceSPIRAMBuffer *buff;
AudioOutputI2SNoDAC *out;
// Called when a metadata event occurs (i.e. an ID3 tag, an ICY block, etc.
void MDCallback(void *cbData, const char *type, bool isUnicode, Stream *stream)
{
const char *ptr = reinterpret_cast<const char *>(cbData);
(void) isUnicode; // Punt this ball for now
Serial.printf("METADATA(%s) '%s' = '%s'\n", ptr, type, stream->readString().c_str());
Serial.flush();
}
// Called when there's a warning or error (like a buffer underflow or decode hiccup)
void StatusCallback(void *cbData, int code, const char *string)
{
const char *ptr = reinterpret_cast<const char *>(cbData);
Serial.printf("STATUS(%s) '%d' = '%s'\n", ptr, code, string);
Serial.flush();
}
void setup()
{
Serial.begin(115200);
@@ -41,18 +58,29 @@ void setup()
Serial.println("...Connecting to WiFi");
delay(1000);
}
Serial.println("Connected");
file = new AudioFileSourceHTTPStream(URL);
file = new AudioFileSourceICYStream(URL);
file->RegisterMetadataCB(MDCallback, (void*)"ICY");
// Initialize 23LC1024 SPI RAM buffer with chip select ion GPIO4 and ram size of 128KByte
buff = new AudioFileSourceSPIRAMBuffer(file, 4, 131072);
buff->RegisterStatusCB(StatusCallback, (void*)"buffer");
out = new AudioOutputI2SNoDAC();
mp3 = new AudioGeneratorMP3();
mp3->RegisterStatusCB(StatusCallback, (void*)"mp3");
mp3->begin(buff, out);
}
void loop()
{
static int lastms = 0;
if (mp3->isRunning()) {
if (millis()-lastms > 1000) {
lastms = millis();
Serial.printf("Running for %d ms...\n", lastms);
Serial.flush();
}
if (!mp3->loop()) mp3->stop();
} else {
Serial.printf("MP3 done\n");
View
@@ -22,6 +22,7 @@
#define _AUDIOFILESOURCE_H
#include <Arduino.h>
#include "AudioStatus.h"
class AudioFileSource
{
@@ -37,6 +38,13 @@ class AudioFileSource
virtual uint32_t getSize() { return 0; };
virtual uint32_t getPos() { return 0; };
virtual bool loop() { return true; };
public:
virtual bool RegisterMetadataCB(AudioStatus::metadataCBFn fn, void *data) { return cb.RegisterMetadataCB(fn, data); }
virtual bool RegisterStatusCB(AudioStatus::statusCBFn fn, void *data) { return cb.RegisterStatusCB(fn, data); }
protected:
AudioStatus cb;
};
#endif
@@ -78,7 +78,7 @@ uint32_t AudioFileSourceBuffer::read(void *data, uint32_t len)
uint32_t bytes = 0;
if (!filled) {
// Fill up completely before returning any data at all
Serial.println("Buffering...");
cb.st(STATUS_FILLING, "Refilling buffer");
length = src->read(buffer, buffSize);
writePtr = length % buffSize;
filled = true;
@@ -115,6 +115,7 @@ uint32_t AudioFileSourceBuffer::read(void *data, uint32_t len)
writePtr = 0;
length = 0;
filled = false;
cb.st(STATUS_UNDERFLOW, "Buffer underflow");
}
fill();
@@ -38,6 +38,8 @@ class AudioFileSourceBuffer : public AudioFileSource
virtual uint32_t getPos() override;
virtual bool loop() override;
enum { STATUS_FILLING=2, STATUS_UNDERFLOW };
private:
virtual void fill();
@@ -42,6 +42,7 @@ bool AudioFileSourceHTTPStream::open(const char *url)
int code = http.GET();
if (code != HTTP_CODE_OK) {
http.end();
cb.st(STATUS_HTTPFAIL, "Can't open HTTP request");
return false;
}
size = http.getSize();
@@ -69,21 +70,20 @@ uint32_t AudioFileSourceHTTPStream::readInternal(void *data, uint32_t len, bool
{
retry:
if (!http.connected()) {
Serial.println("Stream disconnected\n");
Serial.flush();
cb.st(STATUS_DISCONNECTED, "Stream disconnected");
http.end();
for (int i = 0; i < reconnectTries; i++) {
Serial.printf("Attempting to reconnect, try %d\n", i);
Serial.flush();
char buff[32];
sprintf(buff, "Attempting to reconnect, try %d", i);
cb.st(STATUS_RECONNECTING, buff);
delay(reconnectDelayMs);
if (open(saveURL)) {
Serial.println("Reconnected to stream");
cb.st(STATUS_RECONNECTED, "Stream reconnected");
break;
}
}
if (!http.connected()) {
Serial.printf("Unable to reconnect\n");
Serial.flush();
cb.st(STATUS_DISCONNECTED, "Unable to reconnect");
return 0;
}
}
@@ -101,7 +101,7 @@ uint32_t AudioFileSourceHTTPStream::readInternal(void *data, uint32_t len, bool
size_t avail = stream->available();
if (!nonBlock && !avail) {
Serial.printf("No stream data available\n");
cb.st(STATUS_NODATA, "No stream data available");
http.end();
goto retry;
}
@@ -45,6 +45,8 @@ class AudioFileSourceHTTPStream : public AudioFileSource
virtual uint32_t getPos() override;
bool SetReconnect(int tries, int delayms) { reconnectTries = tries; reconnectDelayMs = delayms; return true; }
enum { STATUS_HTTPFAIL=2, STATUS_DISCONNECTED, STATUS_RECONNECTING, STATUS_RECONNECTED, STATUS_NODATA };
private:
virtual uint32_t readInternal(void *data, uint32_t len, bool nonBlock);
HTTPClient http;
Oops, something went wrong.

0 comments on commit 9c120a7

Please sign in to comment.