Aktuelle Teilprojekte: MQTT, Fastled mit SPI und Audio ohne Task

@Joe91 Ich habe jetzt mal deinen PR mit dem Audiotask getestet. Funktioniert soweit, Folgendes ist mir aufgefallen:

5x Upload ohne Abspielvorgang direkt nach Neustart:
306
416
285
315
370

5x Upload mit parallelem Abspielvorgang:
339
375
375
342
361

Ist mir tatsächlich gerade bisschen unklar, warum im ersten Fall der Upload nicht schneller + konstanter ist. Im zweiten Fall sieht man, dass die LEDs anfangen zu blinken, wenn der Upload aktiv ist.

Ich habe auch mal das von @tueddy verlinkte IDF 5.4.2 getestet. Aber damit ist die Blinkerei echt übel. Bei 5.4.1 ist sie auch vorhanden, aber nicht ganz so schlimm.

@Joe91: Also wir brauchen parallel auf jeden Fall auch deinen LED-Fix, sonst haben wir wieder fiese Blinkerei am Start. Du müsstest das aber nochmal mit 5.4.2 testen.

So, bin wieder zurück von den Toten :smiley:

Habe die SPI-LED-Ansteuerung aktualisiert und auf IDF 5.4.2 umgestellt. Zusätzlich hat zackees von Fast-LED nochmal einen Bug in diesem Kontext gefixed auf den ich entsprechend umgestellt habe und jetzt der HACK nicht mehr benötigt wird.

Um den Upload nochmal bisschen zu pushen habe ich den LED-Task wieder pausiert, da wir damit nochmal ein ganzes Stück schneller werden (ist aber Geschmacksache).
Getestet jeweils 3x mit 125 MB und 11 Dateien:
Während Audio-Playback: 438.83 KB/s (mit aktivem LED-Task nur 384.40 KB)
Während Pausiert: 492.23 KB/s (mit aktivem LED-Task nur 424.37 KB/s)

Beim Testen ist mir noch ein Bug aufgefallen, wodurch während dem Upload beim Erkennen einer neuen RFID-Karte ein Watchdog getriggert werden konnte, da die RFID-Statemachine beim Pausieren in einem ungünstigen Zustand stehen bleiben konnte. Das habe ich ebenfalls noch gefixed und damit jetzt keine Probleme mehr gehabt.

Webserver und Audio-Lib habe ich ebenfalls aktualisiert.
Die SPI-LED-Ansteuerung und das Entfernen des Audiotasks sind jetzt zusammen in diesem MR, da das bei der neuen IDF anders ja kein Sinn macht.
Die Audio-Playback-Issues sind damit bei mir weiterhin behoben und das Flackern vollständig weg.
Gerne nochmal testen und Feedback geben.

6 „Gefällt mir“

Moin Joe,

ich hab deine Änderungen bei mir aufgespielt und bislang läuft’s einwandfrei! Werde ich natürlich trotzdem weiter beobachten. Vielen Dank für die Mühe! :slight_smile:

2 „Gefällt mir“

Dem schließe ich mich an: Hab’s eben getestet auf meiner neuen Complete und keinerlei Fehler festgestellt.
@tueddy Testest du das noch? Aus meiner Sicht könnte man das in dev übernehmen. Langsam wäre es dann auch an der Zeit, das Ganze in den master zu bringen, da das letzte Release da schon fast 1j alt ist.

1 „Gefällt mir“

Klingt gut.
Bevor wir den merge in den master machen, würde ich gerne noch auf eine offizielle nächste Version von FastLed warten (hier sind wir zwecks bugfix für uns aktuell auf einer Zwischenversion) und noch bei @Wolle nachfragen, ob wir bei der Audio-Lib auf v3.4.0 bleiben sollen, oder die Bugfixes seither ein Upgrade auf v3.4.1 oder sogar die kommende v3.4.2 rechtfertigen.
Umbau bezüglich MQTT-Namensschema usw. würden wir dann nach dem merge in main auf dem dev-branch weitermachen?

1 „Gefällt mir“

Das mit dem Merge in den Master war mehr so ne perspektivische Sache, die man mittelfristig mal machen sollte. Der Stand ist einfach uralt dort.

Ja, passt. Eilt aus meiner Sicht nicht.

1 „Gefällt mir“

Hab’s jetzt in dev gemergt, damit es mal vorwärts geht :slight_smile:.
Danke für deinen PR.

3 „Gefällt mir“

Hi Joe91,
was sich geändert hat sind hauptsächlich interne Verbesserungen. Zum Einsatz kommen Unique-Pointer fürs PSRAM anstelle von malloc und free. Das verhindert ungewollte Speicherlecks und unerklärliche Abstürze.
Es geht jetzt der Hybrid.Mode in OPUS und somit funktionieren nun auch Streams wie Deutschlandfunk.
Und es gibt den Http-Range Support für Webfiles. Du kannst mit setTimeOffset(); ± xxx Sekunden auch in Webfiles vorwärts und rückwärts springen (die meisten Server unterstützen Ranges). Das ging bisher nur lokal.

3 „Gefällt mir“

@Joe91 Wolltest du kurzfristig noch eine Aktualisierung der Audiolib einbringen? Mir geht’s nur drum, dass der Dev ein bisschen getestet wird, so dass man ihn demnächst mal in den master übernehmen kann. Aktuell sehe ich auf jeden Fall keine Punkte, die dagegen sprächen.

Ja, gerne. Nach Wolles aussage würde ich auf dem dev im platformio.ini die Audio lib wie folgt ändern:
https://github.com/schreibfaul1/ESP32-audioI2S.git#1a48874 ; v3.4.1
Zumindest bei ein paar kurzen Versuchen konnte ich damit keine Probleme feststellen und das verhindern von memory-leaks ist immer gut :slight_smile:
Nimmst du das rein oder soll ich dazu einen PR erstellen?
Zackees (von FastLED) scheint sich ebenfalls einem neuen Release zu nähern, da gibt es aber noch nichts ofzizielles…

1 „Gefällt mir“

Ok. Nee, da brauchst natürlich keinen PR. Ich teste das dann auch mal und wenn das passt, dann mache ich den Commit.

Das kompiliert bei mir gar nicht - da müssen erst Anpassungen in unserem Code gemacht werden:

src/AudioPlayer.cpp: In function 'void Audio_TaskPause()':
src/AudioPlayer.cpp:96:14: warning: unused variable 'audio_active' [-Wunused-variable]
   96 |         bool audio_active = false;
      |              ^~~~~~~~~~~~
src/AudioPlayer.cpp: In function 'void Audio_TaskResume()':
src/AudioPlayer.cpp:99:14: warning: unused variable 'audio_active' [-Wunused-variable]
   99 |         bool audio_active = true;
      |              ^~~~~~~~~~~~
src/AudioPlayer.cpp: In function 'void AudioPlayer_Loop()':
src/AudioPlayer.cpp:436:69: error: 'class Audio' has no member named 'getAudioDataStartPos'; did you mean 'uint32_t Audio::m_audioDataStart'? (not accessible from this context)
  436 |                                 uint32_t audioDataStartPos = audio->getAudioDataStartPos();
      |                                                                     ^~~~~~~~~~~~~~~~~~~~
In file included from src/AudioPlayer.cpp:6:
.pio/libdeps/complete/ESP32-audioI2S/src/Audio.h:972:21: note: declared private here
  972 |     uint32_t        m_audioDataStart = 0;           // in bytes
      |                     ^~~~~~~~~~~~~~~~
src/AudioPlayer.cpp:437:83: error: 'class Audio' has no member named 'getFilePos'; did you mean 'setFilePos'?
  437 |                                 gPlayProperties.currentRelPos = ((double) (audio->getFilePos() - audioDataStartPos - audio->inBufferFilled()) / (fileSize - audioDataStartPos)) * 100;
      |                                                                                   ^~~~~~~~~~
      |                                                                                   setFilePos
src/AudioPlayer.cpp:531:92: error: 'class Audio' has no member named 'getFilePos'; did you mean 'setFilePos'?
  531 |                                         Log_Printf(LOGLEVEL_INFO, trackPausedAtPos, audio->getFilePos(), audio->getFilePos() - audio->inBufferFilled());
      |                                                                                            ^~~~~~~~~~
      |                                                                                            setFilePos
src/AudioPlayer.cpp:531:113: error: 'class Audio' has no member named 'getFilePos'; did you mean 'setFilePos'?
  531 |                                         Log_Printf(LOGLEVEL_INFO, trackPausedAtPos, audio->getFilePos(), audio->getFilePos() - audio->inBufferFilled());
      |                                                                                                                 ^~~~~~~~~~
      |                                                                                                                 setFilePos
src/AudioPlayer.cpp:532:175: error: 'class Audio' has no member named 'getFilePos'; did you mean 'setFilePos'?
  532 |                                         AudioPlayer_NvsRfidWriteWrapper(gPlayProperties.playRfidTag, gPlayProperties.playlist->at(gPlayProperties.currentTrackNumber), audio->getFilePos() - audio->inBufferFilled(), gPlayProperties.playMode, gPlayProperties.currentTrackNumber, gPlayProperties.playlist->size());
      |                                                                                                                                                                               ^~~~~~~~~~
      |                                                                                                                                                                               setFilePos
src/AudioPlayer.cpp:795:72: error: 'class Audio' has no member named 'getAudioDataStartPos'; did you mean 'm_audioDataStart'?
  795 |                         uint32_t newFilePos = uint32_t((double) audio->getAudioDataStartPos() * (1 - gPlayProperties.currentRelPos / 100) + (gPlayProperties.currentRelPos / 100) * audio->getFileSize());
      |                                                                        ^~~~~~~~~~~~~~~~~~~~
      |                                                                        m_audioDataStart
src/AudioPlayer.cpp:795:72: error: 'uint32_t Audio::m_audioDataStart' is private within this context
.pio/libdeps/complete/ESP32-audioI2S/src/Audio.h:972:21: note: declared private here
  972 |     uint32_t        m_audioDataStart = 0;           // in bytes
      |                     ^~~~~~~~~~~~~~~~

Ah, das hatte ich bei mir schon gemacht. Ich schau ob ich das direkt finde und mache doch einen PR :slight_smile: . Sorry, das hattte ich nicht mehr im Kopf.

Edit: So, ich erinnere mich wieder: Hatte das eher halbherzig und auf die Schnelle angepasst, fand das jetzt aber durch die neuen Methoden viel logischer und lesbarer sein, da wir jetzt die Position nur noch in Sekunden lesen und setzen.
Die lokalen Änderungen sind bei mir zwar lauffähig, aber das mit den Positionen in den drei Verschiedenen geänderten Fällen noch nicht getestet… Ich muss schauen ob ich da heute noch dazukomme das zu testen, werde den PR aber in jedem Fall noch rausschicken (dann aber halt auf draft :slight_smile: ) …

Edit 2:

Also war doch nicht ganz so schlecht das auf die Schnelle :wink:
Was ich noch nicht getestet habe ist das Fortsetzen von einer alten Position (also z.B. im Hörbuchmodus). Fortschrittsbalken, relatives Springen usw. scheint alles zu gehen, auch wenn ich mich da nicht besonders bemüht hatte :smiley:

1 „Gefällt mir“

War die letzten Tage stressig: Zwei Einschulungen und zwei Geburtstage; komme erst jetzt dazu :slight_smile:
Also grundsätzlich funktioniert das Springen, habe es über das Webinterface getriggert. Allerdings sieht man im Logging, dass die Einheit, mit der intern gearbeitet wird, zu klein ist:

N [259493] Sprung zu Position 163/10938318

Bei der Position 163 bin ich ungefähr in die Mitte des Files gesprungen. Und genau diese kleine Zahl sorgt auch für Problemen im Hörspielmodus: ESPuino springt mehr oder weniger direkt an den Anfang. Auch ist es so, dass wenn ich eine somit gelernte Hörspielkarte auflege, dass dann kurz am Anfang alle LEDs leuchten. Wobei stop, ich sehe gerade, dass das auch passiert, wenn man aus dem Dateibrowser eine x-beliebige Datei aufruft. Möglicherweise ist das auch ein Bestandsbug.

Ah, das muss man dann auch noch anpassen. Die aktuelle Pos gibt es nicht mehr als Methode. Daher wird nur noch mit Sekunden gearbeitet.

Vorschlag wäre, dass wir einfach alle Stellen, die die Gesamtlänge noch verwenden anpassen auf die Länge in Sekunden (alternative Methode).

Vermutlich ist das mit dem Hörbuch dann auch behoben, wenn man damit durch ist.

Bin jetzt aktuell noch im Urlaub, das sollte dann aber schnell gehen wenn ich zurück bin.

3 „Gefällt mir“

Kurzes Update:
Das mit dem Hörbuch ist eigentlich bereits richtig umgesetzt, allerdings ist die neue Methode in der Audio-Lib setAudioPlayPosition scheinbar nicht dafür geeignet. Diese hängt unter anderem von der aktuellen Bitrate ab, die vermutlich aber zu diesem Moment noch nicht bekannt ist.

@Wolle , könntest du uns für diesen speziellen Fall (direktes Fortsetzen einer Datei an einer bestimmten Zeit - ehemals Position) eine alternative anbieten oder verwenden wir das aktuell einfach falsch?

1 „Gefällt mir“

Noch ein Zusatz:
Wenn man auf einen erfolgreichen Returncode der Funktion wartet, und solgane das audio→loop ausführt, dann “funktioniert” es zwar besser, aufgrund der variablen Bitrate wird das Offset zwischen Soll und Ist aber teilweise richtig groß (Bei 2 Min Soll-Offset startet er z.B. bei etwa 1 min).

            if (gPlayProperties.startAtFilePos > 0) {
                int32_t tiemeoutSetStartPos = 3000; // 3 seconds should be enough to set startposition
                while (!audio->setAudioPlayPosition(gPlayProperties.startAtFilePos) && (tiemeoutSetStartPos > 0)) {
                    audio->loop();
                    Log_Println("not done yet", LOGLEVEL_NOTICE);
                    vTaskDelay(portTICK_PERIOD_MS * 20u);
                    tiemeoutSetStartPos -= 20;
                }
                Log_Printf(LOGLEVEL_NOTICE, trackStartatPos, gPlayProperties.startAtFilePos);
                gPlayProperties.startAtFilePos = 0;
            }

Ich glaube wir kommen hier nicht drum herum auch weiterhin ein GetFilePos() o.ä. zu verwenden, wenn wir diese Funktion weiter anbieten wollen…
Oder halt für den speziellen Fall ein “parsen der Datei” bis zur entsprechenden Stelle um dafür zu kompensieren…

Hi Joe,
die absoluten Dateipositionen möchte ich nicht länger verwenden.
Wie beim Media Player, VLC Player… möchte ich die Zeit als Kriterium benutzen.
Teilweise hatte ich das schon geändert:
bool Audio::setAudioPlayTime(uint16_t sec); springt zur absoluten Zeitmarke
bool Audio::setTimeOffset(int sec); springt relativ um die augenblickliche Zeitmarke
uint32_t Audio::stopSong(); gibt die augenblickliche Zeitmarke zurück (in Sekunden)
uint32_t Audio::getAudioFileDuration(); Gesamtspieldauer (in Sekunden)
uint32_t Audio::getAudioCurrentTime(); Augenblickliche Zeitmarke (in Sekunden)
Wenn die Datei abgespielt wird sollte das immer funktionieren, unabhängig von der Quelle (Web, SD, SPIFFS…)
Dann natürlich noch bool Audio::pauseResume(); zum Anhalten und Fortsetzen

Schwierig wird es die Zeitposition beim Start einer Datei zu setzen.
Wenn die Audiodatei Informationen über die nominelle Bitrate oder die Gesamtspieldauer enthält ist das möglich
Also:
mp3 wenn es mpeg1 LayerIII ist gibt es fast immer einen Xing oder Info Header → okay
flac es gibt fast immer in BITSTREAMINFO den Wert totalSamplesInStream → okay
m4a es gibt fast immer ein mdhd Atom welches den Tag04 enthält → okay

In diesen Fällen, also bei fast allen neueren Dateien (flac, mp3, m4a) geht dann
bool Audio::connecttoFS(fs::FS& fs, const char* path, int32_t fileStartTime = -1); in fileStartTime Werte > 0 … < Gesamtspieldauer

Bei allen anderen muss die Datei erst gestartet wenden, dann ist nach kurzer Zeit die mittlere Bitrate bekannt und du kannst setAudioPlayTime() anwenden.

P.S.
Die „weak compiler bindings“ (wie audio_info(const char* info){}) habe ich durch „functional callbacks“ ersetzt. Das ist moderner und etwas einfacher anzuwenden

Im setup() setzt du eine Zeiger auf die Callback Funktion
Audio::audio_info_callback = my_audio_info; // Name frei vergebbar

und dann in CB:
void my_audio_info(Audio::msg_t m) {
switch(m.e){
case Audio::evt_info:

}

Das sind die Events in m.e (zur Unterscheidung)
typedef enum {evt_info = 0, evt_id3data, evt_eof, evt_name, evt_icydescription, evt_streamtitle, evt_bitrate, evt_icyurl, evt_icylogo, evt_lasthost, evt_image, evt_lyrics, evt_log} event_t;

in m.msg steht ein kurzer Text wie „SampleRate (Hz): 48000“
in m.arg1 steht der Wert als Integer also 48000
in m.s der EventName als const char*
in m.i2s_num der I2S Kanal
in m.vec der BildVektor (wie bisher APIC, Segment, Anfang, Ende)

Hoffe, das ist hilfreich für euer Projekt

vG
Wolle

3 „Gefällt mir“

Vielen Dank dir für diese ausführliche Antwort!
Habe bisschen gebraucht bis ich festgestellt habe, dass du das erst jetzt geändert hattest und das in V3.4.1 (oder V3.4.2) noch gar nicht drin war :slight_smile:
Scheint aber so wirklich ziemlich gut unseren Fall abzudecken!

Ich würde Vorschlagen für alle Datein, wo das aktuell noch nicht geht, das einfach trotzdem so zu lassen und damit zu leben. Das Abwarten bis die mittlere Bitrate bekannt ist und dann dynamisch die Position zu setzten gefällt mir nicht so arg :wink:

Habe den PR entsprechend aktualisiert:

Vielen Dank an @Wolle !
Gerne mal ausprobieren und Rückmeldung geben…

2 „Gefällt mir“

Ich teste das morgen mal.