diff --git a/README.md b/README.md
index 1430809..afa1da4 100644
--- a/README.md
+++ b/README.md
@@ -13,9 +13,9 @@ I started this project back in october 2019 and never expected it to become that
* Partition-layout for ESP32 is changed along with this branch. This step was necessary in order to resize (enlarge) the memory-region where especially the assignments for the RFID-tags are saved. As all permanent settings (e.g. WiFi-settings) are saved there too, it's necessary to re-enter WiFi-credentials after update. But the most important thing is to recover the assignments for the RFID-tags. Please consult my [migration-document](https://forum.espuino.de/t/wechsel-zum-refactoring-branch-was-ist-zu-beachten/510).
## Changelog
Last three events:
+* 20.07.2021: Adding new playmode: multiple webradio-paylist from local .m3u-file
* 13.07.2021: Adding OTA-support via webGUI
* 09.07.2021: Making branch `refactoring` the new master
-* 09.07.2021: Making master new branch `old` (not maintained any longer!)
## Known bugs
* Some webstreams don't run. Guess it's a combination of saturated connection-pool and lack of heap-memory. Works probably better if ESP32-WROVER (e.g. Lolin D32 pro) is used, as this chip has PSRAM. Advice: Don't enable modules (e.g. MQTT) if you don't need them as this could save memory (and trouble).
## ESPuino - what's that?
@@ -278,6 +278,7 @@ It's not just simply playing music; different playmodes are supported:
* `folder/playlist (alph. sorted)` => plays all tracks in alph. order from a folder forever
* `folder/playlist (random order)` => plays all tracks in random order from a folder forever
* `webradio` => always only one "track": plays a webstream
+* `Webradiolist from local .m3u-File` => can be one or more webradio-stations with local .m3u as sourcefile
### Modification RFID-tags
There are special RFID-tags, that don't start music by themself but can modify things. If applied a second time, it's previous action/modification will be reversed. Please note: all sleep-modes do dimming (Neopixel) automatically because it's supposed to be used in the evening when going to bed. Well, at least that's my children's indication :-) So first make sure to start the music then use a modification-card in order to apply your desired modification:
diff --git a/changelog.md b/changelog.md
index 550f974..6fd32ca 100644
--- a/changelog.md
+++ b/changelog.md
@@ -9,6 +9,7 @@
* 09.07.2021: Making branch `refactoring` the the master
* 09.07.2021: Making master the new branch `old` (not maintained any longer!)
* 13.07.2021: Adding OTA-support via webGUI
+* 20.07.2021: Adding new playmode: multiple webradio-paylist from local .m3u-file
## Old (monolithic main.cpp)
* 11.07.2020: Added support for reversed Neopixel addressing.
diff --git a/html/management_DE.html b/html/management_DE.html
index 0058c10..ae0fc49 100644
--- a/html/management_DE.html
+++ b/html/management_DE.html
@@ -272,6 +272,7 @@
Alle Titel eines Verzeichnis (sortiert, Endlosschleife)
Alle Titel eines Verzeichnis (zufällig, Endlosschleife)
Webradio
+ Webradioliste aus .m3u-Datei
diff --git a/html/management_EN.html b/html/management_EN.html
index 3d5961a..3404b64 100644
--- a/html/management_EN.html
+++ b/html/management_EN.html
@@ -272,6 +272,7 @@
All tracks of a directory (sorted alph., loop)
All tracks of a directory (random, loop)
Webradio
+ Webradiolist from local .m3u-File
diff --git a/src/AudioPlayer.cpp b/src/AudioPlayer.cpp
index 060f4a8..3e0a342 100644
--- a/src/AudioPlayer.cpp
+++ b/src/AudioPlayer.cpp
@@ -553,7 +553,7 @@ void AudioPlayer_Task(void *parameter) {
}
}
- if (gPlayProperties.playMode == WEBSTREAM) { // Webstream
+ if (gPlayProperties.playMode == WEBSTREAM || gPlayProperties.playMode == WEBSTREAMS_LOCAL_M3U) { // Webstream
audio->connecttohost(*(gPlayProperties.playlist + gPlayProperties.currentTrackNumber));
gPlayProperties.playlistFinished = false;
} else {
@@ -866,6 +866,22 @@ void AudioPlayer_TrackQueueDispatcher(const char *_itemToPlay, const uint32_t _l
break;
}
+ case WEBSTREAMS_LOCAL_M3U: { // This is always just one "track"
+ Log_Println((char *) FPSTR(modeWebstreamM3u), LOGLEVEL_NOTICE);
+ if (Wlan_IsConnected()) {
+ xQueueSend(gTrackQueue, &(musicFiles), 0);
+ #ifdef MQTT_ENABLE
+ publishMqtt((char *) FPSTR(topicPlaymodeState), gPlayProperties.playMode, false);
+ publishMqtt((char *) FPSTR(topicRepeatModeState), NO_REPEAT, false);
+ #endif
+ } else {
+ Log_Println((char *) FPSTR(webstreamNotAvailable), LOGLEVEL_ERROR);
+ System_IndicateError();
+ gPlayProperties.playMode = NO_PLAYLIST;
+ }
+ break;
+ }
+
default:
Log_Println((char *) FPSTR(modeDoesNotExist), LOGLEVEL_ERROR);
gPlayProperties.playMode = NO_PLAYLIST;
diff --git a/src/HTMLmanagement_DE.h b/src/HTMLmanagement_DE.h
index 66a2b2d..7cdce4e 100644
--- a/src/HTMLmanagement_DE.h
+++ b/src/HTMLmanagement_DE.h
@@ -272,6 +272,7 @@ static const char management_HTML[] PROGMEM = "\
Alle Titel eines Verzeichnis (sortiert, Endlosschleife) \
Alle Titel eines Verzeichnis (zufällig, Endlosschleife) \
Webradio \
+ Webradioliste aus .m3u-Datei \
\
\
\
diff --git a/src/HTMLmanagement_EN.h b/src/HTMLmanagement_EN.h
index 4275ff6..140e198 100644
--- a/src/HTMLmanagement_EN.h
+++ b/src/HTMLmanagement_EN.h
@@ -272,6 +272,7 @@ static const char management_HTML[] PROGMEM = "\
All tracks of a directory (sorted alph., loop) \
All tracks of a directory (random, loop) \
Webradio \
+ Webradiolist from local .m3u-File \
\
\
\
diff --git a/src/Led.cpp b/src/Led.cpp
index 18da742..c373db8 100644
--- a/src/Led.cpp
+++ b/src/Led.cpp
@@ -467,7 +467,7 @@ static void Led_Task(void *parameter) {
redrawProgress = true;
}
- if (gPlayProperties.playMode != WEBSTREAM) {
+ if (gPlayProperties.playMode != WEBSTREAM && gPlayProperties.playMode != WEBSTREAMS_LOCAL_M3U) {
if (gPlayProperties.currentRelPos != lastPos || redrawProgress) {
redrawProgress = false;
lastPos = gPlayProperties.currentRelPos;
diff --git a/src/LogMessages_DE.cpp b/src/LogMessages_DE.cpp
index be95bd8..9d2561e 100644
--- a/src/LogMessages_DE.cpp
+++ b/src/LogMessages_DE.cpp
@@ -31,7 +31,7 @@
const char nameOfFileFound[] PROGMEM = "Gefundenes File";
const char reallocCalled[] PROGMEM = "Speicher reallokiert.";
const char unableToAllocateMemForLinearPlaylist[] PROGMEM = "Speicher für lineare Playlist konnte nicht allokiert werden!";
- const char numberOfValidFiles[] PROGMEM = "Anzahl gültiger Files";
+ const char numberOfValidFiles[] PROGMEM = "Anzahl gültiger Files/Webstreams";
const char newLoudnessReceivedQueue[] PROGMEM = "Neue Lautstärke empfangen via Queue";
const char newCntrlReceivedQueue[] PROGMEM = "Kontroll-Kommando empfangen via Queue";
const char newPlaylistReceived[] PROGMEM = "Neue Playlist empfangen";
@@ -72,6 +72,7 @@
const char modeAllTrackAlphSortedLoop[] PROGMEM = "Modus: Alle Tracks eines Ordners sortiert (alphabetisch) in Endlosschleife";
const char modeAllTrackRandomLoop[] PROGMEM = "Modus: Alle Tracks eines Ordners zufällig in Endlosschleife";
const char modeWebstream[] PROGMEM = "Modus: Webstream";
+ const char modeWebstreamM3u[] PROGMEM = "Modus: Webstream (lokale .m3u-Datei)";
const char webstreamNotAvailable[] PROGMEM = "Aktuell kein Webstream möglich, da keine WLAN-Verbindung vorhanden!";
const char modeDoesNotExist[] PROGMEM = "Abspielmodus existiert nicht!";
const char modeRepeatNone[] PROGMEM = "Repeatmodus: Kein Repeat";
diff --git a/src/LogMessages_EN.cpp b/src/LogMessages_EN.cpp
index 93bd9f3..459c6d5 100644
--- a/src/LogMessages_EN.cpp
+++ b/src/LogMessages_EN.cpp
@@ -31,7 +31,7 @@
const char nameOfFileFound[] PROGMEM = "File found";
const char reallocCalled[] PROGMEM = "Reallocated memory.";
const char unableToAllocateMemForLinearPlaylist[] PROGMEM = "Unable to allocate memory for linear playlist!";
- const char numberOfValidFiles[] PROGMEM = "Number of valid files";
+ const char numberOfValidFiles[] PROGMEM = "Number of valid files/webstreams";
const char newLoudnessReceivedQueue[] PROGMEM = "New volume received via queue";
const char newCntrlReceivedQueue[] PROGMEM = "Control-command received via queue";
const char newPlaylistReceived[] PROGMEM = "New playlist received";
@@ -72,6 +72,7 @@
const char modeAllTrackAlphSortedLoop[] PROGMEM = "Mode: all tracks (in alph. order) of directory as infinite loop";
const char modeAllTrackRandomLoop[] PROGMEM = "Mode: all tracks (in random order) of directory as infinite loop";
const char modeWebstream[] PROGMEM = "Mode: webstream";
+ const char modeWebstreamM3u[] PROGMEM = "Mode: Webstream (local .m3u-file)";
const char webstreamNotAvailable[] PROGMEM = "Unable to access webstream as no wifi-connection is available!";
const char modeDoesNotExist[] PROGMEM = "Playmode does not exist!";
const char modeRepeatNone[] PROGMEM = "Repeatmode: no repeat";
diff --git a/src/SdCard.cpp b/src/SdCard.cpp
index 1433d07..7bda195 100644
--- a/src/SdCard.cpp
+++ b/src/SdCard.cpp
@@ -89,6 +89,7 @@ char **SdCard_ReturnPlaylist(const char *fileName, const uint32_t _playMode) {
char cacheFileNameBuf[275];
bool readFromCacheFile = false;
bool enablePlaylistCaching = false;
+ bool enablePlaylistFromM3u = false;
// Look if file/folder requested really exists. If not => break.
File fileOrDirectory = gFSystem.open(fileName);
@@ -97,6 +98,7 @@ char **SdCard_ReturnPlaylist(const char *fileName, const uint32_t _playMode) {
return NULL;
}
+ // Create linear playlist of caching-file
#ifdef CACHED_PLAYLIST_ENABLE
strncpy(cacheFileNameBuf, fileName, sizeof(cacheFileNameBuf));
strcat(cacheFileNameBuf, "/");
@@ -140,6 +142,36 @@ char **SdCard_ReturnPlaylist(const char *fileName, const uint32_t _playMode) {
}
#endif
+ // Parse m3u-playlist and create linear-playlist out of it
+ if (_playMode == WEBSTREAMS_LOCAL_M3U) {
+ enablePlaylistFromM3u = true;
+ if (fileOrDirectory && !fileOrDirectory.isDirectory()) {
+ serializedPlaylist = (char *) x_calloc(2048, sizeof(char));
+ if (serializedPlaylist == NULL) {
+ Log_Println((char *) FPSTR(unableToAllocateMemForLinearPlaylist), LOGLEVEL_ERROR);
+ System_IndicateError();
+ return files;
+ }
+ char buf;
+ uint32_t fPos = 1;
+
+ serializedPlaylist[0] = '#';
+ while (fileOrDirectory.available() > 0) {
+ buf = fileOrDirectory.read();
+ if (buf != '\n' && buf != '\r') {
+ serializedPlaylist[fPos++] = buf;
+ } else {
+ serializedPlaylist[fPos++] = '#';
+ }
+ }
+ if (serializedPlaylist[fPos-1] == '#') { // Remove trailing delimiter if set
+ serializedPlaylist[fPos-1] = '\0';
+ }
+ } else {
+ return files;
+ }
+ }
+
snprintf(Log_Buffer, Log_BufferLength, "%s: %u", (char *) FPSTR(freeMemory), ESP.getFreeHeap());
Log_Println(Log_Buffer, LOGLEVEL_DEBUG);
@@ -151,8 +183,8 @@ char **SdCard_ReturnPlaylist(const char *fileName, const uint32_t _playMode) {
Log_Println(Log_Buffer, LOGLEVEL_DEBUG);
}
- // Don't read from cachefile (if cachefile doesn't exist, playmode doesn't fit or caching isn't desired)
- if (!readFromCacheFile) {
+ // Don't read from cachefile or m3u-file. Means: read filenames from SD and make playlist of it
+ if (!readFromCacheFile && !enablePlaylistFromM3u) {
Log_Println((char *) FPSTR(playlistGenModeUncached), LOGLEVEL_NOTICE);
// File-mode
if (!fileOrDirectory.isDirectory()) {
@@ -173,7 +205,7 @@ char **SdCard_ReturnPlaylist(const char *fileName, const uint32_t _playMode) {
return ++files;
}
- // Directory-mode
+ // Directory-mode (linear-playlist)
uint16_t allocCount = 1;
uint16_t allocSize = 4096;
if (psramInit()) {
diff --git a/src/Wlan.cpp b/src/Wlan.cpp
index 8cd5df1..e7382c6 100644
--- a/src/Wlan.cpp
+++ b/src/Wlan.cpp
@@ -164,7 +164,7 @@ void writeWifiStatusToNVS(bool wifiStatus) {
if (!wifiStatus) {
if (gPrefsSettings.putUInt("enableWifi", 0)) { // disable
Log_Println((char *) FPSTR(wifiDisabledAfterRestart), LOGLEVEL_NOTICE);
- if (gPlayProperties.playMode == WEBSTREAM) {
+ if (gPlayProperties.playMode == WEBSTREAM || gPlayProperties.playMode == WEBSTREAMS_LOCAL_M3U) {
AudioPlayer_TrackControlToQueueSender(STOP);
}
delay(300);
diff --git a/src/logmessages.h b/src/logmessages.h
index 883e31d..bd8cdee 100644
--- a/src/logmessages.h
+++ b/src/logmessages.h
@@ -68,6 +68,7 @@ extern const char modeAllTrackRandom[];
extern const char modeAllTrackAlphSortedLoop[];
extern const char modeAllTrackRandomLoop[];
extern const char modeWebstream[];
+extern const char modeWebstreamM3u[];
extern const char webstreamNotAvailable[];
extern const char modeDoesNotExist[];
extern const char modeRepeatNone[];
diff --git a/src/revision.h b/src/revision.h
index 76fe93f..8996247 100644
--- a/src/revision.h
+++ b/src/revision.h
@@ -1,4 +1,4 @@
#ifndef __REVISION_H__
#define __REVISION_H__
- constexpr const char softwareRevision[] PROGMEM = "Software-revision: 20210716-2";
+ constexpr const char softwareRevision[] PROGMEM = "Software-revision: 20210720-1";
#endif
\ No newline at end of file
diff --git a/src/values.h b/src/values.h
index 591acbb..bb84985 100644
--- a/src/values.h
+++ b/src/values.h
@@ -25,6 +25,7 @@
#define ALL_TRACKS_OF_DIR_SORTED_LOOP 7 // Play all files of a directory (alph. sorted) in infinite-loop
#define ALL_TRACKS_OF_DIR_RANDOM_LOOP 9 // Play all files of a directory (randomized) in infinite-loop
#define WEBSTREAM 8 // Play webradio-stream
+ #define WEBSTREAMS_LOCAL_M3U 11 // Plays webreadio-streams with addresses from a local m3u-file
#define BUSY 10 // Used if playlist is created