Playmodus für Ordner mit mehreren Folgen (z. B. Drei ? Kids)

Hallo zusammen,

ich habe mir eine ESPuino-Box gebaut, die soweit auch super läuft.
Jetzt möchte ich für meinen Sohn einen speziellen Ordner anlegen, in dem die Drei ??? Kids-Folgen liegen. Meine Idee:

  • Hauptordner: Drei ??? Kids

  • Darin jeweils ein Unterordner pro Folge

  • Jede Folge besteht aus mehreren MP3-Dateien

Am liebsten wäre mir folgender Ablauf:
Wenn mein Sohn die Drei ??? Kids-Karte auflegt, startet der Ordner. Durch langes Drücken der Vorwärts- oder Rückwärtstaste soll er aber direkt zum nächsten oder vorherigen Folgen-Ordner springen können – also nicht nur ein Track weiter, sondern gleich ein ganzer Ordner.

Meine Frage:
Gibt es so einen Playmodus schon? Oder lässt sich das mit Bordmitteln vom ESPuino leicht realisieren?

Danke schon mal für eure Tipps!

Nein.

Man könnte mit M3U-Playlisten arbeiten, das geht aber nur auf Datei/Webradio-Ebene. Ansonsten haben wir das noch: Playmodus: Zufälligen Unterordner aus Ordner abspielen.

Vielen Dank für die schnelle Antwort!
Ich werd mir das mal genauer anschauen. Klingt auf jeden Fall interessant.

Meinst du, es wäre sehr aufwändig, so etwas umzusetzen, dass man nicht zufällig, sondern gezielt mit langem Druck der Vor-/Zurück-Tasten in den nächsten bzw. vorherigen Unterordner springen kann?

Ist wahrscheinlich gar nicht so fürchterlich viel Arbeit.

Eine Playlist, die in std::vector<char*> lebt, sähe rekursiv dann z. B. so aus:

"/mp3_1/titel1",
"/mp3_1/titel2",
"/mp3_1/titel3",
"/mp3_2/titel1",
"/mp3_2/titel2",
"/mp3_2/titel3"

Nehmen wir an, es wird der zweite Titel (/mp3_1/titel2) abgespielt (Index 1), dann müsste man, um in den nächsten Ordner zu kommen, auf den vierten Titel (/mp3_2/titel1) springen (Index 3). Man müsste also schauen, wann sich vorne der Basepath ändert. Dort, wo er sich ändert, das ist der Titel, den wir gerne hätten. Da wollen wir hinspringen. Vorausgesetzt aber immer, dass das Ganze alphabetisch sortiert ist. Das ist Grundvoraussetzung!

Ich habe chatgpt damit mal ein bisschen gefüttert, um ein lauffähiges Beispiel zu kriegen:

#include <iostream>
#include <vector>
#include <string>

int findNextDirectoryTrack(const std::vector<char*>& paths, size_t currentIndex) {
    // Überprüfen, ob der aktuelle Index gültig ist
    if (currentIndex >= paths.size()) {
        return -1; // Ungültiger Index
    }

    // Extrahiere das Verzeichnis des aktuellen Titels
    const char* currentPath = paths[currentIndex];
    size_t lastSlashPos = std::string(currentPath).find_last_of('/');
    std::string currentDirectory = std::string(currentPath).substr(0, lastSlashPos);

    // Iteriere ab dem nächsten Index
    for (size_t i = currentIndex + 1; i < paths.size(); ++i) {
        const char* path = paths[i];
        if (path != nullptr) {
            std::string strPath(path);
            // Überprüfen, ob das Verzeichnis sich ändert
            if (strPath.find(currentDirectory) == std::string::npos) {
                return i; // Rückgabe des Index des nächsten Titels im anderen Verzeichnis
            }
        }
    }

    // Wenn kein nächster Titel gefunden wurde, gib -1 zurück
    return -1;
}

int main() {
    std::vector<char*> paths = {
        "/mp3_1/titel1",
        "/mp3_1/titel2",
        "/mp3_1/titel3",
        "/mp3_2/titel1",
        "/mp3_2/titel2",
        "/mp3_2/titel3"
    };

    size_t currentIndex = 1; // Aktueller Titel (Index)
    int nextTrackIndex = findNextDirectoryTrack(paths, currentIndex);

    if (nextTrackIndex != -1) {
        std::cout << "Nächster Titel Index: " << nextTrackIndex << std::endl;
    } else {
        std::cout << "Kein nächster Titel gefunden." << std::endl;
    }

    return 0;
}


Lösungsansatz:

  1. Es wird ein neuer Playmode gebraucht, dessen Playlist alphabetisch sortiert ist.
  2. Die Playlist muss jedoch rekursiv generiert werden. Ich glaube allerdings, dass man das in Rekursionstiefe begrenzen muss, so dass das nicht im Desaster endet.
  3. Man braucht die o.g. Methode (oder was Ähnliches). In die steckt man die Playlist und die aktuelle Abspielposition rein. Gesucht wird dann ab dem Element danach. Die Methode gibt die Indexnummer oder (im Fehlerfalle) -1 zurück.
  4. Das kann man dann hier einbetten: ESPuino/src/AudioPlayer.cpp at efeb042a3e9ac2514030bc80f61a3938bbc04b7c · biologist79/ESPuino · GitHub. Dort braucht man eine Sonderbehandlung für den neuen Playmode, so dass LASTTRACK zu einem Sprung an die gewünschte Stelle führt. Kann nicht gesprungen werden, so leuchten kurz alle LEDs auf und es wird nicht gesprungen.

Mir fällt aber gerade noch ein, dass das natürlich auch mit Springen rückwärts gehen muss. Da habe ich mit chatgpt jetzt mehrere Iterationen gebraucht, da es sich verrannt hatte. Aber nachdem ich eine Strategie beschrieben habe, kam das raus:

#include <iostream>
#include <vector>
#include <string>

enum class Direction {
    Forward,
    Backward
};

int findNextDirectoryTrack(const std::vector<char*>& paths, size_t currentIndex, Direction direction) {
    // Überprüfen, ob der aktuelle Index gültig ist
    if (currentIndex >= paths.size()) {
        return -1; // Ungültiger Index
    }

    // Extrahiere das Verzeichnis des aktuellen Titels
    const char* currentPath = paths[currentIndex];
    size_t lastSlashPos = std::string(currentPath).find_last_of('/');
    std::string currentDirectory = std::string(currentPath).substr(0, lastSlashPos);

    if (direction == Direction::Forward) {
        // Iteriere ab dem nächsten Index
        for (size_t i = currentIndex + 1; i < paths.size(); ++i) {
            const char* path = paths[i];
            if (path != nullptr) {
                std::string strPath(path);
                // Überprüfen, ob das Verzeichnis sich ändert
                if (strPath.find(currentDirectory) == std::string::npos) {
                    return i; // Rückgabe des Index des nächsten Titels im anderen Verzeichnis
                }
            }
        }
    } else if (direction == Direction::Backward) {
        std::string previousDirectory;
        // Iteriere rückwärts
        for (size_t i = currentIndex; i-- > 0;) {
            const char* path = paths[i];
            if (path != nullptr) {
                std::string strPath(path);
                // Überprüfen, ob das Verzeichnis sich ändert
                if (strPath.find(currentDirectory) == std::string::npos) {
                    // Wenn wir das erste andere Verzeichnis gefunden haben, merken wir uns diesen Titel
                    previousDirectory = strPath.substr(0, strPath.find_last_of('/'));
                } else if (!previousDirectory.empty() && strPath.find(previousDirectory) != std::string::npos) {
                    // Wenn wir wieder in das vorherige Verzeichnis gelangen, geben wir den Index zurück
                    return i; // Rückgabe des Index des vorherigen Titels im anderen Verzeichnis
                }
            }
        }
        // Wenn wir bei 0 angekommen sind, geben wir 0 zurück
        return 0; // Immer zum ersten Titel zurückspringen
    }

    // Wenn kein nächster Titel gefunden wurde, gib -1 zurück
    return -1;
}

int main() {
    std::vector<char*> paths = {
        "/mp3_1/titel1",
        "/mp3_1/titel2",
        "/mp3_1/titel3",
        "/mp3_2/titel1",
        "/mp3_2/titel2",
        "/mp3_2/titel3"
    };

    size_t currentIndex = 4; // Aktueller Titel (Index für "/mp3_2/titel2")
    
    // Vorwärts springen
    int nextTrackIndexForward = findNextDirectoryTrack(paths, currentIndex, Direction::Forward);
    if (nextTrackIndexForward != -1) {
        std::cout << "Nächster Titel Index (vorwärts): " << nextTrackIndexForward << std::endl;
    } else {
        std::cout << "Kein nächster Titel gefunden (vorwärts)." << std::endl;
    }

    // Rückwärts springen
    int nextTrackIndexBackward = findNextDirectoryTrack(paths, currentIndex, Direction::Backward);
    if (nextTrackIndexBackward != -1) {
        std::cout << "Nächster Titel Index (rückwärts): " << nextTrackIndexBackward << std::endl;
    } else {
        std::cout << "Kein nächster Titel gefunden (rückwärts)." << std::endl;
    }

    return 0;
}
  1. Bei FIRSTTRACK muss man entsprechend rückwärts springen.

Problem mit dem Code-Snippet ist jedoch, dass es rückwärts immer auf 0 springt. Ich habe mal neun Elemente eingefügt und unten eine redimentäre for-Schleife implementiert:

#include <iostream>
#include <string>
#include <vector>

enum class Direction { Forward, Backward };

int findNextDirectoryTrack(const std::vector<char *> &paths,
                           size_t currentIndex, Direction direction) {
  // Überprüfen, ob der aktuelle Index gültig ist
  if (currentIndex >= paths.size()) {
    return -1; // Ungültiger Index
  }

  // Extrahiere das Verzeichnis des aktuellen Titels
  const char *currentPath = paths[currentIndex];
  size_t lastSlashPos = std::string(currentPath).find_last_of('/');
  std::string currentDirectory =
      std::string(currentPath).substr(0, lastSlashPos);

  if (direction == Direction::Forward) {
    // Iteriere ab dem nächsten Index
    for (size_t i = currentIndex + 1; i < paths.size(); ++i) {
      const char *path = paths[i];
      if (path != nullptr) {
        std::string strPath(path);
        // Überprüfen, ob das Verzeichnis sich ändert
        if (strPath.find(currentDirectory) == std::string::npos) {
          return i; // Rückgabe des Index des nächsten Titels im anderen
                    // Verzeichnis
        }
      }
    }
  } else if (direction == Direction::Backward) {
    std::string previousDirectory;
    // Iteriere rückwärts
    for (size_t i = currentIndex; i-- > 0;) {
      const char *path = paths[i];
      if (path != nullptr) {
        std::string strPath(path);
        // Überprüfen, ob das Verzeichnis sich ändert
        if (strPath.find(currentDirectory) == std::string::npos) {
          // Wenn wir das erste andere Verzeichnis gefunden haben, merken wir
          // uns diesen Titel
          previousDirectory = strPath.substr(0, strPath.find_last_of('/'));
        } else if (!previousDirectory.empty() &&
                   strPath.find(previousDirectory) != std::string::npos) {
          // Wenn wir wieder in das vorherige Verzeichnis gelangen, geben wir
          // den Index zurück
          return i; // Rückgabe des Index des vorherigen Titels im anderen
                    // Verzeichnis
        }
      }
    }
    // Wenn wir bei 0 angekommen sind, geben wir 0 zurück
    return 0; // Immer zum ersten Titel zurückspringen
  }

  // Wenn kein nächster Titel gefunden wurde, gib -1 zurück
  return -1;
}

int main() {
  std::vector<char *> paths = {
      "/mp3_0/titel1", "/mp3_0/titel2", "/mp3_0/titel3",
      "/mp3_1/titel1", "/mp3_1/titel2", "/mp3_1/titel3",
      "/mp3_2/titel1", "/mp3_2/titel2", "/mp3_2/titel3"};

  for (int i = 0; i < 9; i++) {
    size_t currentIndex = i; // Aktueller Titel (Index für "/mp3_2/titel2")

    // Vorwärts springen
    int nextTrackIndexForward =
        findNextDirectoryTrack(paths, currentIndex, Direction::Forward);
    if (nextTrackIndexForward != -1) {
      std::cout << i
                << " Nächster Titel Index (vorwärts): " << nextTrackIndexForward
                << std::endl;
    } else {
      std::cout << i << " Kein nächster Titel gefunden (vorwärts)."
                << std::endl;
    }

    // Rückwärts springen
    int nextTrackIndexBackward =
        findNextDirectoryTrack(paths, currentIndex, Direction::Backward);
    if (nextTrackIndexBackward != -1) {
      std::cout << i << " Nächster Titel Index (rückwärts): "
                << nextTrackIndexBackward << std::endl;
    } else {
      std::cout << i << " Kein nächster Titel gefunden (rückwärts)."
                << std::endl;
    }
  }
  return 0;
}

Die Ausgabe ist:

0 Nächster Titel Index (vorwärts): 3
0 Nächster Titel Index (rückwärts): 0
1 Nächster Titel Index (vorwärts): 3
1 Nächster Titel Index (rückwärts): 0
2 Nächster Titel Index (vorwärts): 3
2 Nächster Titel Index (rückwärts): 0
3 Nächster Titel Index (vorwärts): 6
3 Nächster Titel Index (rückwärts): 0
4 Nächster Titel Index (vorwärts): 6
4 Nächster Titel Index (rückwärts): 0
5 Nächster Titel Index (vorwärts): 6
5 Nächster Titel Index (rückwärts): 0
6 Kein nächster Titel gefunden (vorwärts).
6 Nächster Titel Index (rückwärts): 0
7 Kein nächster Titel gefunden (vorwärts).
7 Nächster Titel Index (rückwärts): 0
8 Kein nächster Titel gefunden (vorwärts).
8 Nächster Titel Index (rückwärts): 0

Ich habe jetzt heute keinen Nerv mehr, drüber nachzudenken :slight_smile:
Also vorwärts passt alles. Aber rückwärts halt (noch) nicht. Erwarten würde man (ich)
0+1+2 => 0 (aktuelles Hörspiel beginnt von vorne)
3+4+5 => 0 (Sprung auf voriges Hörspiel)
6+7+8: => 3 (Sprung auf voriges Hörspiel)

Ok, also hiermit scheint es zu klappen. Das habe ich jetzt selbst erweitert, da ich es ChatGPT nicht mehr aus den Rippen leiern konnte:

#include <iostream>
#include <string>
#include <vector>

enum class Direction { Forward, Backward };

int findNextDirectoryTrack(const std::vector<char *> &paths,
                           size_t currentIndex, Direction direction) {
  // Überprüfen, ob der aktuelle Index gültig ist
  if (currentIndex >= paths.size()) {
    return -1; // Ungültiger Index
  }

  // Extrahiere das Verzeichnis des aktuellen Titels
  const char *currentPath = paths[currentIndex];
  size_t lastSlashPos = std::string(currentPath).find_last_of('/');
  std::string currentDirectory =
      std::string(currentPath).substr(0, lastSlashPos);

  if (direction == Direction::Forward) {
    // Iteriere ab dem nächsten Index
    for (size_t i = currentIndex + 1; i < paths.size(); ++i) {
      const char *path = paths[i];
      if (path != nullptr) {
        std::string strPath(path);
        // Überprüfen, ob das Verzeichnis sich ändert
        if (strPath.find(currentDirectory) == std::string::npos) {
          return i; // Rückgabe des Index des nächsten Titels im anderen
                    // Verzeichnis
        }
      }
    }
  } else if (direction == Direction::Backward) {
    std::string previousDirectory;
    // Iteriere rückwärts, jedoch nicht, wenn wir schon bei 0 sind
    if (!currentIndex) {
      return currentIndex;
    }
    for (size_t i = currentIndex; --i > 0;) {
      const char *path = paths[i];
      if (path != nullptr) {
        std::string strPath(path);
        // Überprüfen, ob das Verzeichnis sich zum ersten Mal ändert
        if (strPath.find(currentDirectory) == std::string::npos) {
          // Wenn wir das erste andere Verzeichnis gefunden haben, probieren wir
          // weiter, bis es sich erneut ändert oder der Index 0 wird
          previousDirectory = strPath.substr(0, strPath.find_last_of('/'));
          for (size_t j = i; --j > 0;) {
            const char *innerPath = paths[j];
            if (innerPath != nullptr) {
              std::string innerStrPath(innerPath);
              if (innerStrPath.find(previousDirectory) == std::string::npos) {
                // Wenn sich das Verzeichnis nochmal ändert, dann springe um 1
                // nach vorne. Dieses Element suchen wir!
                return j + 1;
              }
            }
          }
        } else if (!previousDirectory.empty() &&
                   strPath.find(previousDirectory) != std::string::npos) {
          // Wenn wir wieder in das vorherige Verzeichnis gelangen, geben wir
          // den Index zurück
          return i; // Rückgabe des Index des vorherigen Titels im anderen
                    // Verzeichnis
        }
      }
    }
    // Wenn wir bei 0 angekommen sind, geben wir 0 zurück
    return 0;
  }

  // Wenn kein nächster Titel gefunden wurde, gib -1 zurück
  return -1;
}

int main() {
  std::vector<char *> paths = {
      "/mp3_0/titel1", "/mp3_0/titel2", "/mp3_0/titel3", "/mp3_0/titel4",
      "/mp3_1/titel1", "/mp3_1/titel2", "/mp3_1/titel3", "/mp3_2/titel1",
      "/mp3_2/titel2", "/mp3_2/titel3", "/mp3_3/titel1", "/mp3_3/titel2",
      "/mp3_3/titel3"};

  for (int i = 0; i < paths.size(); i++) {
    size_t currentIndex = i; // Aktueller Titel (Index für "/mp3_2/titel2")

    // Vorwärts springen
    int nextTrackIndexForward =
        findNextDirectoryTrack(paths, currentIndex, Direction::Forward);
    if (nextTrackIndexForward != -1) {
      std::cout << i
                << " Nächster Titel Index (vorwärts): " << nextTrackIndexForward
                << std::endl;
    } else {
      std::cout << i << " Kein nächster Titel gefunden (vorwärts)."
                << std::endl;
    }

    // Rückwärts springen
    int nextTrackIndexBackward =
        findNextDirectoryTrack(paths, currentIndex, Direction::Backward);
    if (nextTrackIndexBackward != -1) {
      std::cout << i << " Nächster Titel Index (rückwärts): "
                << nextTrackIndexBackward << std::endl;
    } else {
      std::cout << i << " Kein nächster Titel gefunden (rückwärts)."
                << std::endl;
    }
  }
  return 0;
}

Ausgabe:

0 Nächster Titel Index (vorwärts): 3
0 Nächster Titel Index (rückwärts): 0
1 Nächster Titel Index (vorwärts): 3
1 Nächster Titel Index (rückwärts): 0
2 Nächster Titel Index (vorwärts): 3
2 Nächster Titel Index (rückwärts): 0
3 Nächster Titel Index (vorwärts): 6
3 Nächster Titel Index (rückwärts): 0
4 Nächster Titel Index (vorwärts): 6
4 Nächster Titel Index (rückwärts): 0
5 Nächster Titel Index (vorwärts): 6
5 Nächster Titel Index (rückwärts): 0
6 Nächster Titel Index (vorwärts): 9
6 Nächster Titel Index (rückwärts): 3
7 Nächster Titel Index (vorwärts): 9
7 Nächster Titel Index (rückwärts): 3
8 Nächster Titel Index (vorwärts): 9
8 Nächster Titel Index (rückwärts): 3
9 Kein nächster Titel gefunden (vorwärts).
9 Nächster Titel Index (rückwärts): 6
10 Kein nächster Titel gefunden (vorwärts).
10 Nächster Titel Index (rückwärts): 6
11 Kein nächster Titel gefunden (vorwärts).
11 Nächster Titel Index (rückwärts): 6

WOW ich bin absolut Baff. Cool das du gleich anfängst dir ws zu überlegen. Ich hab leider so überhaupt kein Wort verstanden was du da geschrieben hast :smiley:

Ich dachte es wär mega einfach ^^ ShortPress → NextTitel, LongPress → NextFolder, Ende :smiley: Aber deswegen bin ich Elektriker und kein Programmierer. :face_with_peeking_eye:

Ich muss zugeben, dass ich den Request eigentlich erst ablehnen wollte, da wir schon 13 Playlist-Modi haben. Ich muss aber zugeben, dass ich es für valide halte (und auch schon Bedarf hatte) für:

  1. Mehr als ein Hörspiel pro Karte
  2. Ein Hörspiel besteht aus mehreren Titeln, so dass man Sprungmarken hat
  3. Eine Form von Organisation, so dass die Hörspiele jeweils in einem eigenen Ordner leben

Ggf. wird man für das Ganze zwei Playlist-Modi brauchen: Einmal Hörspielmodus und einmal „normal“.

2 „Gefällt mir“

Ich habe das Feature soweit fertig, aber mir geht’s jetzt noch drum, wie man das Ganze final in Playmodi gießt. Kurz gesagt: Eigentlich steckt in der Rekursion mehr drin, als es hier bisher andiskutiert wurde. Daher würde ich das gerne etwas „höher aufhängen“ und gerne mal eure Meinung hören.

Würden wir jetzt auf der grünen Wiese starten, dann würde ich gar keine neuen Playmodi einführen sondern stattdessen einfach nur einen Konfigurationsparameter, bei dem man die Rekursionstiefe konfiguriert. Wobei 0 bedeutet, dass Unterordner nicht einbezogen werden und das würde ich standardmäßig auch so setzen. Dann wäre also alles wie jetzt, man könnte die Anzahl der Rekursionsebenen jedoch hochsetzen, wenn man das möchte.

Nun starten wir aber nicht auf der grünen Wiese und ich habe so ein bisschen Bedenken, dass es ESPuino-Usern die Hörspiele „zerhaut“, wenn sie den Rekursionsparameter unbedarft hochsetzen oder aber das Feature nutzen möchten, jedoch Bestand umkopieren/neu arrangieren müssten. Grob gesagt: Es gäbe keine Möglichkeit, den alten Kram zu lassen, wie er ist, jedoch das Feature in Zukunft zu nutzen.

Daher schlage ich jetzt Folgendes vor:

  • Es gibt drei neue Playmodi auf Verzeichnisebene mit Rekursion: sortiert, sortiert (Hörspiel) und unsortiert.
  • Statt der zuvor vorgeschlagenen Steuerung mittels FIRSTTRACK und LASTTRACK, gibt es zwei neue CMDs: NEXT_FOLDER, PREV_FOLDER. Das hätte den Vorteil, dass man die bisherige Steuermöglichkeit nicht verlöre. Im unsortierten Modus ist allerdings ein bisschen random, was dann jeweils passiert.
  • Es gibt einen Konfigurationsparameter im Webinterface, mit dem man die Rekursionstiefe einstellen kann. Diese wirkt nur auf die neuen Playmodi. Die würde ich per Default auf 1 setzen. Einstellbar wäre sie von 1 bis 3.

Dazu hätte ich gerne Meinungen gehört.

1 „Gefällt mir“

Klingt für mich echt super so :+1:
Eine Frage hab ich aber noch: Kannst du kurz erklären, wie genau die Rekursionstiefe (1–3) funktioniert und was das praktisch bedeutet?

Du hast einen Ordner, den wählst du aus, wenn du die Karte anlernst. Alle Dateien in diesem Ordner wären Rekursionstiefe 0. Nehmen wir an, da sind jetzt drei Ordner drin, die ihrerseits Dateien haben. Dann wären die Dateien in diesen Ordnern Rekursionstiefe 1. Naja und so könnte das dann tiefer gehen. Ein kleiner Baum also.

Ah ok jetzt hab ichs verstanden, klingt gut.

Wird noch ein bisschen dauern. Gibt noch ein paar Probleme. Mal schauen, wann ich das gefixt kriege.

Gut Ding will Weile haben :smiley:

So, ich habe das Feature endlich (hatte nur ab und zu Lust/Zeit dran zu arbeiten) soweit fertig, dass es getestet werden kann in einem neuen Branch:

Was nicht geht, das ist der rekursive Playmode als Hörbuch. @Joe91 oder @tueddy könnt ihr vielleicht mal einen Blick drauf werfen? Ich habe jetzt 3h gesucht und finde den Fehler einfach nicht. Also man kann eine Karte mit diesem Playmode anlernen und auch den Abspielvorgang starten. Jedoch bleibt der Led-Ring im IDLE-Mode und darüber hinaus lässt sich die Abspielposition nicht speichern, da der Playmode wohl offenbar wieder auf 0 gesetzt wird. Ich verstehe aber bislang nicht, wo oder wann das passiert. Initial ist er 16 (aka AUDIOBOOK_RECURSIVE).

Die Tiefe der Rekursion ist in den allgemeinen Einstellungen einstellbar (nicht in der settings.h) und wird standardmäßig auf 2 gesetzt. Die Tiefen 1 bis 4 sind im Webinterface einstellbar. Die Ordner-Sprungfunktionen kann man auf beliebige Tasten legen (z. B. via Webinterface).

Nochmal zur Funktionsweise:
Rekursionstiefe 0 heißt, dass keine Unterordner einbezogen werden in die Playlist-Generierung. Entsprechend ist Rekursionstiefe 1 der Einbezug von Unterordnern in der ersten Ebene. Und so geht’s dann halt entsprechend weiter mit mehr Rekursionstiefe.

Beispiel: Gegeben sei folgende Struktur

# Aktion Ergebnis
1 Man lernt eine Karte mit Rekursionstiefe 0 auf der Ebene root an Es werden gar keine Titel abgespielt, da sich in /root keine Titel befinden sondern nur Ordner befinden.
2 Man lernt eine Karte mit Rekursionstiefe 1 auf der Ebene root an. Benutzt wird alphabetische Sortierung. Aufgrund der Rekursionstiefe 1 werden die Unterordner Audiobook1, Audiobook2 und Audiobook3 einbezogen; Subaudiobook3 jedoch nicht.

Es wird eine Playlist mit sechs Titeln generiert und alphabetisch sortiert.

Wird ein Titel aus Audiobook1 abgespielt und man springt einen Ordner nach vorne, so wird als nächstes A2_Track01.mp3 abgespielt.

Wird ein Titel aus Audiobook2 abgespielt und man springt einen Ordner nach vorne, so wird als nächstes A3_Track01.mp3 abgespielt.

Wird ein Titel aus Audiobook3 abgespielt und man springt einen Ordner nach vorne, so wird eine Fehlermeldung angezeigt, dass es bereits der letzte Ordner ist.

Wird ein Titel aus Audiobook3 abgespielt und man springt einen Ordner zurück, so wird als nächstes A2_Track01.mp3 abgespielt.

Wird ein Titel aus Audiobook2 abgespielt und man springt einen Ordner zurück, so wird als nächstes A1_Track01.mp3 abgespielt.

Wird ein Titel aus Audiobook1 abgespielt und man springt einen Ordner zurück, so startet A1_Track01.mp3 von vorne (keine Fehlermeldung).
3 Man lernt eine Karte mit Rekursionstiefe 2 auf der Ebene root an. Benutzt wird alphabetische Sortierung. Aufgrund der Rekursionstiefe 2 werden alle Unterordner einbezogen.

Es wird eine Playlist mit acht Titeln generiert und alphabetisch sortiert.

Ansonsten gleich zu Fall2, jedoch eben ein Unterordner mehr.
4 Man führt Fall 2 bzw. 3 aus, verwendet jedoch eine zufällige Sortierung. Die Playlist ist gleich groß wie bei Fall 2 oder 3.

Wann in den nächsten Ordner gesprungen wird hängt davon ab, wie zufällig die Playlist gewürfelt wurde.

War jetzt alles in allem doch umfangreicher, als ich dachte.
Auf dem Weg ist mir auch noch aufgefallen, dass es beim Speichern der allg. Einstellungen zu Neustarts kommen kann, die offenbar an einem Stack-Overflow des LED-Rings hängen. Ich habe daher den Speicher um 1k vergrößert.

3 „Gefällt mir“

Richtig coole Erweiterung! Gefällt mir sehr :slight_smile: .

1 „Gefällt mir“

@Joe91 Hat den Fehler mit AUDIOBOOK_RECURSIVE gefunden :clap:. Weiß nicht, ob ich da so schnell drauf gekommen wäre: Wir verwenden für die Playlist eine Bitfeld und für die Speicherung der Playmodi sind das nur 4 Bit. Der besagte Playmodus hatte jedoch den Integer 16, womit das Bitfeld übergelaufen ist und bei 0 wieder anfing :rofl:.

Leider funktioniert das aber dennoch nicht gescheit. Das ist mit der Art und Weise, wie wir das im NVS speichern, dazu einfach nicht so passend. Eija, zur Not nehme ich es halt wieder raus.
Aber jetzt, wo ich das, was bereits gelaufen ist, nochmal nachtesten wollte, gibt’s auch da Probleme beim Ordnerspringen. Da habe ich einen Logikfehler drin.

Habe die Logik zum Springen zwischen den Verzeichnissen nochmal komplett überarbeitet - Commit ist raus. @Joe91 wollte sich AUDIOBOOK_RECURSIVE nochmal vornehmen.

1 „Gefällt mir“

So, das Feature ist jetzt aus meiner Sicht fertig.
Hier ist der Pull Request:

@Joe91 hat noch eine Idee für AudioPlayer_NvsRfidWriteWrapper() geliefert, so dass jetzt auch der rekursive Hörbuchmodus funktioniert.

Dann hätten wir jetzt also folgende drei Playmodes:

  • Alphabetisch sortiert + rekursiv
  • Zufällig sortiert + rekursiv
  • Hörbuchmodus rekursiv

Die Tiefe der Rekursion kann man in den allg. Einstellungen definieren. Passt bisschen auf damit: Wenn man das auf der obersten Ebene macht und viele Files auf der SD hat, dann kann das schnell mal zu einem Restart führen wegen Speichermangels.

2 „Gefällt mir“

Wow richtig cool, dass das jetzt so schnell ging. Ich komm gerade leider überhaupt nicht zum testen, da wir komplett in Renovierung und Umzugsarbeiten stecken. Ich freu mich schon riesig wenn ich dazu komme. Mein Junior wird sich sicher auch freuen.