Entry for: type|url|title
This commit is contained in:
parent
35c8ec13ee
commit
bcf6001c55
21
src/Entry.h
Normal file
21
src/Entry.h
Normal file
@ -0,0 +1,21 @@
|
||||
#ifndef ENTRY_H
|
||||
#define ENTRY_H
|
||||
|
||||
#include <WString.h>
|
||||
#include <utility>
|
||||
|
||||
struct Entry {
|
||||
|
||||
const String type;
|
||||
|
||||
const String url;
|
||||
|
||||
const String title;
|
||||
|
||||
explicit Entry(String type = "", String url = "", String title = "") : type(std::move(type)), url(std::move(url)), title(std::move(title)) {
|
||||
//
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
116
src/audio.cpp
116
src/audio.cpp
@ -45,15 +45,40 @@ void audioStop() {
|
||||
snap.end();
|
||||
}
|
||||
|
||||
bool audioBeginMP3Stream(const char *code, Stream &source) {
|
||||
bool audioPlayInit(const Entry &entry) {
|
||||
audioStop();
|
||||
|
||||
Serial.printf("[%s] %s", entry.type.c_str(), entry.url.c_str());
|
||||
running = true;
|
||||
|
||||
if (entry.type == "ICY" || entry.type == "SNAP") {
|
||||
if (!isWifiConnected()) {
|
||||
Serial.printf("[%s] WiFi not connected.\n", entry.type.c_str());
|
||||
audioStop();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (entry.type == "SD") {
|
||||
if (!SD.begin(PIN_AUDIO_KIT_SD_CARD_CS)) {
|
||||
Serial.printf("[%s] Failed to mount SD-card.\n", entry.type.c_str());
|
||||
audioStop();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool audioBeginMP3Stream(const Entry &entry, Stream &source) {
|
||||
if (!mp3Decoder.begin()) {
|
||||
Serial.printf("[%s] Failed to start MP3DecoderHelix.", code);
|
||||
Serial.printf("[%s] Failed to start MP3DecoderHelix.", entry.type.c_str());
|
||||
audioStop();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!mp3Stream.begin()) {
|
||||
Serial.printf("[%s] Failed to start EncodedAudioStream.", code);
|
||||
Serial.printf("[%s] Failed to start EncodedAudioStream.", entry.type.c_str());
|
||||
audioStop();
|
||||
return false;
|
||||
}
|
||||
@ -61,7 +86,7 @@ bool audioBeginMP3Stream(const char *code, Stream &source) {
|
||||
copier.begin(mp3Stream, source);
|
||||
|
||||
if (copier.copy() == 0) {
|
||||
Serial.printf("[%s] Failed to copy initial data.", code);
|
||||
Serial.printf("[%s] Failed to copy initial data.", entry.type.c_str());
|
||||
audioStop();
|
||||
return false;
|
||||
}
|
||||
@ -69,72 +94,47 @@ bool audioBeginMP3Stream(const char *code, Stream &source) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool audioPlayInit(const char *code, const String &url, const bool needsWiFi, const bool needsSD) {
|
||||
audioStop();
|
||||
|
||||
Serial.printf("[%s] %s", code, url.c_str());
|
||||
running = true;
|
||||
|
||||
if (needsWiFi) {
|
||||
if (!isWifiConnected()) {
|
||||
Serial.printf("[%s] WiFi not connected.\n", code);
|
||||
audioStop();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (needsSD) {
|
||||
if (!SD.begin(PIN_AUDIO_KIT_SD_CARD_CS)) {
|
||||
Serial.printf("[%s] Failed to mount SD-card.\n", code);
|
||||
audioStop();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool audioPlayICY(const String &url) {
|
||||
if (!audioPlayInit("ICY", url, true, false)) {
|
||||
bool audioPlayICY(const Entry &entry) {
|
||||
if (!audioPlayInit(entry)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!icy.begin(url.c_str())) {
|
||||
Serial.println("[ICY] Failed to start ICYStream.");
|
||||
if (!icy.begin(entry.url.c_str())) {
|
||||
Serial.printf("[%s] Failed to start ICYStream.\n", entry.type.c_str());
|
||||
audioStop();
|
||||
return false;
|
||||
}
|
||||
|
||||
return audioBeginMP3Stream("ICY", icy);
|
||||
return audioBeginMP3Stream(entry, icy);
|
||||
}
|
||||
|
||||
bool audioPlaySNAP(const String &url) {
|
||||
if (!audioPlayInit("SNAP", url, true, false)) {
|
||||
bool audioPlaySNAP(const Entry &entry) {
|
||||
if (!audioPlayInit(entry)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
IPAddress ip;
|
||||
if (!WiFiClass::hostByName(url.c_str(), ip)) {
|
||||
Serial.println("[SNAP] Failed to resolve host.");
|
||||
if (!WiFiClass::hostByName(entry.url.c_str(), ip)) {
|
||||
Serial.printf("[%s] Failed to resolve host.\n", entry.type.c_str());
|
||||
audioStop();
|
||||
return false;
|
||||
}
|
||||
|
||||
snap.setServerIP(ip);
|
||||
if (!snap.begin()) {
|
||||
Serial.println("[SNAP] Failed to connect.");
|
||||
Serial.printf("[%s] Failed to connect.\n", entry.type.c_str());
|
||||
audioStop();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!opusDecoder.begin()) {
|
||||
Serial.println("[SNAP] Failed to start OpusAudioDecoder.");
|
||||
Serial.printf("[%s] Failed to start OpusAudioDecoder.\n", entry.type.c_str());
|
||||
audioStop();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!snap.doLoop()) {
|
||||
Serial.println("[SNAP] Failed to copy initial data.");
|
||||
Serial.printf("[%s] Failed to copy initial data.\n", entry.type.c_str());
|
||||
audioStop();
|
||||
return false;
|
||||
}
|
||||
@ -142,38 +142,42 @@ bool audioPlaySNAP(const String &url) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool audioPlaySD(const String &url) {
|
||||
if (!audioPlayInit("SD", url, false, true)) {
|
||||
bool audioPlaySD(const Entry &entry) {
|
||||
if (!audioPlayInit(entry)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!SD.exists(url.c_str())) {
|
||||
Serial.println("[SD] File not found.");
|
||||
if (!SD.exists(entry.url.c_str())) {
|
||||
Serial.printf("[%s] File not found.\n", entry.type.c_str());
|
||||
audioStop();
|
||||
return false;
|
||||
}
|
||||
|
||||
file = SD.open(url.c_str(), FILE_READ);
|
||||
file = SD.open(entry.url.c_str(), FILE_READ);
|
||||
if (!file) {
|
||||
Serial.println("[SD] Failed to open file.");
|
||||
Serial.printf("[%s] Failed to open file.\n", entry.type.c_str());
|
||||
audioStop();
|
||||
return false;
|
||||
}
|
||||
|
||||
return audioBeginMP3Stream("SD", file);
|
||||
return audioBeginMP3Stream(entry, file);
|
||||
}
|
||||
|
||||
bool audioPlay(const String &url) {
|
||||
if (url.startsWith("http://") || url.startsWith("https://")) {
|
||||
return audioPlayICY(url);
|
||||
bool audioPlay(const Entry &entry) {
|
||||
audioStop();
|
||||
if (entry.type == "") {
|
||||
return false;
|
||||
}
|
||||
if (url.startsWith("sd://")) {
|
||||
return audioPlaySD(url.substring(5));
|
||||
if (entry.type == "ICY") {
|
||||
return audioPlayICY(entry);
|
||||
}
|
||||
if (url.startsWith("snap://") < 0) {
|
||||
return audioPlaySNAP(url.substring(7));
|
||||
if (entry.type == "SD") {
|
||||
return audioPlaySD(entry);
|
||||
}
|
||||
Serial.printf("[ERROR] unknown protocol: %s\n", url.c_str());
|
||||
if (entry.type == "SNAP") {
|
||||
return audioPlaySNAP(entry);
|
||||
}
|
||||
Serial.printf("[ERROR] unknown type: %s\n", entry.type.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
#ifndef AUDIO_H
|
||||
#define AUDIO_H
|
||||
|
||||
#include <WString.h>
|
||||
#include "playlist.h"
|
||||
|
||||
bool audioPlay(const String &url);
|
||||
bool audioPlay(const Entry &entry);
|
||||
|
||||
void audioStop();
|
||||
|
||||
|
||||
@ -9,7 +9,8 @@ void setup() {
|
||||
Serial.begin(115200);
|
||||
playerSetup();
|
||||
playlistClear();
|
||||
playlistAdd("http://liveradio.sr.de/sr/sr1/mp3/128/stream.mp3");
|
||||
playlistAdd("ICY|http://liveradio.sr.de/sr/sr1/mp3/128/stream.mp3|SR1");
|
||||
playlistAdd("ICY|https://stream.rockantenne.de/rockantenne/stream/mp3|Rockantenne Classic Perlen");
|
||||
}
|
||||
|
||||
void loop() {
|
||||
|
||||
@ -24,11 +24,8 @@ void playerLoop() {
|
||||
if (errorMs > 0 && millis() - errorMs < delayMs) {
|
||||
return;
|
||||
}
|
||||
const auto url = playlistNextOrRepeatOneIfEnabled();
|
||||
if (url.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
if (audioPlay(url)) {
|
||||
const auto entry = playlistNextOrRepeatOneIfEnabled();
|
||||
if (audioPlay(entry)) {
|
||||
errorMs = 0;
|
||||
delayMs = 0;
|
||||
} else {
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
#include "playlist.h"
|
||||
|
||||
#include <vector>
|
||||
#include <SD.h>
|
||||
|
||||
#include "AudioTools/AudioLibs/I2SCodecStream.h"
|
||||
#include <vector>
|
||||
|
||||
bool playlistRepeatOne = false;
|
||||
|
||||
@ -15,22 +15,53 @@ String playlistTitle = "";
|
||||
|
||||
size_t playlistIndex = 0;
|
||||
|
||||
std::vector<String> playlistEntries;
|
||||
std::vector<Entry> playlistEntries;
|
||||
|
||||
void playlistClear() {
|
||||
playlistTitle = "";
|
||||
playlistIndex = 0;
|
||||
playlistEntries.clear();
|
||||
std::vector<String>().swap(playlistEntries);
|
||||
std::vector<Entry>().swap(playlistEntries);
|
||||
}
|
||||
|
||||
void playlistAdd(String url) {
|
||||
url.trim();
|
||||
if (url.startsWith("http://") || url.startsWith("https://") || url.startsWith("sd://") || url.startsWith("snap://")) {
|
||||
playlistEntries.push_back(url);
|
||||
} else if (url.startsWith("title://")) {
|
||||
playlistTitle = url.substring(8);
|
||||
void playlistAdd(String entry) {
|
||||
entry.trim();
|
||||
|
||||
const auto index_type_url = entry.indexOf('|');
|
||||
if (index_type_url < 0) {
|
||||
Serial.println("[PLAYLIST] type/url-delimiter not found: " + entry);
|
||||
return;
|
||||
}
|
||||
auto type = entry.substring(0, index_type_url);
|
||||
type.trim();
|
||||
if (type != "TITLE" && type != "ICY" && type != "SD" && type != "SNAP") {
|
||||
Serial.println("[PLAYLIST] Unknown type: " + type);
|
||||
}
|
||||
|
||||
entry = entry.substring(index_type_url + 1);
|
||||
entry.trim();
|
||||
|
||||
if (type == "TITLE") {
|
||||
playlistTitle = entry;
|
||||
return;
|
||||
}
|
||||
|
||||
const auto index_url_title = entry.indexOf('|');
|
||||
if (index_url_title < 0) {
|
||||
Serial.println("[PLAYLIST] url/title-delimiter not found: " + entry);
|
||||
return;
|
||||
}
|
||||
auto url = entry.substring(0, index_url_title);
|
||||
url.trim();
|
||||
if (url.isEmpty()) {
|
||||
Serial.println("[PLAYLIST] url is empty.");
|
||||
return;
|
||||
}
|
||||
|
||||
auto title = entry.substring(index_url_title + 1);
|
||||
title.trim();
|
||||
|
||||
playlistEntries.emplace_back(type, url, title);
|
||||
}
|
||||
|
||||
void playlistLoad(const String &path) {
|
||||
@ -50,42 +81,42 @@ void playlistLoad(const String &path) {
|
||||
}
|
||||
|
||||
while (file.available() > 0) {
|
||||
String entry = file.readStringUntil('\n');
|
||||
const String entry = file.readStringUntil('\n');
|
||||
playlistAdd(entry);
|
||||
}
|
||||
file.close();
|
||||
}
|
||||
|
||||
String playlistSet(const size_t index) {
|
||||
Entry playlistSet(const size_t index) {
|
||||
const auto size = playlistEntries.size();
|
||||
if (size == 0) {
|
||||
return "";
|
||||
return Entry();
|
||||
}
|
||||
playlistIndex = (index % size + size) % size;
|
||||
return playlistEntries[playlistIndex];
|
||||
}
|
||||
|
||||
String playlistCurrent() {
|
||||
Entry playlistCurrent() {
|
||||
return playlistSet(playlistIndex);
|
||||
}
|
||||
|
||||
String playlistNext() {
|
||||
Entry playlistNext() {
|
||||
if (playlistIndex >= playlistEntries.size() - 1 && !playlistRepeatAll) {
|
||||
return "";
|
||||
return Entry();
|
||||
}
|
||||
return playlistSet(playlistIndex + 1);
|
||||
}
|
||||
|
||||
String playlistNextOrRepeatOneIfEnabled() {
|
||||
Entry playlistNextOrRepeatOneIfEnabled() {
|
||||
if (playlistRepeatOne) {
|
||||
return playlistCurrent();
|
||||
}
|
||||
return playlistNext();
|
||||
}
|
||||
|
||||
String playlistPrevious() {
|
||||
Entry playlistPrevious() {
|
||||
if (playlistIndex <= 0 && !playlistRepeatAll) {
|
||||
return "";
|
||||
return Entry();
|
||||
}
|
||||
return playlistSet(playlistIndex - 1);
|
||||
}
|
||||
|
||||
@ -1,20 +1,20 @@
|
||||
#ifndef PLAYLIST_H
|
||||
#define PLAYLIST_H
|
||||
|
||||
#include <WString.h>
|
||||
#include "Entry.h"
|
||||
|
||||
void playlistClear();
|
||||
|
||||
void playlistAdd(String url);
|
||||
void playlistAdd(String entry);
|
||||
|
||||
void playlistLoad(const String &path);
|
||||
|
||||
String playlistCurrent();
|
||||
Entry playlistCurrent();
|
||||
|
||||
String playlistNext();
|
||||
Entry playlistNext();
|
||||
|
||||
String playlistNextOrRepeatOneIfEnabled();
|
||||
Entry playlistNextOrRepeatOneIfEnabled();
|
||||
|
||||
String playlistPrevious();
|
||||
Entry playlistPrevious();
|
||||
|
||||
#endif
|
||||
|
||||
Loading…
Reference in New Issue
Block a user