Zufallswiedergabe Probleme

Hörspiel? Oder gibt es das schon irgendwo in einem anderen Modus…?

Der Party-Modus hat schon ein Array, das entweder immer wieder gleich oder neu gemischt abgespielt wird nach einem Durchlauf.

OK da wird dann aber alles abgespielt … Bei Hörspiel soll ja nur eins laufen.
Beim nächsten auflegen halt das nächste nur halt nicht wieder das gleiche (das geht ja auch mit meiner Schleife)
Macht das dann hier nicht auch Sinn wie im Party Modus?

Dann übernimm das doch vom Partymodus in den Hörspielmodus.

Ich habe so ein seltsames Verhalten mit der Zufallswiedergabe auch gerade festgestellt, allerdings mit der heute frisch geladenen DEV-Version (vom 05.05.2019).
Es tritt im Hörspielmodus auf, wenn sich 7 Dateien im Ordner befinden (hab es mit mehreren Ordnern ausprobiert).

Folgendes Verhalten habe ich beobachtet:
Wird der Tonuino neu gestartet und die Karte (eines Ordners mit 7 Dateien) aufgelegt, wird daraus immer Track 1 abgespielt. Lege ich kurz danach die Karte erneut auf, wird wieder Track 1 abgespielt. Und mit jedem mal wieder Track 1.
Lasse ich das Hörspiel eine Weile laufen (>2min) bevor ich die Karte erneut auflege wird ein anderer Track abgespielt, abhängig von der Wartezeit.

Ich habe nicht herausgefunden woran es genau liegt, konnte es aber mit folgendem Workaround erst mal beseitigen:
Ich rufe random() zweimal direkt hintereinander auf und verwerfe das erste Ergebnis.

Viele Grüße

Was passiert, wenn die Karte nicht unmittelbar nach Neustart auflegst, sondern 2-3 Sekunden später?

Bis eine Karte gespielt wird, wird nach dem starten 3x randomSeed() aufgerufen. Was schon lustig ist. Probier mal:

in randomSeed(micros()); zu ändern. Ggf die anderen aurufe mal auskommentieren (L717 und L1088).

Wenn ich 2-3 Sekunden warte habe ich das selbe Verhalten. Wenn ich länger Warte (ca. 2min) wählt er stattdessen Track 7, aber reproduzierbar Track 7. Und nach erneutem kurzen Auflegen wieder Track 7.

Logfile auflegen nach 8s:

07:14:47.555 ->  _____         _____ _____ _____ _____
07:14:47.555 -> |_   _|___ ___|  |  |     |   | |     |
07:14:47.595 ->   | | | . |   |  |  |-   -| | | |  |  |
07:14:47.595 ->   |_| |___|_|_|_____|_____|_|___|_____|
07:14:47.595 -> 
07:14:47.595 -> TonUINO Version 2.1
07:14:47.595 -> created by Thorsten Voß and licensed under GNU/GPL.
07:14:47.595 -> Information and contribution at https://tonuino.de.
07:14:47.595 -> 
07:14:47.595 -> === loadSettingsFromFlash()
07:14:47.595 -> Version: 2
07:14:47.595 -> Maximal Volume: 17
07:14:47.595 -> Minimal Volume: 1
07:14:47.595 -> Initial Volume: 15
07:14:47.595 -> EQ: 1
07:14:47.595 -> Locked: 0
07:14:47.595 -> Sleep Timer: 5
07:14:47.595 -> Inverted Volume Buttons: 0
07:14:47.595 -> Admin Menu locked: 0
07:14:47.595 -> Admin Menu Pin: 1111
07:14:47.595 -> === setstandbyTimer()
07:14:47.595 -> 300038
Firmware Version: 0x92 = v2.0
07:14:49.718 -> === playShortCut()
07:14:49.758 -> 3
07:14:49.758 -> == playFolder()
07:14:49.758 -> === disablestandby()
1 Dateien in Ordner 10
07:14:49.918 -> Einzel Modus -> eine Datei aus dem Odrdner abspielen
07:14:49.918 -> === disablestandby()
=== nextTrack()
07:14:52.482 -> Einzel Modus aktiv -> Strom sparen
07:14:52.482 -> === setstandbyTimer()
07:14:52.482 -> 304922
Card UID: 21 9F B6 1E
07:15:00.173 -> PICC type: MIFARE 1KB
07:15:00.173 -> Authenticating Classic using key A...
07:15:00.173 -> Reading data from block 4 ...
07:15:00.213 -> Data on Card :
07:15:00.213 ->  13 37 B3 47 02 06 01 09 CE 00 00 00 00 00 00 00
07:15:00.213 -> 
07:15:00.213 -> 6
07:15:00.213 -> 6
07:15:00.213 -> == playFolder()
07:15:00.213 -> === disablestandby()
7 Dateien in Ordner 6
07:15:00.373 -> Hörspielmodus -> zufälligen Track wiedergeben
07:15:00.373 -> 1
Card UID: 21 9F B6 1E
07:15:05.460 -> PICC type: MIFARE 1KB
07:15:05.460 -> Authenticating Classic using key A...
07:15:05.460 -> Reading data from block 4 ...
07:15:05.500 -> Data on Card :
07:15:05.500 ->  13 37 B3 47 02 06 01 09 CE 00 00 00 00 00 00 00
07:15:05.500 -> 
07:15:05.500 -> 6
07:15:05.500 -> 6
07:15:05.500 -> == playFolder()
07:15:05.500 -> === disablestandby()
7 Dateien in Ordner 6
07:15:05.661 -> Hörspielmodus -> zufälligen Track wiedergeben
07:15:05.661 -> 1
Card UID: 21 9F B6 1E
07:15:10.748 -> PICC type: MIFARE 1KB
07:15:10.748 -> Authenticating Classic using key A...
07:15:10.748 -> Reading data from block 4 ...
07:15:10.748 -> Data on Card :
07:15:10.748 ->  13 37 B3 47 02 06 01 09 CE 00 00 00 00 00 00 00
07:15:10.798 -> 
07:15:10.798 -> 6
07:15:10.798 -> 6
07:15:10.798 -> == playFolder()
07:15:10.798 -> === disablestandby()
7 Dateien in Ordner 6
07:15:10.958 -> Hörspielmodus -> zufälligen Track wiedergeben
07:15:10.958 -> 1
Card UID: 21 9F B6 1E
07:15:15.685 -> PICC type: MIFARE 1KB
07:15:15.685 -> Authenticating Classic using key A...
07:15:15.685 -> Reading data from block 4 ...
07:15:15.685 -> Data on Card :
07:15:15.685 ->  13 37 B3 47 02 06 01 09 CE 00 00 00 00 00 00 00
07:15:15.685 -> 
07:15:15.685 -> 6
07:15:15.685 -> 6
07:15:15.685 -> == playFolder()
07:15:15.685 -> === disablestandby()
7 Dateien in Ordner 6
07:15:15.885 -> Hörspielmodus -> zufälligen Track wiedergeben
07:15:15.885 -> 1
Card UID: 21 9F B6 1E
07:15:21.534 -> PICC type: MIFARE 1KB
07:15:21.534 -> Authenticating Classic using key A...
07:15:21.534 -> Reading data from block 4 ...
07:15:21.534 -> Data on Card :
07:15:21.534 ->  13 37 B3 47 02 06 01 09 CE 00 00 00 00 00 00 00
07:15:21.534 -> 
07:15:21.534 -> 6
07:15:21.534 -> 6
07:15:21.534 -> == playFolder()
07:15:21.534 -> === disablestandby()
7 Dateien in Ordner 6
07:15:21.734 -> Hörspielmodus -> zufälligen Track wiedergeben
07:15:21.734 -> 1

Logfile auflegen nach 2min:

07:28:11.381 ->  _____         _____ _____ _____ _____
07:28:11.381 -> |_   _|___ ___|  |  |     |   | |     |
07:28:11.381 ->   | | | . |   |  |  |-   -| | | |  |  |
07:28:11.381 ->   |_| |___|_|_|_____|_____|_|___|_____|
07:28:11.381 -> 
07:28:11.381 -> TonUINO Version 2.1
07:28:11.381 -> created by Thorsten Voß and licensed under GNU/GPL.
07:28:11.421 -> Information and contribution at https://tonuino.de.
07:28:11.421 -> 
07:28:11.421 -> === loadSettingsFromFlash()
07:28:11.421 -> Version: 2
07:28:11.421 -> Maximal Volume: 17
07:28:11.421 -> Minimal Volume: 1
07:28:11.421 -> Initial Volume: 15
07:28:11.421 -> EQ: 1
07:28:11.421 -> Locked: 0
07:28:11.421 -> Sleep Timer: 5
07:28:11.421 -> Inverted Volume Buttons: 0
07:28:11.421 -> Admin Menu locked: 0
07:28:11.421 -> Admin Menu Pin: 1111
07:28:11.421 -> === setstandbyTimer()
07:28:11.421 -> 300038
Firmware Version: 0x92 = v2.0
07:28:13.544 -> === playShortCut()
07:28:13.544 -> 3
07:28:13.544 -> == playFolder()
07:28:13.544 -> === disablestandby()
1 Dateien in Ordner 10
07:28:13.744 -> Einzel Modus -> eine Datei aus dem Odrdner abspielen
07:28:13.744 -> === disablestandby()
=== nextTrack()
07:28:16.308 -> Einzel Modus aktiv -> Strom sparen
07:28:16.308 -> === setstandbyTimer()
07:28:16.308 -> 304923
Card UID: 21 9F B6 1E
07:30:31.031 -> PICC type: MIFARE 1KB
07:30:31.031 -> Authenticating Classic using key A...
07:30:31.031 -> Reading data from block 4 ...
07:30:31.031 -> Data on Card :
07:30:31.031 ->  13 37 B3 47 02 06 01 09 CE 00 00 00 00 00 00 00
07:30:31.031 -> 
07:30:31.031 -> 6
07:30:31.031 -> 6
07:30:31.031 -> == playFolder()
07:30:31.031 -> === disablestandby()
7 Dateien in Ordner 6
07:30:31.192 -> Hörspielmodus -> zufälligen Track wiedergeben
07:30:31.192 -> 7
Card UID: 21 9F B6 1E
07:30:37.601 -> PICC type: MIFARE 1KB
07:30:37.601 -> Authenticating Classic using key A...
07:30:37.601 -> Reading data from block 4 ...
07:30:37.601 -> Data on Card :
07:30:37.601 ->  13 37 B3 47 02 06 01 09 CE 00 00 00 00 00 00 00
07:30:37.601 -> 
07:30:37.601 -> 6
07:30:37.601 -> 6
07:30:37.601 -> == playFolder()
07:30:37.601 -> === disablestandby()
7 Dateien in Ordner 6
07:30:37.761 -> Hörspielmodus -> zufälligen Track wiedergeben
07:30:37.801 -> 7
Card UID: 21 9F B6 1E
07:30:45.372 -> PICC type: MIFARE 1KB
07:30:45.372 -> Authenticating Classic using key A...
07:30:45.412 -> Reading data from block 4 ...
07:30:45.412 -> Data on Card :
07:30:45.412 ->  13 37 B3 47 02 06 01 09 CE 00 00 00 00 00 00 00
07:30:45.412 -> 
07:30:45.412 -> 6
07:30:45.412 -> 6
07:30:45.412 -> == playFolder()
07:30:45.412 -> === disablestandby()
7 Dateien in Ordner 6
07:30:45.572 -> Hörspielmodus -> zufälligen Track wiedergeben
07:30:45.572 -> 7

Ich hab das wirklich mit einer unveränderten, frisch geladenen DEV ausprobiert. Kann das niemand reproduzieren (7 Dateien im Ordner + Hörspielmodus = Mist)?

Das randomSeed() oft aufgerufen wird hab ich auch schon bemerkt. Ich probier das auskommentieren heute Abend mal aus, verspreche mir aber nicht viel davon. Es sollte ja nur der letzte Aufruf randomSeed(millis() + random(1000)); ausschlaggebend sein. Es scheint mir als wäre das erste Ergebnis von random() nach einem neuen randomSeed unbrauchbar. Der seed muss ja gewechselt haben, sonst würde der zweite Aufruf von random() ja auch immer die gleiche Zahl liefern.

Oh sorry, das war nur für einen Ordner so. Komisch. Hast du einen anderen Nano zum probieren?

Ich hab es ausprobiert:

  • Nur Zeile 717 und 1088 auskommentieren: Verhalten unverändert
  • Zeile 717 ändern in randomSeed(micros()); und Zeile 717 und 1088 auskommentieren: Es wird jetzt ein zufälliger Track ausgewählt.
  • Nur Zeile 839 und 1088 auskommentieren: Es wird jetzt ein zufälliger Track ausgewählt, aber er scheint sich zu wiederholen wenn man die Karte kurz danach erneut auflegt (<2s).

Sorry, ich hatte überlesen, das du millis() in micros() geändert hast. Da hast du natürlich recht, das sollte was ändern.
Danke für die Tipps!

Ich habe zwar noch einen anderen Nano, aber die anderen Teile nicht mehr. Und der Nano lässt sich nicht so einfach tauschen, da er schon fest verlötet in ein Gehäuse eingebaut ist.

Ich hatte ja gedacht, dass ich nicht der Einzige bin bei dem das Phänomen auftritt. Es ist wirklich nur wenn ein Ordner 7 Dateien beinhaltet, bei 6 oder 8 Dateien funktioniert es wunderbar.

Viele Grüße!

Bei mir scheint das Problem auch vorhanden zu sein. Habe einen Ordner mit 14 Titeln. Dort wird auch immer nur zwischen 2 Titeln gewechselt. Lösche ich einen Titel raus werden alle Titel druchgemischt. Wobei manche trotzdem wesentlich häufiger dran kommen als andere.

Man kommt auch ohne Speichern einer Zufallsplaylist aus:

Der Zweck einer Zufallsplaylist ist ja, dass alle Tracks eines Ordners ohne Wiederholung abgespielt werden sollen, aber in keiner erkennbaren Reihenfolge. Außerdem soll die verwendete Reihenfolge bei jedem Auflegen anders sein. Das geht mit ein wenig Mathematik auch vollkommen ohne Speichern einer Zufallspermutation. Mit dem folgenden Trick erhält man für einen Ordner mit N Tracks eine von mindestens N·(N-1) Playlist-Permutationen. Das sind zwar längst nicht alle möglichen Permutationen, aber mehr als genug, um keine Wiederholungsmuster zu hören.

Der Trick ist der Folgende:

Für zufällig wahrgenommene Abspielreihenfolgen reicht im Grunde eine konstante Schrittweite von Tracknummer zu anschließend abgespielter Tracknummer. Dazu muss man nur einmal beim Auflegen der Karte per Zufall eine Schrittweite wählen (und einen zufälligen Startpunkt setzen). Mittels der Modulo-Funktion wird nun immer der nächste Track ermittelt. So erhält man bei N Tracks und festem Startpunkt N-1 verschiedene Playlists ohne jegliche Wiederholung, sofern N eine Primzahl ist. Und wenn man nun noch den Startpunkt zufällig wählt, sind es N·(N-1) garantiert verschiedene Playlists. Das reicht vollkommen aus.

Das Prinzip funktioniert so aber nur dann, wenn die Anzahl der Tracks in einem Ordner eine Primzahl ist. Sonst könnte es zu kürzeren Zyklen kommen. Man kannn aber einfach eine Primzahl wählen, die größer ist als die Anzahl der Tracks, und überspringt einfach zu große Werte der Permutation. Damit funktioniert das Prinzip auch für Nicht-Primzahlen. So ergeben sich teilweise sogar noch mehr als N·(N-1) unterschiedliche Playlists (wenn man noch den Umbruch der zu großen Werte mit einem zufälligen Offset variiert). Jede Playlist umfasst dabei garantiert alle Tracks.

Auch das Ermitteln einer Primzahl, die größer ist als N, ist für N<256 algorithmisch lösbar. Es muss ja nicht zwingend die nächstgrößte Primzahl sein. Theoretisch würde es reichen, eine feste Primzahl über 255 zu wählen (z.B. 257). Aber dann müsste man teilweise schon sehr viele Werte überspringen. Es geht aber auch etwas besser: Die Funktion 11+(60·ceil(N-11)/60) liefert für jedes N<256 eine Primzahl P>=N (und zwar eine der Folgenden: 11, 71, 131, 191, 251, 311).

Die Implementation sieht dann so aus:

Initialisierung (beim Auflegen der Karte):

firstTrack=random(numTracksInFolder)+1;
periodLength=11+60*ceil((numTracksInFolder-11)/60);
step=random(1,numTracksInFolder);
currentPos=random(numTracksInFolder);
currentTrack=firstTrack;

Anwählen des nächsten Tracks:

do{
    currentPos=(currentPos+step)%periodLength;
} while (currentPos>=numTracksInFolder);
currentTrack=(currentPos+firstTrack-1)%numTracksInFolder+1;

Anwählen des vorhergehenden Tracks:

do{
    currentPos=(currentPos+periodLength-step)%periodLength;
} while (currentPos>=numTracks);
currentTrack=(currentPos+firstTrack-1)%numTracksInFolder+1;

Die While-Schleifen werden jeweils maximal 60 mal durchlaufen, im Durchschnitt aber nicht öfter als 1,53 mal.

Man könnte auch die vier nötigen Werte (firstTrack, periodLength, currentPos, step) auf der Karte speichern und so eine Playlist beim nächsten Auflegen fortsetzen. Wenn man möchte, dass nach dem Durchlaufen eines Zyklus eine neue Reihenfolge erstellt wird, reicht es, sobald currentTrack wieder gleich firstTrack ist, step neu zu setzen. Damit ist auch gewährleistet, dass auch der aktuelle Track nicht zufällig wieder gleich als nächstes kommt.

3 „Gefällt mir“

Ich liebe ja so etwas! :+1:

1 „Gefällt mir“

Mich hatten noch zwei Sachen an der Lösung gewurmt:

  1. Die do-while-Schleife ist unschön, da sie manchmal (gerade bei nur wenigen Tracks in einem Ordner) bis zu 60 mal durchlaufen werden muss. Der Grund ist, dass das zugegebenermaßen trickreiche Ermitteln einer geeigneten Primzahl ist optimal ist. Besser wäre es, eine Primzahl zu finden, die möglichst noch näher an der jeweiligen Trackanzahl liegt, oder aber man findet einen Weg, die do-while-Schleife vollständig einzusparen.
  2. Fast alle aufeinanderfolgenden Tracks haben denselben Abstand. Ich bin mir nicht sicher, ob das in jedem Fall hinreichend zufällig wirkt.

Für beides habe ich jetzt eine Lösung gefunden.

Zum Einen kann die do-while-Schleife durch eine einzige Modulo-Operation ersetzt werden wenn bestimmte Fälle vorher abgefangen wurden. Damit ist es nicht mehr nötig, eine möglichst kleine Primzahl zu wählen und ich setze die Primzahl daher fest auf 257.

Zum Anderen kann ich noch die Zahl der erzeugbaren Permutationen vervielfachen, indem ich das Bitmuster einer generierten Zahl mit einem initial zufällig gewählten aber dann festen Bitmuster per XOR durcheinanderwürfele. Das ergibt dann zwangsläufig wieder eine Permutation derselben Zykluslänge, bei der nun aber die Differenzen aufeinanderfolgender Werte nicht mehr so einheitlich sind. Man muss nur gewährleisten, dass das most significant bit (MSB) des verwendete Bitmuster kleiner ist als das der Trackanzahl. Das ist aber (ohne aufwändiges Errechnen des MSBs) gewährleistet, wenn man das Bitmuster einer Zufallszahl nimmt, die maximal halb so groß ist wie die Zahl der Tracks.

Zusammen ergibt das dann den folgenden Code:

Initialisierung (beim Auflegen der Karte):

firstTrack = random(numTracksInFolder) + 1;
periodLength = 257;
step = random(1, numTracksInFolder);
currentPos = random(numTracksInFolder);
currentTrack = firstTrack;
xorMask = random(numTracksInFolder >> 2);

Anwählen des nächsten Tracks:

currentPos = (currentPos + step) % periodLength;
if (currentPos >= numTracksInFolder) {
    currentPos = step - (periodLength - currentPos - 1) % step - 1;
}

currentTrack = (currentPos ^ xorMask + firstTrack - 1) % numTracksInFolder + 1;

Anwählen des vorhergehenden Tracks:

currentPos = (currentPos + periodLength - step) % periodLength;
if (currentPos >= numTracksInFolder) {
    currentPos = numTracksInFolder - step + (currentPos - numTracksInFolder) % step;
}

currentTrack = (currentPos ^ xorMask + firstTrack - 1) % numTracksInFolder + 1;

Damit erhält man eine zufällige von mehr als N(N-1)N/2 Permutationen mit geringem konstantem Zeitaufwand pro Schritt.

Wurde das auch schon in den Tonuino Code übernommen? Inder App gibt es einen derartigen Modus nicht…

Welchen Modus meinst du?

Den Zufallswiedergabe aber nicht endlos, sondern nur einmal jedes Lied.

Nein, ist vermutlich auch nicht vorgesehen. Du kannst das aber für dich anpassen. Den Partymodus findest du ungefähr in Zeile 550:

if (myFolder->mode == 3 || myFolder->mode == 9) {
    if (currentTrack != numTracksInFolder - firstTrack + 1) {
      Serial.print(F("Party -> weiter in der Queue "));
      currentTrack++;
    } else {
      Serial.println(F("Ende der Queue -> beginne von vorne"));
      currentTrack = 1;
      //// Wenn am Ende der Queue neu gemischt werden soll bitte die Zeilen wieder aktivieren
      //     Serial.println(F("Ende der Queue -> mische neu"));
      //     shuffleQueue();
    }
    Serial.println(queue[currentTrack - 1]);
    mp3.playFolderTrack(myFolder->folder, queue[currentTrack - 1]);
  }

Diese Zeilen müsstest du durch Folgende ersetzen:

if (myFolder->mode == 3 || myFolder->mode == 9) {
  if (currentTrack != numTracksInFolder - firstTrack + 1) {
    Serial.print(F("Party -> weiter in der Queue "));
    currentTrack++;
    Serial.println(queue[currentTrack - 1]);
    mp3.playFolderTrack(myFolder->folder, queue[currentTrack - 1]);
} else {
    Serial.println(F("Ende der Queue -> Stop"));
    setstandbyTimer();
    //// Wenn am Ende der Queue neu gemischt werden soll bitte die Zeilen wieder aktivieren
    //     Serial.println(F("Ende der Queue -> mische neu"));
    //     shuffleQueue();
  }
}

Dann wird bei dir nach einem Durchlauf die Wiedergabe beendet. Das verändert den Partymodus dann permanent. Wenn du das nur für einige Karten willst, müsstest du daraus einen eigenen Modus machen

Edit: Ich habe das jetzt noch nicht ausgiebig getestet.