Hier gab es ja schon öfters Diskussionen zum Thema MP3-Player auf
ARM-Controllern.
Ich möchte mit Hilfe einer Black-Pill, bestückt mit STM32F411 und einem
I2S-DAC-Verstärker (MAX98357A) Sounds wie Tierlaute und kurze
Melodieschnipsel ausgeben. Ich habe nur wenig Speicherplatz (16MByte)
zur Verfügung. Da die Qualität ohnehin durch den verwendeten
Lautsprecher begrenzt ist, müßte MP3 mit 64kbit/s Mono, genügen. Besser
wäre natürlich AAC. Nun bin ich auf der Suche nach einer geeigneten
Decoder-Library, die idealerweise die Features des Arm M4 wie floating
point und DSP-Funktionen nutzt.
Die altbewährten Helix und MADlib scheinen schon seit langem nicht mehr
gepflegt zu werden, der Link zu helixcommunity.org ist tot, das letzte
Github-update von Madlib stammt aus dem Jahre 2004.
Welche Library würdet Ihr empfehlen, die mit den Ressoucen des STM32
(max. 100MHz, 128kB Ram) zurechtkommt?
Ich wünsche noch einen schönen Sonntag.
Ich weiß, dass das nicht ist, was du gefragt hast, aber: Was spricht
gegen eine SD-Karte mit WAV Dateien? Diese können quasi einfach
abgespielt werden ohne aufwendige Prozessierung.
Wenn es doch MP3 sein soll:
Mike schrieb:> Die altbewährten Helix und MADlib scheinen schon seit langem nicht mehr> gepflegt zu werden, der Link zu helixcommunity.org ist tot, das letzte> Github-update von Madlib stammt aus dem Jahre 2004.
Ist das ein Problem? Code altert nicht. Wenn das Featureset ausreichend
ist, warum nicht?
Mike schrieb:> Nun bin ich auf der Suche nach einer geeigneten> Decoder-Library, die idealerweise die Features des Arm M4 wie floating> point und DSP-Funktionen nutzt.
Floating Point wird für MP3 gar nicht gebraucht. Wäre aber egal, da du
die Library ja eh als Quellcode mitcompilierst und somit, wenn du mit
"hardfpu" compilierst FPU Befehle eingebaut werden. Die DSP
Funktionalitäten sind schon etwas schwieriger... So richtig hardcore
optimiert wird das nur, wenn man die selektiv als Programmierer
einstreut. Ansonsten ist man auf die Gnade des Compilers angewiesen,
dass er in etwa das baut, was man haben will. Das ist aber meist
tatsächlich nicht wirklich der Fall.
Je nachdem, was dein STM sonst noch machen soll, ist aber eine so starke
Optimierung gar nicht notwendig. Der STM hat genug Power, um eine
niederqualitative MP3 auch mit eher suboptimaler Implementierung
abspielen zu können.
Eine weitere Alternative wären die klassischen VSxxxx ICs von VLSI zum
MP3 dekodieren. Sind halt teuer und benötigen eventuell dann noch einen
kleinen Ausgangsverstärker.
Moin,
wie bereits gesagt, Code verdirbt üblicherweise nicht:
ich habe vor vielen Jahren(2014) das schöne Projekt/die Library von Uwe
Becker
54-CS43L22_MP3_USB-Library (STM32F4)
(https://mikrocontroller.bplaced.net/wordpress/?page_id=438(aktueller
link))
vom STM32F407-disco auf das STM32F401C-disco sehr leicht 'portieren'
können.
Verwendet wird der decoder helix(integer only, no float), der gut
funktioniert mit allen getesteten MP3s(verschiedene
Bitraten,'Soundarten').
Es ist also lediglich der MP3-decoder aus dem Projekt zu
'extrahieren'...
Deine 'schwarze Pille' steht ja mit den Ressourcen (100Mhz,128K Ram)
noch besser da als das 401-disco(84Mhz,64K Ram) und verwendet wohl auch
kein USB...
Gruss
Jürgen
Ich habe mir die STM XCubeAudio Library gezogen. Dort ist ein
MP3-Decoder namens Spirit enthalten. Auf den ersten Blick sehr schön, da
die Einbindung des Decoder sehr einfach erscheint. Es gibt nur zwei
Funktionen, eine zur Initialisierung, die andere zum häppchenweisen
Dekodieren. Er scheint auch keinen dynamischen Speicher zu allozieren
sondern verwendet ein festes Array als Workspace.
Leider bin ich an der Einbindung in mein Projekt gescheitert. Das Ding
existiert nur als vorkompilierte Library, der Quelltext ist nicht
verfügbar. Die Compileroptionen, mit denen die Bibliothek übersetzt
wurde, sind nirgends dokumentiert. Unsinnigerweise wurde bei der
Cortex-M4 Version -mfloat-abi=soft verwendet, so dass ich
Gleitkommarechnung in Hardware in meinem Projekt nicht mehr verwenden
kann. Mit der Krücke softfp hat es dann funktioniert. Leider hängt sich
bereits die Initialisierungsfunktion beim Aufruf auf, die Ursache läßt
sich mangels Quellcode nicht herausfinden. Eine Fehlerursache könnte
sein, dass die Bibliothek 2 byte breite wchar verwendet, der Rest des
Programmes aber 4 Byte. Umstellen mit -fshort-wchar geht leider nicht,
da es dann woanders (stdlib?) knirscht.
Die Dokumentation des Decoders an sich ist gut, die Implementierung
seitens STM äußerst lieblos.
@Jürgen Vielen Dank für den Link. Ich werde es noch mal mit Helix
probieren, da gibt es wenigstens Quellcode. DSP-Funktionen scheint er
tatsächlich in Form von 32x32->64 bit MAC - Instruktionen (SMLAL) zu
nutzen.
Schau mal ob Du minimp3 https://github.com/lieff/minimp3 zum Laufen
bekommst.
Das hatte ich hier auf einem Cortex-M7, braucht IIRC ca. 20KB Stack RAM.
Auf der Seite ist auch eine ältere Lib verlinkt die Integer Berechnungen
macht.
Den Unterschied MP3 als Integer vs. MP3 als float hört man aber deutlich
im direkten Vergleich, wenn die Sound Ausgabe einigermaßen rauschfrei
ist.
Ich habe jetzt mal den Helix-Decoder verwendet. Das Abspielen
funktioniert flüssig mit Bitraten von ca. 64kBit bei einem Prozessortakt
von 48MHz. Die Soundqualität ist unabhängig von der Bit-und Samplerate
leider recht mäßig, was vermutlich aber am Lautsprecher und nicht an der
Software liegt. Für meine Zwecke aber ausreichend.
Mit der Verwaltung des MP3-Eingangsdatenpuffers habe ich aber noch ein
Problem. Bei variablen Bitraten sind die MP3-Frames unterschiedlich
lang, es ist also nicht immer möglich, vorauszuberechnen, wann der
Puffer nachgeladen werden muss.Der Decoder rennt dann gegen das
Pufferende und gibt die Fehlermeldung ERR_MP3_INDATA_UNDERFLOW zurück.
Ich setze dann den Datenpointer auf den Frameheader zurück und starte
den Dekodiervorgang noch einmal. Das scheint zu funktionieren, ist aber
nicht sonderlich elegant. Gibt es eine Möglichkeit, die Framelänge vorab
auszulesen?
Mp3 und aac-adts hat einen Header mit fester Struktur und da drin steht
auch die Länge.
Aber es ist besser man hat einen eingangsspeicher der groß genug ist um
mehr als 3 frames zu speichern und den nach decodierung (wenn genug raus
ist) wieder auf füllen.