Compare commits
10 Commits
f421b77700
...
fbb800c472
| Author | SHA1 | Date | |
|---|---|---|---|
| fbb800c472 | |||
| c81ca008cf | |||
| 1e304979c5 | |||
| 8d4d888957 | |||
| 2c343d98c7 | |||
| 48e0004b34 | |||
| dc4c3bf33a | |||
| a96890efbb | |||
| 68efa17092 | |||
| f7afaa05f7 |
@ -7,10 +7,19 @@
|
|||||||
|
|
||||||
#include <SPI.h>
|
#include <SPI.h>
|
||||||
#include <SD.h>
|
#include <SD.h>
|
||||||
|
#include <SnapClient.h>
|
||||||
|
|
||||||
#include "SnapClient.h"
|
#include "player.h"
|
||||||
#include "wifi.h"
|
#include "wifi.h"
|
||||||
|
|
||||||
|
bool audioMute = true;
|
||||||
|
|
||||||
|
bool audioMute2 = true; // keep last applied mute state because we cannot retrieve it from library
|
||||||
|
|
||||||
|
float audioVolume = 0.1;
|
||||||
|
|
||||||
|
int copyErrors = 0;
|
||||||
|
|
||||||
AudioBoardStream board(AudioKitEs8388V1);
|
AudioBoardStream board(AudioKitEs8388V1);
|
||||||
|
|
||||||
File file;
|
File file;
|
||||||
@ -34,8 +43,9 @@ bool running = false;
|
|||||||
void audioStop() {
|
void audioStop() {
|
||||||
if (running) {
|
if (running) {
|
||||||
running = false;
|
running = false;
|
||||||
Serial.println("[STOP]");
|
Serial.println("[AUDIO ] STOP");
|
||||||
}
|
}
|
||||||
|
copyErrors = 0;
|
||||||
copier.end();
|
copier.end();
|
||||||
mp3Stream.end();
|
mp3Stream.end();
|
||||||
mp3Decoder.end();
|
mp3Decoder.end();
|
||||||
@ -48,12 +58,12 @@ void audioStop() {
|
|||||||
bool audioPlayInit(const Entry &entry) {
|
bool audioPlayInit(const Entry &entry) {
|
||||||
audioStop();
|
audioStop();
|
||||||
|
|
||||||
Serial.printf("[%s] %s\n", entry.type.c_str(), entry.url.c_str());
|
Serial.printf("[AUDIO ] [%-4s] Start: %s\n", entry.type.c_str(), entry.url.c_str());
|
||||||
running = true;
|
running = true;
|
||||||
|
|
||||||
if (entry.type == "ICY" || entry.type == "SNAP") {
|
if (entry.type == "ICY" || entry.type == "SNAP") {
|
||||||
if (!isWifiConnected()) {
|
if (!isWifiConnected()) {
|
||||||
Serial.printf("[%s] WiFi not connected.\n", entry.type.c_str());
|
Serial.printf("[AUDIO ] [%-4s] WiFi not connected.\n", entry.type.c_str());
|
||||||
audioStop();
|
audioStop();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -61,7 +71,7 @@ bool audioPlayInit(const Entry &entry) {
|
|||||||
|
|
||||||
if (entry.type == "SD") {
|
if (entry.type == "SD") {
|
||||||
if (!SD.begin(PIN_AUDIO_KIT_SD_CARD_CS)) {
|
if (!SD.begin(PIN_AUDIO_KIT_SD_CARD_CS)) {
|
||||||
Serial.printf("[%s] Failed to mount SD-card.\n", entry.type.c_str());
|
Serial.printf("[AUDIO ] [%-4s] Failed to mount SD-card.\n", entry.type.c_str());
|
||||||
audioStop();
|
audioStop();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -72,13 +82,13 @@ bool audioPlayInit(const Entry &entry) {
|
|||||||
|
|
||||||
bool audioBeginMP3Stream(const Entry &entry, Stream &source) {
|
bool audioBeginMP3Stream(const Entry &entry, Stream &source) {
|
||||||
if (!mp3Decoder.begin()) {
|
if (!mp3Decoder.begin()) {
|
||||||
Serial.printf("[%s] Failed to start MP3DecoderHelix.", entry.type.c_str());
|
Serial.printf("[AUDIO ] [%-4s] Failed to start MP3DecoderHelix.", entry.type.c_str());
|
||||||
audioStop();
|
audioStop();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!mp3Stream.begin()) {
|
if (!mp3Stream.begin()) {
|
||||||
Serial.printf("[%s] Failed to start EncodedAudioStream.", entry.type.c_str());
|
Serial.printf("[AUDIO ] [%-4s] Failed to start EncodedAudioStream.", entry.type.c_str());
|
||||||
audioStop();
|
audioStop();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -86,7 +96,7 @@ bool audioBeginMP3Stream(const Entry &entry, Stream &source) {
|
|||||||
copier.begin(mp3Stream, source);
|
copier.begin(mp3Stream, source);
|
||||||
|
|
||||||
if (copier.copy() == 0) {
|
if (copier.copy() == 0) {
|
||||||
Serial.printf("[%s] Failed to copy initial data.", entry.type.c_str());
|
Serial.printf("[AUDIO ] [%-4s] Failed to copy initial data.", entry.type.c_str());
|
||||||
audioStop();
|
audioStop();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -100,7 +110,7 @@ bool audioPlayICY(const Entry &entry) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!icy.begin(entry.url.c_str())) {
|
if (!icy.begin(entry.url.c_str())) {
|
||||||
Serial.printf("[%s] Failed to start ICYStream.\n", entry.type.c_str());
|
Serial.printf("[AUDIO ] [%-4s] Failed to start ICYStream.\n", entry.type.c_str());
|
||||||
audioStop();
|
audioStop();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -115,26 +125,26 @@ bool audioPlaySNAP(const Entry &entry) {
|
|||||||
|
|
||||||
IPAddress ip;
|
IPAddress ip;
|
||||||
if (!WiFiClass::hostByName(entry.url.c_str(), ip)) {
|
if (!WiFiClass::hostByName(entry.url.c_str(), ip)) {
|
||||||
Serial.printf("[%s] Failed to resolve host.\n", entry.type.c_str());
|
Serial.printf("[AUDIO ] [%-4s] Failed to resolve host.\n", entry.type.c_str());
|
||||||
audioStop();
|
audioStop();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
snap.setServerIP(ip);
|
snap.setServerIP(ip);
|
||||||
if (!snap.begin()) {
|
if (!snap.begin()) {
|
||||||
Serial.printf("[%s] Failed to connect.\n", entry.type.c_str());
|
Serial.printf("[AUDIO ] [%-4s] Failed to connect.\n", entry.type.c_str());
|
||||||
audioStop();
|
audioStop();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!opusDecoder.begin()) {
|
if (!opusDecoder.begin()) {
|
||||||
Serial.printf("[%s] Failed to start OpusAudioDecoder.\n", entry.type.c_str());
|
Serial.printf("[AUDIO ] [%-4s] Failed to start OpusAudioDecoder.\n", entry.type.c_str());
|
||||||
audioStop();
|
audioStop();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!snap.doLoop()) {
|
if (!snap.doLoop()) {
|
||||||
Serial.printf("[%s] Failed to copy initial data.\n", entry.type.c_str());
|
Serial.printf("[AUDIO ] [%-4s] Failed to copy initial data.\n", entry.type.c_str());
|
||||||
audioStop();
|
audioStop();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -148,14 +158,14 @@ bool audioPlaySD(const Entry &entry) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!SD.exists(entry.url.c_str())) {
|
if (!SD.exists(entry.url.c_str())) {
|
||||||
Serial.printf("[%s] File not found.\n", entry.type.c_str());
|
Serial.printf("[AUDIO ] [%-4s] File not found.\n", entry.type.c_str());
|
||||||
audioStop();
|
audioStop();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
file = SD.open(entry.url.c_str(), FILE_READ);
|
file = SD.open(entry.url.c_str(), FILE_READ);
|
||||||
if (!file) {
|
if (!file) {
|
||||||
Serial.printf("[%s] Failed to open file.\n", entry.type.c_str());
|
Serial.printf("[AUDIO ] [%-4s] Failed to open file.\n", entry.type.c_str());
|
||||||
audioStop();
|
audioStop();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -177,22 +187,39 @@ bool audioPlay(const Entry &entry) {
|
|||||||
if (entry.type == "SNAP") {
|
if (entry.type == "SNAP") {
|
||||||
return audioPlaySNAP(entry);
|
return audioPlaySNAP(entry);
|
||||||
}
|
}
|
||||||
Serial.printf("[ERROR] unknown type: %s\n", entry.type.c_str());
|
Serial.printf("[AUDIO ] Unknown type: %s\n", entry.type.c_str());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void audioSetup() {
|
void audioSetup() {
|
||||||
AudioToolsLogger.setLogLevel(AudioToolsLogLevel::Error);
|
AudioToolsLogger.setLogLevel(AudioToolsLogLevel::Error);
|
||||||
Serial.println("Initializing board...");
|
Serial.println("[AUDIO ] Initializing board...");
|
||||||
board.begin();
|
board.begin();
|
||||||
|
audioMute2 = !audioMute;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool audioLoop() {
|
bool audioLoop() {
|
||||||
|
if (abs(board.volume() - audioVolume) >= 0.01f) {
|
||||||
|
Serial.printf("[AUDIO ] volume = %.2f\n", audioVolume);
|
||||||
|
board.setVolume(audioVolume);
|
||||||
|
stateBufferUpdateRequest();
|
||||||
|
}
|
||||||
|
if (audioMute2 != audioMute) {
|
||||||
|
audioMute2 = audioMute;
|
||||||
|
Serial.printf("[AUDIO ] mute = %s\n", audioMute ? "MUTED" : "no");
|
||||||
|
board.setMute(audioMute);
|
||||||
|
stateBufferUpdateRequest();
|
||||||
|
}
|
||||||
if (copier.copy() > 0) {
|
if (copier.copy() > 0) {
|
||||||
|
copyErrors = 0;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (running) {
|
if (running) {
|
||||||
Serial.println("Stream ended!");
|
copyErrors++;
|
||||||
|
if (copyErrors < 100) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
Serial.println("[AUDIO ] Stream ended!");
|
||||||
audioStop();
|
audioStop();
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@ -3,6 +3,10 @@
|
|||||||
|
|
||||||
#include "playlist.h"
|
#include "playlist.h"
|
||||||
|
|
||||||
|
extern bool audioMute;
|
||||||
|
|
||||||
|
extern float audioVolume;
|
||||||
|
|
||||||
bool audioPlay(const Entry &entry);
|
bool audioPlay(const Entry &entry);
|
||||||
|
|
||||||
void audioStop();
|
void audioStop();
|
||||||
|
|||||||
93
src/http.cpp
Normal file
93
src/http.cpp
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
#include "http.h"
|
||||||
|
|
||||||
|
#include <ArduinoJson.h>
|
||||||
|
|
||||||
|
#include "audio.h"
|
||||||
|
#include "ESPAsyncWebServer.h"
|
||||||
|
#include "player.h"
|
||||||
|
|
||||||
|
AsyncWebServer server(80);
|
||||||
|
|
||||||
|
AsyncWebSocket ws("/ws");
|
||||||
|
|
||||||
|
void onWebSocketEvent(AsyncWebSocket *server, AsyncWebSocketClient *client, const AwsEventType type, void *arg, const uint8_t *data, const size_t len) {
|
||||||
|
if (type == WS_EVT_CONNECT) {
|
||||||
|
Serial.printf("[%-9s] Connected: %s\n", "WEBSOCKET", client->remoteIP().toString().c_str());
|
||||||
|
client->text(stateBuffer);
|
||||||
|
} else if (type == WS_EVT_DISCONNECT) {
|
||||||
|
Serial.printf("[%-9s] Disconnected: %s\n", "WEBSOCKET", client->remoteIP().toString().c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void httpSetup() {
|
||||||
|
Serial.println("[HTTP ] Starting HTTP server");
|
||||||
|
DefaultHeaders::Instance().addHeader("Access-Control-Allow-Origin", "*");
|
||||||
|
server.onNotFound([](AsyncWebServerRequest *request) {
|
||||||
|
const auto path = request->url();
|
||||||
|
if (path.endsWith("/") && path.length() > 1) {
|
||||||
|
request->redirect(path.substring(0, path.length() - 1));
|
||||||
|
} else {
|
||||||
|
request->send(404, "text/plain", "Not Found");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
server.on("/state", [](AsyncWebServerRequest *request) {
|
||||||
|
Serial.println("[HTTP ] " + request->url());
|
||||||
|
request->send(200, "application/json", stateBuffer);
|
||||||
|
});
|
||||||
|
server.on("/stop", [](AsyncWebServerRequest *request) {
|
||||||
|
Serial.println("[HTTP ] " + request->url());
|
||||||
|
playerState = PLAYER_STOP;
|
||||||
|
request->send(200);
|
||||||
|
});
|
||||||
|
server.on("/play", [](AsyncWebServerRequest *request) {
|
||||||
|
Serial.println("[HTTP ] " + request->url());
|
||||||
|
playerState = PLAYER_PLAY;
|
||||||
|
request->send(200);
|
||||||
|
});
|
||||||
|
server.on("/pause", [](AsyncWebServerRequest *request) {
|
||||||
|
Serial.println("[HTTP ] " + request->url());
|
||||||
|
playerState = PLAYER_PAUSE;
|
||||||
|
request->send(200);
|
||||||
|
});
|
||||||
|
server.on("/mute", [](AsyncWebServerRequest *request) {
|
||||||
|
Serial.println("[HTTP ] " + request->url());
|
||||||
|
audioMute = true;
|
||||||
|
request->send(200);
|
||||||
|
});
|
||||||
|
server.on("/unmute", [](AsyncWebServerRequest *request) {
|
||||||
|
Serial.println("[HTTP ] " + request->url());
|
||||||
|
audioMute = false;
|
||||||
|
request->send(200);
|
||||||
|
});
|
||||||
|
server.on("/up", [](AsyncWebServerRequest *request) {
|
||||||
|
Serial.println("[HTTP ] " + request->url());
|
||||||
|
audioVolume = max(0.0f, min(audioVolume + 0.05f, 1.0f));
|
||||||
|
request->send(200);
|
||||||
|
});
|
||||||
|
server.on("/down", [](AsyncWebServerRequest *request) {
|
||||||
|
Serial.println("[HTTP ] " + request->url());
|
||||||
|
audioVolume = max(0.0f, min(audioVolume - 0.05f, 1.0f));
|
||||||
|
request->send(200);
|
||||||
|
});
|
||||||
|
server.on("/next", [](AsyncWebServerRequest *request) {
|
||||||
|
Serial.println("[HTTP ] " + request->url());
|
||||||
|
playerSkip += 1;
|
||||||
|
request->send(200);
|
||||||
|
});
|
||||||
|
server.on("/back", [](AsyncWebServerRequest *request) {
|
||||||
|
Serial.println("[HTTP ] " + request->url());
|
||||||
|
playerSkip -= 1;
|
||||||
|
request->send(200);
|
||||||
|
});
|
||||||
|
ws.onEvent(onWebSocketEvent);
|
||||||
|
server.addHandler(&ws);
|
||||||
|
server.begin();
|
||||||
|
}
|
||||||
|
|
||||||
|
void httpLoop() {
|
||||||
|
ws.cleanupClients();
|
||||||
|
}
|
||||||
|
|
||||||
|
void websocketSendAll() {
|
||||||
|
ws.textAll(stateBuffer);
|
||||||
|
}
|
||||||
10
src/http.h
Normal file
10
src/http.h
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
#ifndef HTTP_H
|
||||||
|
#define HTTP_H
|
||||||
|
|
||||||
|
void httpSetup();
|
||||||
|
|
||||||
|
void httpLoop();
|
||||||
|
|
||||||
|
void websocketSendAll();
|
||||||
|
|
||||||
|
#endif
|
||||||
15
src/main.cpp
15
src/main.cpp
@ -1,5 +1,7 @@
|
|||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
|
|
||||||
|
#include "audio.h"
|
||||||
|
#include "http.h"
|
||||||
#include "wifi.h"
|
#include "wifi.h"
|
||||||
#include "player.h"
|
#include "player.h"
|
||||||
#include "playlist.h"
|
#include "playlist.h"
|
||||||
@ -9,13 +11,20 @@ void setup() {
|
|||||||
Serial.begin(115200);
|
Serial.begin(115200);
|
||||||
Serial.println("\n\n\nStartup!");
|
Serial.println("\n\n\nStartup!");
|
||||||
|
|
||||||
playerSetup();
|
audioSetup();
|
||||||
|
|
||||||
playlistClear();
|
playlistClear();
|
||||||
playlistAdd("ICY|http://liveradio.sr.de/sr/sr1/mp3/128/stream.mp3|SR1");
|
playlistAdd("ICY|http://liveradio.sr.de/sr/sr1/mp3/128/stream.mp3|SR1"); // 48000 Hz
|
||||||
playlistAdd("ICY|https://stream.rockantenne.de/rockantenne/stream/mp3|Rockantenne Classic Perlen");
|
playlistAdd("ICY|http://uk3.internet-radio.com:8082/live|1940s Radio"); // 44100 Hz TODO stuttering
|
||||||
|
playlistAdd("ICY|https://das-edge16-live365-dal02.cdnstream.com/a22508|Mistletoe Radio"); // 44100 Hz TODO stuttering
|
||||||
|
playlistAdd("ICY|https://stream.rockantenne.de/rockantenne/stream/mp3|Rockantenne Classic Perlen"); // 44100 Hz TODO stuttering
|
||||||
|
|
||||||
|
wifiLoop();
|
||||||
|
httpSetup();
|
||||||
}
|
}
|
||||||
|
|
||||||
void loop() {
|
void loop() {
|
||||||
wifiLoop();
|
wifiLoop();
|
||||||
playerLoop();
|
playerLoop();
|
||||||
|
httpLoop();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,8 +1,9 @@
|
|||||||
#include "player.h"
|
#include "player.h"
|
||||||
|
|
||||||
#include <Arduino.h>
|
#include <ArduinoJson.h>
|
||||||
|
|
||||||
#include "audio.h"
|
#include "audio.h"
|
||||||
|
#include "http.h"
|
||||||
#include "playlist.h"
|
#include "playlist.h"
|
||||||
|
|
||||||
#define DELAY_MS_ADD 250UL
|
#define DELAY_MS_ADD 250UL
|
||||||
@ -12,25 +13,93 @@ unsigned long errorMs = 0;
|
|||||||
|
|
||||||
unsigned long delayMs = 0;
|
unsigned long delayMs = 0;
|
||||||
|
|
||||||
void playerSetup() {
|
int playerSkip = 0;
|
||||||
audioSetup();
|
|
||||||
|
PlayerState playerState = PLAYER_PLAY;
|
||||||
|
|
||||||
|
char stateBuffer0[200] = "{}";
|
||||||
|
|
||||||
|
char stateBuffer1[200];
|
||||||
|
|
||||||
|
char *stateBuffer = stateBuffer0;
|
||||||
|
|
||||||
|
bool stateBufferUpdateNeeded = true;
|
||||||
|
|
||||||
|
const char *getPlayerStateString() {
|
||||||
|
switch (playerState) {
|
||||||
|
case PLAYER_STOP:
|
||||||
|
return "STOP";
|
||||||
|
case PLAYER_PLAY:
|
||||||
|
return "PLAY";
|
||||||
|
case PLAYER_PAUSE:
|
||||||
|
return "PAUSE";
|
||||||
|
default: return "[???]";
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void playerLoop() {
|
void stateBufferUpdateRequest() {
|
||||||
if (audioLoop()) {
|
stateBufferUpdateNeeded = true;
|
||||||
errorMs = 0;
|
}
|
||||||
|
|
||||||
|
void updateStateBufferIfNeeded() {
|
||||||
|
if (!stateBufferUpdateNeeded) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (errorMs > 0 && millis() - errorMs < delayMs) {
|
stateBufferUpdateNeeded = false;
|
||||||
return;
|
auto json = JsonDocument();
|
||||||
|
const auto entry = playlistCurrent();
|
||||||
|
json["state"] = getPlayerStateString();
|
||||||
|
json["entry"]["type"] = entry.type;
|
||||||
|
json["entry"]["url"] = entry.url;
|
||||||
|
json["entry"]["title"] = entry.title;
|
||||||
|
json["volume"] = audioVolume;
|
||||||
|
json["mute"] = audioMute;
|
||||||
|
json["playlist"]["count"] = playlistGetCount();
|
||||||
|
json["playlist"]["index"] = playlistGetIndex();
|
||||||
|
if (stateBuffer == stateBuffer0) {
|
||||||
|
serializeJson(json, stateBuffer1, sizeof(stateBuffer1));
|
||||||
|
stateBuffer = stateBuffer1;
|
||||||
|
} else {
|
||||||
|
serializeJson(json, stateBuffer0, sizeof(stateBuffer0));
|
||||||
|
stateBuffer = stateBuffer0;
|
||||||
}
|
}
|
||||||
const auto entry = playlistNextOrRepeatOneIfEnabled();
|
websocketSendAll();
|
||||||
if (audioPlay(entry)) {
|
}
|
||||||
|
|
||||||
|
void playerStart(const Entry &entry) {
|
||||||
|
if (entry.type == "") {
|
||||||
|
Serial.println("[PLAYER ] Not playing empty Entry => STOP");
|
||||||
|
playerState = PLAYER_STOP;
|
||||||
|
} else if (audioPlay(entry)) {
|
||||||
errorMs = 0;
|
errorMs = 0;
|
||||||
delayMs = 0;
|
delayMs = 0;
|
||||||
} else {
|
} else {
|
||||||
errorMs = max(1UL, millis());
|
errorMs = max(1UL, millis());
|
||||||
delayMs = min(DELAY_MS_MAX, delayMs + DELAY_MS_ADD);
|
delayMs = min(DELAY_MS_MAX, delayMs + DELAY_MS_ADD);
|
||||||
Serial.printf("retry delay: %d ms\n", delayMs);
|
Serial.printf("[PLAYER ] Retry delay: %d ms\n", delayMs);
|
||||||
}
|
}
|
||||||
|
stateBufferUpdateRequest();
|
||||||
|
}
|
||||||
|
|
||||||
|
void playerLoop() {
|
||||||
|
if (playerSkip != 0) {
|
||||||
|
playerState = PLAYER_PLAY;
|
||||||
|
playerStart(playlistNext(playerSkip));
|
||||||
|
playerSkip = 0;
|
||||||
|
} else {
|
||||||
|
const auto playing = audioLoop();
|
||||||
|
if (playing) {
|
||||||
|
errorMs = 0;
|
||||||
|
}
|
||||||
|
if (playing && playerState == PLAYER_STOP) {
|
||||||
|
errorMs = 0;
|
||||||
|
audioStop();
|
||||||
|
stateBufferUpdateRequest();
|
||||||
|
}
|
||||||
|
if (!playing && playerState == PLAYER_PLAY && (errorMs == 0 || millis() - errorMs >= delayMs)) {
|
||||||
|
playerStart(playlistNextOrRepeatOneIfEnabled());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
updateStateBufferIfNeeded();
|
||||||
}
|
}
|
||||||
|
|||||||
14
src/player.h
14
src/player.h
@ -1,8 +1,20 @@
|
|||||||
#ifndef PLAYER_H
|
#ifndef PLAYER_H
|
||||||
#define PLAYER_H
|
#define PLAYER_H
|
||||||
|
|
||||||
void playerSetup();
|
enum PlayerState {
|
||||||
|
PLAYER_STOP,
|
||||||
|
PLAYER_PLAY,
|
||||||
|
PLAYER_PAUSE,
|
||||||
|
};
|
||||||
|
|
||||||
|
extern PlayerState playerState;
|
||||||
|
|
||||||
|
extern int playerSkip;
|
||||||
|
|
||||||
|
extern char *stateBuffer;
|
||||||
|
|
||||||
void playerLoop();
|
void playerLoop();
|
||||||
|
|
||||||
|
void stateBufferUpdateRequest();
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@ -3,6 +3,8 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
#include <SD.h>
|
#include <SD.h>
|
||||||
|
|
||||||
|
#include "AudioTools/CoreAudio/AudioBasic/Collections/Vector.h" // needed for following import (library mistake???=
|
||||||
|
#include "AudioTools/CoreAudio/BaseConverter.h" // needed for following import (library mistake???=
|
||||||
#include "AudioTools/AudioLibs/I2SCodecStream.h"
|
#include "AudioTools/AudioLibs/I2SCodecStream.h"
|
||||||
|
|
||||||
bool playlistRepeatOne = false;
|
bool playlistRepeatOne = false;
|
||||||
@ -18,7 +20,7 @@ size_t playlistIndex = 0;
|
|||||||
std::vector<Entry> playlistEntries;
|
std::vector<Entry> playlistEntries;
|
||||||
|
|
||||||
void playlistClear() {
|
void playlistClear() {
|
||||||
Serial.println("[PLAYLIST] clear");
|
Serial.println("[PLAYLIST ] clear");
|
||||||
playlistTitle = "";
|
playlistTitle = "";
|
||||||
playlistIndex = 0;
|
playlistIndex = 0;
|
||||||
playlistEntries.clear();
|
playlistEntries.clear();
|
||||||
@ -27,17 +29,17 @@ void playlistClear() {
|
|||||||
|
|
||||||
void playlistAdd(String entry) {
|
void playlistAdd(String entry) {
|
||||||
entry.trim();
|
entry.trim();
|
||||||
Serial.println("[PLAYLIST] add: " + entry);
|
Serial.println("[PLAYLIST ] [ADD] " + entry);
|
||||||
|
|
||||||
const auto index_type_url = entry.indexOf('|');
|
const auto index_type_url = entry.indexOf('|');
|
||||||
if (index_type_url < 0) {
|
if (index_type_url < 0) {
|
||||||
Serial.println("[PLAYLIST] type/url-delimiter not found: " + entry);
|
Serial.println("[PLAYLIST ] type/url-delimiter not found: " + entry);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
auto type = entry.substring(0, index_type_url);
|
auto type = entry.substring(0, index_type_url);
|
||||||
type.trim();
|
type.trim();
|
||||||
if (type != "TITLE" && type != "ICY" && type != "SD" && type != "SNAP") {
|
if (type != "TITLE" && type != "ICY" && type != "SD" && type != "SNAP") {
|
||||||
Serial.println("[PLAYLIST] Unknown type: " + type);
|
Serial.println("[PLAYLIST ] Unknown type: " + type);
|
||||||
}
|
}
|
||||||
|
|
||||||
entry = entry.substring(index_type_url + 1);
|
entry = entry.substring(index_type_url + 1);
|
||||||
@ -50,13 +52,13 @@ void playlistAdd(String entry) {
|
|||||||
|
|
||||||
const auto index_url_title = entry.indexOf('|');
|
const auto index_url_title = entry.indexOf('|');
|
||||||
if (index_url_title < 0) {
|
if (index_url_title < 0) {
|
||||||
Serial.println("[PLAYLIST] url/title-delimiter not found: " + entry);
|
Serial.println("[PLAYLIST ] url/title-delimiter not found: " + entry);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
auto url = entry.substring(0, index_url_title);
|
auto url = entry.substring(0, index_url_title);
|
||||||
url.trim();
|
url.trim();
|
||||||
if (url.isEmpty()) {
|
if (url.isEmpty()) {
|
||||||
Serial.println("[PLAYLIST] url is empty.");
|
Serial.println("[PLAYLIST ] url is empty.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -68,18 +70,18 @@ void playlistAdd(String entry) {
|
|||||||
|
|
||||||
void playlistLoad(const String &path) {
|
void playlistLoad(const String &path) {
|
||||||
playlistClear();
|
playlistClear();
|
||||||
Serial.println("[PLAYLIST] Loading playlist: " + path);
|
Serial.println("[PLAYLIST ] Loading playlist: " + path);
|
||||||
if (!SD.begin(PIN_AUDIO_KIT_SD_CARD_CS)) {
|
if (!SD.begin(PIN_AUDIO_KIT_SD_CARD_CS)) {
|
||||||
Serial.println("[PLAYLIST] Failed to initialize SD card.");
|
Serial.println("[PLAYLIST ] Failed to initialize SD card.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!SD.exists(path)) {
|
if (!SD.exists(path)) {
|
||||||
Serial.println("[PLAYLIST] File not found.");
|
Serial.println("[PLAYLIST ] File not found.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
auto file = SD.open(path, FILE_READ);
|
auto file = SD.open(path, FILE_READ);
|
||||||
if (!file) {
|
if (!file) {
|
||||||
Serial.println("[PLAYLIST] Failed to open file.");
|
Serial.println("[PLAYLIST ] Failed to open file.");
|
||||||
}
|
}
|
||||||
|
|
||||||
while (file.available() > 0) {
|
while (file.available() > 0) {
|
||||||
@ -102,23 +104,27 @@ Entry playlistCurrent() {
|
|||||||
return playlistSet(playlistIndex);
|
return playlistSet(playlistIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
Entry playlistNext() {
|
Entry playlistNext(const int amount) {
|
||||||
if (playlistIndex >= playlistEntries.size() - 1 && !playlistRepeatAll) {
|
if (amount < 0 && playlistIndex <= 0 && !playlistRepeatAll) {
|
||||||
return Entry();
|
return Entry();
|
||||||
}
|
}
|
||||||
return playlistSet(playlistIndex + 1);
|
if (amount > 0 && playlistIndex >= playlistEntries.size() - 1 && !playlistRepeatAll) {
|
||||||
|
return Entry();
|
||||||
|
}
|
||||||
|
return playlistSet(playlistIndex + amount);
|
||||||
}
|
}
|
||||||
|
|
||||||
Entry playlistNextOrRepeatOneIfEnabled() {
|
Entry playlistNextOrRepeatOneIfEnabled() {
|
||||||
if (playlistRepeatOne) {
|
if (playlistRepeatOne) {
|
||||||
return playlistCurrent();
|
return playlistCurrent();
|
||||||
}
|
}
|
||||||
return playlistNext();
|
return playlistNext(+1);
|
||||||
}
|
}
|
||||||
|
|
||||||
Entry playlistPrevious() {
|
size_t playlistGetCount() {
|
||||||
if (playlistIndex <= 0 && !playlistRepeatAll) {
|
return playlistEntries.size();
|
||||||
return Entry();
|
}
|
||||||
}
|
|
||||||
return playlistSet(playlistIndex - 1);
|
size_t playlistGetIndex() {
|
||||||
|
return playlistIndex;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -11,10 +11,12 @@ void playlistLoad(const String &path);
|
|||||||
|
|
||||||
Entry playlistCurrent();
|
Entry playlistCurrent();
|
||||||
|
|
||||||
Entry playlistNext();
|
Entry playlistNext(int amount);
|
||||||
|
|
||||||
Entry playlistNextOrRepeatOneIfEnabled();
|
Entry playlistNextOrRepeatOneIfEnabled();
|
||||||
|
|
||||||
Entry playlistPrevious();
|
size_t playlistGetCount();
|
||||||
|
|
||||||
|
size_t playlistGetIndex();
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@ -12,21 +12,21 @@ void wifiLoop() {
|
|||||||
if (connected) {
|
if (connected) {
|
||||||
// still connected
|
// still connected
|
||||||
} else {
|
} else {
|
||||||
Serial.printf("WIFI disconnected!\n");
|
Serial.printf("[WIFI ] Disconnected!\n");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (connected) {
|
if (connected) {
|
||||||
wifiLastMillis = 0;
|
wifiLastMillis = 0;
|
||||||
Serial.printf("WIFI connected: %s\n", WiFi.localIP().toString().c_str());
|
Serial.printf("[WIFI ] Connected: %s\n", WiFi.localIP().toString().c_str());
|
||||||
} else if (wifiLastMillis == 0 || millis() - wifiLastMillis > 10000) {
|
} else if (wifiLastMillis == 0 || millis() - wifiLastMillis > 10000) {
|
||||||
WiFi.disconnect();
|
WiFi.disconnect();
|
||||||
yield();
|
yield();
|
||||||
if (wifiLastMillis != 0) {
|
if (wifiLastMillis != 0) {
|
||||||
Serial.printf("WIFI connect timeout!\n");
|
Serial.printf("[WIFI ] Connect timeout!\n");
|
||||||
}
|
}
|
||||||
wifiLastMillis = max(1UL, millis());
|
wifiLastMillis = max(1UL, millis());
|
||||||
const auto ssid = "HappyNet";
|
const auto ssid = "HappyNet";
|
||||||
Serial.printf("WIFI connecting: %s\n", ssid);
|
Serial.printf("[WIFI ] Connecting: %s\n", ssid);
|
||||||
WiFi.begin(ssid, "1Grausame!Sackratte7");
|
WiFi.begin(ssid, "1Grausame!Sackratte7");
|
||||||
yield();
|
yield();
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user