Browse Source

Allowing mixed .m3u files (webstreams+files)

master
Torsten Stauder 4 years ago
parent
commit
9d0957d815
  1. 4
      README.md
  2. 2
      changelog.md
  3. 2
      html/management_DE.html
  4. 2
      html/management_EN.html
  5. 38
      src/AudioPlayer.cpp
  6. 1
      src/AudioPlayer.h
  7. 2
      src/HTMLmanagement_DE.h
  8. 2
      src/HTMLmanagement_EN.h
  9. 2
      src/Led.cpp
  10. 4
      src/SdCard.cpp
  11. 2
      src/Wlan.cpp
  12. 2
      src/revision.h
  13. 2
      src/values.h

4
README.md

@ -13,7 +13,7 @@ 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). * 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 ## Changelog
Last three events: Last three events:
* 20.07.2021: Adding new playmode: multiple webradio-paylist from local .m3u-file
* 23.07.2021: Adding new playmode: from local .m3u-file (files or webstreams)
* 13.07.2021: Adding OTA-support via webGUI * 13.07.2021: Adding OTA-support via webGUI
* 09.07.2021: Making branch `refactoring` the new master * 09.07.2021: Making branch `refactoring` the new master
## Known bugs ## Known bugs
@ -278,7 +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 (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 * `folder/playlist (random order)` => plays all tracks in random order from a folder forever
* `webradio` => always only one "track": plays a webstream * `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
* `list (files from SD and/or webstreams) from local .m3u-File` => can be one or more files / webradio-stations with local .m3u as sourcefile
### Modification RFID-tags ### 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: 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:

2
changelog.md

@ -9,7 +9,7 @@
* 09.07.2021: Making branch `refactoring` the the master * 09.07.2021: Making branch `refactoring` the the master
* 09.07.2021: Making master the new branch `old` (not maintained any longer!) * 09.07.2021: Making master the new branch `old` (not maintained any longer!)
* 13.07.2021: Adding OTA-support via webGUI * 13.07.2021: Adding OTA-support via webGUI
* 20.07.2021: Adding new playmode: multiple webradio-paylist from local .m3u-file
* 23.07.2021: Adding new playmode: from local .m3u-file (files or webstreams)
## Old (monolithic main.cpp) ## Old (monolithic main.cpp)
* 11.07.2020: Added support for reversed Neopixel addressing. * 11.07.2020: Added support for reversed Neopixel addressing.

2
html/management_DE.html

@ -272,7 +272,7 @@
<option class="option-folder" value="7">Alle Titel eines Verzeichnis (sortiert, Endlosschleife)</option> <option class="option-folder" value="7">Alle Titel eines Verzeichnis (sortiert, Endlosschleife)</option>
<option class="option-folder" value="9">Alle Titel eines Verzeichnis (zufällig, Endlosschleife)</option> <option class="option-folder" value="9">Alle Titel eines Verzeichnis (zufällig, Endlosschleife)</option>
<option class="option-stream" value="8">Webradio</option> <option class="option-stream" value="8">Webradio</option>
<option class="option-stream" value="11">Webradioliste aus lokaler .m3u-Datei</option>
<option class="option-stream" value="11">Liste (Dateien von SD und/oder Webstreams) aus lokaler .m3u-Datei</option>
</select> </select>
</div> </div>
<div class="tab-pane " id="rfidmod" role="tabpanel"> <div class="tab-pane " id="rfidmod" role="tabpanel">

2
html/management_EN.html

@ -272,7 +272,7 @@
<option class="option-folder" value="7">All tracks of a directory (sorted alph., loop)</option> <option class="option-folder" value="7">All tracks of a directory (sorted alph., loop)</option>
<option class="option-folder" value="9">All tracks of a directory (random, loop)</option> <option class="option-folder" value="9">All tracks of a directory (random, loop)</option>
<option class="option-stream" value="8">Webradio</option> <option class="option-stream" value="8">Webradio</option>
<option class="option-stream" value="11">Webradiolist from local .m3u-File</option>
<option class="option-stream" value="11">List (files from SD and/or webstreams) from local .m3u-File</option>
</select> </select>
</div> </div>
<div class="tab-pane " id="rfidmod" role="tabpanel"> <div class="tab-pane " id="rfidmod" role="tabpanel">

38
src/AudioPlayer.cpp

@ -553,10 +553,18 @@ void AudioPlayer_Task(void *parameter) {
} }
} }
if (gPlayProperties.playMode == WEBSTREAM || gPlayProperties.playMode == WEBSTREAMS_LOCAL_M3U) { // Webstream
audio->connecttohost(*(gPlayProperties.playlist + gPlayProperties.currentTrackNumber));
gPlayProperties.playlistFinished = false;
if (!strncmp("http", *(gPlayProperties.playlist + gPlayProperties.currentTrackNumber), 4)) {
gPlayProperties.isWebstream = true;
} else { } else {
gPlayProperties.isWebstream = false;
}
gPlayProperties.currentRelPos = 0;
audioReturnCode = false;
if (gPlayProperties.playMode == WEBSTREAM || (gPlayProperties.playMode == LOCAL_M3U && gPlayProperties.isWebstream)) { // Webstream
audioReturnCode = audio->connecttohost(*(gPlayProperties.playlist + gPlayProperties.currentTrackNumber));
gPlayProperties.playlistFinished = false;
} else if (gPlayProperties.playMode != WEBSTREAM && !gPlayProperties.isWebstream) {
// Files from SD // Files from SD
if (!gFSystem.exists(*(gPlayProperties.playlist + gPlayProperties.currentTrackNumber))) { // Check first if file/folder exists if (!gFSystem.exists(*(gPlayProperties.playlist + gPlayProperties.currentTrackNumber))) { // Check first if file/folder exists
snprintf(Log_Buffer, Log_BufferLength, "%s: %s", (char *) FPSTR(dirOrFileDoesNotExist), *(gPlayProperties.playlist + gPlayProperties.currentTrackNumber)); snprintf(Log_Buffer, Log_BufferLength, "%s: %s", (char *) FPSTR(dirOrFileDoesNotExist), *(gPlayProperties.playlist + gPlayProperties.currentTrackNumber));
@ -566,23 +574,30 @@ void AudioPlayer_Task(void *parameter) {
} else { } else {
audioReturnCode = audio->connecttoFS(gFSystem, *(gPlayProperties.playlist + gPlayProperties.currentTrackNumber)); audioReturnCode = audio->connecttoFS(gFSystem, *(gPlayProperties.playlist + gPlayProperties.currentTrackNumber));
// consider track as finished, when audio lib call was not successful // consider track as finished, when audio lib call was not successful
}
}
if (!audioReturnCode) { if (!audioReturnCode) {
System_IndicateError(); System_IndicateError();
gPlayProperties.trackFinished = true; gPlayProperties.trackFinished = true;
continue; continue;
}
} else {
if (gPlayProperties.currentTrackNumber) {
Led_Indicate(LedIndicatorType::PlaylistProgress); Led_Indicate(LedIndicatorType::PlaylistProgress);
}
if (gPlayProperties.startAtFilePos > 0) { if (gPlayProperties.startAtFilePos > 0) {
audio->setFilePos(gPlayProperties.startAtFilePos); audio->setFilePos(gPlayProperties.startAtFilePos);
gPlayProperties.startAtFilePos = 0; gPlayProperties.startAtFilePos = 0;
snprintf(Log_Buffer, Log_BufferLength, "%s %u", (char *) FPSTR(trackStartatPos), audio->getFilePos()); snprintf(Log_Buffer, Log_BufferLength, "%s %u", (char *) FPSTR(trackStartatPos), audio->getFilePos());
Log_Println(Log_Buffer, LOGLEVEL_NOTICE); Log_Println(Log_Buffer, LOGLEVEL_NOTICE);
} }
if (!gPlayProperties.isWebstream) { // Is done via audio_showstation()
char buf[255]; char buf[255];
snprintf(buf, sizeof(buf) / sizeof(buf[0]), "(%d/%d) %s", (gPlayProperties.currentTrackNumber + 1), gPlayProperties.numberOfTracks, (const char *)*(gPlayProperties.playlist + gPlayProperties.currentTrackNumber)); snprintf(buf, sizeof(buf) / sizeof(buf[0]), "(%d/%d) %s", (gPlayProperties.currentTrackNumber + 1), gPlayProperties.numberOfTracks, (const char *)*(gPlayProperties.playlist + gPlayProperties.currentTrackNumber));
#ifdef MQTT_ENABLE #ifdef MQTT_ENABLE
publishMqtt((char *) FPSTR(topicTrackState), buf, false); publishMqtt((char *) FPSTR(topicTrackState), buf, false);
#endif #endif
}
#if (LANGUAGE == DE) #if (LANGUAGE == DE)
snprintf(Log_Buffer, Log_BufferLength, "'%s' wird abgespielt (%d von %d)", *(gPlayProperties.playlist + gPlayProperties.currentTrackNumber), (gPlayProperties.currentTrackNumber + 1), gPlayProperties.numberOfTracks); snprintf(Log_Buffer, Log_BufferLength, "'%s' wird abgespielt (%d von %d)", *(gPlayProperties.playlist + gPlayProperties.currentTrackNumber), (gPlayProperties.currentTrackNumber + 1), gPlayProperties.numberOfTracks);
#else #else
@ -592,27 +607,28 @@ void AudioPlayer_Task(void *parameter) {
gPlayProperties.playlistFinished = false; gPlayProperties.playlistFinished = false;
} }
} }
}
// Handle seekmodes // Handle seekmodes
if (gPlayProperties.seekmode != SEEK_NORMAL) { if (gPlayProperties.seekmode != SEEK_NORMAL) {
if (gPlayProperties.seekmode == SEEK_FORWARDS) { if (gPlayProperties.seekmode == SEEK_FORWARDS) {
if (audio->setTimeOffset(jumpOffset)) { if (audio->setTimeOffset(jumpOffset)) {
#if (LANGUAGE == DE) #if (LANGUAGE == DE)
Serial.printf("%d Sekunden nach vorne gesprungen\n", jumpOffset);
snprintf(Log_Buffer, Log_BufferLength, "%d Sekunden nach vorne gesprungen", jumpOffset);
#else #else
Serial.printf("Jumped %d seconds forwards\n", jumpOffset);
snprintf(Log_Buffer, Log_BufferLength, "Jumped %d seconds forwards", jumpOffset);
#endif #endif
Log_Println(Log_Buffer, LOGLEVEL_NOTICE);
} else { } else {
System_IndicateError(); System_IndicateError();
} }
} else if (gPlayProperties.seekmode == SEEK_BACKWARDS) { } else if (gPlayProperties.seekmode == SEEK_BACKWARDS) {
if (audio->setTimeOffset(-(jumpOffset))) { if (audio->setTimeOffset(-(jumpOffset))) {
#if (LANGUAGE == DE) #if (LANGUAGE == DE)
Serial.printf("%d Sekunden zurueck gesprungen\n", jumpOffset);
snprintf(Log_Buffer, Log_BufferLength, "%d Sekunden zurueck gesprungen", jumpOffset);
#else #else
Serial.printf("Jumped %d seconds backwards\n", jumpOffset);
snprintf(Log_Buffer, Log_BufferLength, "Jumped %d seconds backwards", jumpOffset);
#endif #endif
Log_Println(Log_Buffer, LOGLEVEL_NOTICE);
} else { } else {
System_IndicateError(); System_IndicateError();
} }
@ -634,7 +650,7 @@ void AudioPlayer_Task(void *parameter) {
} }
// Calculate relative position in file (for neopixel) for SD-card-mode // Calculate relative position in file (for neopixel) for SD-card-mode
if (!gPlayProperties.playlistFinished && gPlayProperties.playMode != WEBSTREAM) {
if (!gPlayProperties.playlistFinished && !gPlayProperties.isWebstream) {
double fp = (double)audio->getFilePos() / (double)audio->getFileSize(); double fp = (double)audio->getFilePos() / (double)audio->getFileSize();
if (millis() % 100 == 0) { if (millis() % 100 == 0) {
gPlayProperties.currentRelPos = fp * 100; gPlayProperties.currentRelPos = fp * 100;
@ -866,7 +882,7 @@ void AudioPlayer_TrackQueueDispatcher(const char *_itemToPlay, const uint32_t _l
break; break;
} }
case WEBSTREAMS_LOCAL_M3U: { // This is always just one "track"
case LOCAL_M3U: { // Can be one or more webradio-station(s)
Log_Println((char *) FPSTR(modeWebstreamM3u), LOGLEVEL_NOTICE); Log_Println((char *) FPSTR(modeWebstreamM3u), LOGLEVEL_NOTICE);
if (Wlan_IsConnected()) { if (Wlan_IsConnected()) {
xQueueSend(gTrackQueue, &(musicFiles), 0); xQueueSend(gTrackQueue, &(musicFiles), 0);

1
src/AudioPlayer.h

@ -20,6 +20,7 @@ typedef struct { // Bit field
uint8_t seekmode: 2; // If seekmode is active and if yes: forward or backwards? uint8_t seekmode: 2; // If seekmode is active and if yes: forward or backwards?
bool newPlayMono: 1; // true if mono; false if stereo (helper) bool newPlayMono: 1; // true if mono; false if stereo (helper)
bool currentPlayMono: 1; // true if mono; false if stereo bool currentPlayMono: 1; // true if mono; false if stereo
bool isWebstream: 1; // Indicates if track currenty played is a webstream
} playProps; } playProps;
extern playProps gPlayProperties; extern playProps gPlayProperties;

2
src/HTMLmanagement_DE.h

@ -272,7 +272,7 @@ static const char management_HTML[] PROGMEM = "<!DOCTYPE html>\
<option class=\"option-folder\" value=\"7\">Alle Titel eines Verzeichnis (sortiert, Endlosschleife)</option>\ <option class=\"option-folder\" value=\"7\">Alle Titel eines Verzeichnis (sortiert, Endlosschleife)</option>\
<option class=\"option-folder\" value=\"9\">Alle Titel eines Verzeichnis (zufällig, Endlosschleife)</option>\ <option class=\"option-folder\" value=\"9\">Alle Titel eines Verzeichnis (zufällig, Endlosschleife)</option>\
<option class=\"option-stream\" value=\"8\">Webradio</option>\ <option class=\"option-stream\" value=\"8\">Webradio</option>\
<option class=\"option-stream\" value=\"11\">Webradioliste aus lokaler .m3u-Datei</option>\
<option class=\"option-stream\" value=\"11\">Liste (Dateien von SD und/oder Webstreams) aus lokaler .m3u-Datei</option>\
</select>\ </select>\
</div>\ </div>\
<div class=\"tab-pane \" id=\"rfidmod\" role=\"tabpanel\">\ <div class=\"tab-pane \" id=\"rfidmod\" role=\"tabpanel\">\

2
src/HTMLmanagement_EN.h

@ -272,7 +272,7 @@ static const char management_HTML[] PROGMEM = "<!DOCTYPE html>\
<option class=\"option-folder\" value=\"7\">All tracks of a directory (sorted alph., loop)</option>\ <option class=\"option-folder\" value=\"7\">All tracks of a directory (sorted alph., loop)</option>\
<option class=\"option-folder\" value=\"9\">All tracks of a directory (random, loop)</option>\ <option class=\"option-folder\" value=\"9\">All tracks of a directory (random, loop)</option>\
<option class=\"option-stream\" value=\"8\">Webradio</option>\ <option class=\"option-stream\" value=\"8\">Webradio</option>\
<option class=\"option-stream\" value=\"11\">Webradiolist from local .m3u-File</option>\
<option class=\"option-stream\" value=\"11\">List (files from SD and/or webstreams) from local .m3u-File</option>\
</select>\ </select>\
</div>\ </div>\
<div class=\"tab-pane \" id=\"rfidmod\" role=\"tabpanel\">\ <div class=\"tab-pane \" id=\"rfidmod\" role=\"tabpanel\">\

2
src/Led.cpp

@ -467,7 +467,7 @@ static void Led_Task(void *parameter) {
redrawProgress = true; redrawProgress = true;
} }
if (gPlayProperties.playMode != WEBSTREAM && gPlayProperties.playMode != WEBSTREAMS_LOCAL_M3U) {
if (!gPlayProperties.isWebstream) {
if (gPlayProperties.currentRelPos != lastPos || redrawProgress) { if (gPlayProperties.currentRelPos != lastPos || redrawProgress) {
redrawProgress = false; redrawProgress = false;
lastPos = gPlayProperties.currentRelPos; lastPos = gPlayProperties.currentRelPos;

4
src/SdCard.cpp

@ -154,7 +154,7 @@ char **SdCard_ReturnPlaylist(const char *fileName, const uint32_t _playMode) {
} }
// Parse m3u-playlist and create linear-playlist out of it // Parse m3u-playlist and create linear-playlist out of it
if (_playMode == WEBSTREAMS_LOCAL_M3U) {
if (_playMode == LOCAL_M3U) {
if (fileOrDirectory && !fileOrDirectory.isDirectory() && fileOrDirectory.size() >= 0) { if (fileOrDirectory && !fileOrDirectory.isDirectory() && fileOrDirectory.size() >= 0) {
enablePlaylistFromM3u = true; enablePlaylistFromM3u = true;
uint16_t allocCount = 1; uint16_t allocCount = 1;
@ -170,7 +170,7 @@ char **SdCard_ReturnPlaylist(const char *fileName, const uint32_t _playMode) {
return files; return files;
} }
char buf; char buf;
char lastBuf = '0';
char lastBuf = '#';
uint32_t fPos = 1; uint32_t fPos = 1;
serializedPlaylist[0] = '#'; serializedPlaylist[0] = '#';

2
src/Wlan.cpp

@ -164,7 +164,7 @@ void writeWifiStatusToNVS(bool wifiStatus) {
if (!wifiStatus) { if (!wifiStatus) {
if (gPrefsSettings.putUInt("enableWifi", 0)) { // disable if (gPrefsSettings.putUInt("enableWifi", 0)) { // disable
Log_Println((char *) FPSTR(wifiDisabledAfterRestart), LOGLEVEL_NOTICE); Log_Println((char *) FPSTR(wifiDisabledAfterRestart), LOGLEVEL_NOTICE);
if (gPlayProperties.playMode == WEBSTREAM || gPlayProperties.playMode == WEBSTREAMS_LOCAL_M3U) {
if (gPlayProperties.isWebstream) {
AudioPlayer_TrackControlToQueueSender(STOP); AudioPlayer_TrackControlToQueueSender(STOP);
} }
delay(300); delay(300);

2
src/revision.h

@ -1,4 +1,4 @@
#ifndef __REVISION_H__ #ifndef __REVISION_H__
#define __REVISION_H__ #define __REVISION_H__
constexpr const char softwareRevision[] PROGMEM = "Software-revision: 20210720-2";
constexpr const char softwareRevision[] PROGMEM = "Software-revision: 20210723-1";
#endif #endif

2
src/values.h

@ -25,7 +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_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 ALL_TRACKS_OF_DIR_RANDOM_LOOP 9 // Play all files of a directory (randomized) in infinite-loop
#define WEBSTREAM 8 // Play webradio-stream #define WEBSTREAM 8 // Play webradio-stream
#define WEBSTREAMS_LOCAL_M3U 11 // Plays webreadio-streams with addresses from a local m3u-file
#define LOCAL_M3U 11 // Plays items (webstream or files) with addresses/paths from a local m3u-file
#define BUSY 10 // Used if playlist is created #define BUSY 10 // Used if playlist is created

Loading…
Cancel
Save