Nach ca. eineinhalb Jahren kommt mal wieder ein etwas größeres Update für meinen Player. Auch wenn sich auf den ersten Blick nicht so sehr viel getan, bewerte ich das dennoch als Major-Update und wage den Sprung auf Version 5 - erstmal aber als Beta.
(Crossposting Entwickler-Ecke)
Nemp 5.0 (Beta)
Download bisher nur als Pre-Release auf Github, und auch nur als Zip, kein Installer.
Allgemeine Infos zum Update im Posting auf der ersten Seite. An dieser Stelle evtl. ein paar Hintergrund-Angaben zum Code, warum ich große Teile komplett neu geschrieben habe, und weswegen dann der Sprung auf Version 5 irgendwie doch gerechtfertigt ist.
Zustand in Nemp 3 und 4
Bisher konnte man in Nemp zwei Kriterien auswählen (z.B. Interpret und Album). Die Liste mit allen Dateien wurde nach diesen Kriterien sortiert, und daraus wurden zwei Listen generiert: Eine mit allen Interpreten, und eine zweite mit allen Alben. Diese beiden Listen wurden in zwei Treeviews ausgegeben. Nach Klick auf einen Interpreten wurde erstens eine Liste mit den Alben generiert und ausgegeben, an denen dieser Interpret beteiligt ist. Zweitens wurde eine Liste mit allen Dateien ausgegeben, bei denen dieser Interpret gesetzt ist. Nach Klick auf ein Album in der zweiten Liste wurde die Datei-Liste eingeschränkt auf das markierte Album.
Damit das schnell funktioniert, wurde ein komplexes System aus sortierten Listen und Binärsuchen verwendet. Es gab aber keinen direkten Zusammenhang im Code zwischen der Sortierung und der Binärsuche - weswegen es dabei immer wieder zu Problemen kam. Wenn die Vergleichsmethode bei der Sortierung und der Suche nicht exakt gleich ist, geht das nunmal in die Hose. Das war nicht wirklich wartbar, und machte auch anderweitig Probleme - z.B. bei verschiedenen Alben mit dem Titel "Best of".
Zusätzlich dazu gab es den Coverflow. Hier wurden die Dateien nach einer "Cover-ID" sortiert. Für die Anzeige des Album-Interpreten und des Album-Titels im 3D-Coverflow habe ich eine neue Klasse eingeführt - TNempCoverItem, welches diese Informationen aus der Liste der Titel auf diesem Album bestimmt hat. Damit hatte ich ein zweites Datenmodell für die Anzeige der Medienbibliothek.
Und dann war da noch die Anzeige in Form einer Tagwolke. Ich möchte hier gar nicht lang und breit erklären, was ich mir damals an dieser Stelle zusammengebastelt habe - im Grunde habe ich das Konzept von TDictionary (schlecht) nachgebaut. Aber das gab es "damals" noch nicht in Delphi. Oder es war mir unbekannt - daher was eigenes. Eine Hash-Tabelle mit fixer Größe, Kollisionsauflösung per Liste, sowas halt.
Damit hatte ich drei verschiedene Datenmodelle, um die Medienbibliothek anzuzeigen. Drei! Hinzu kamen noch diverse Klimmzüge für Webradio und Playlist-Verwaltung.
Neues Datenmodell - Eines für Alles
Damit ist jetzt Schluss. Das neue Datenmodell hat keine Probleme mehr mit Sortierung und Suche. Es gibt intern (fast) keinen Unterschied zwischen Coverflow und Baumansicht und Tagwolke. Daher kann man nun die Tagwolken-Tags in der Baumansicht aktivieren, und es können dort auch die Album-Cover eingeblendet werden. Und man muss sich nun nicht mehr auf eine Einteilung festlegen (auch wenn die zur Laufzeit geändert werden konnte), sondern man kann mehrere parallel nutzen. Interpret-Album und Verzeichnisse? Oder nur Alben? Oder eine tiefere Verschachtelung wie Erscheinungsjahr-Interpret-Album? Oder Genre-Erscheinungsjahr. Oder ...
Realisiert wird das über eine Klasse
TAudioFileCollection
, die neben einer Property
CollectionType
auch ein Dictionary
TDictionary<string, TAudioFileCollection>
enthält. Der CollectionType wird für die einzelnen Ebenen in einem Konfigurations-Objekt hinterlegt. Für das Einfügen einer Audio-Datei in eine AudioFileCollection wird je nach CollectionType ein Key aus dem Interpreten, dem Album, dem Erscheinungsjahr, dem Ordner, Genre gebildet. Dann wird die Datei in die passende Sub-AudioFileCollection im Dictionary eingefügt. Wenn der CollectionType leer ist, dann gibt es keine weitere Dictionary-Ebene, und die Datei wird einfach in eine
TObjectList<TAudioFile>
in der Collection eingefügt.
Für die Ausgabe in der Baumansicht können einfach alle Values des Dictionaries als Knoten ausgegeben werden. Wenn es ein Sub-Dictionary gibt, werden entsprechend Kind-Knoten mit allen Values des Sub-Dictionaries ergänzt. Bei Klick auf einen Knoten in der Baumansicht wird die Methode
SelectedCollection.GetFiles;
aufgerufen, die alle Dateien in der TAudioFileList ausgibt und ggf. durch rekursiven Aufruf von GetFiles auch alle Dateien der enthaltenen SubCollections im Dictionary.
Eine Methode
AudioFileCollection.Analyse;
analysiert bei Bedarf die Collection und ermittelt einen gemeinsamen Interpreten, Album-Titel, oder ein passendes Cover, falls es (abhängig vom Typ) sinnvoll ist. Diese Werte können als Caption oder Image in der Baumansicht genutzt werden, oder auch im Coverflow ...
Und für den Coverflow?
Ganz einfach: Eine AudioFileCollection vom Typ "Album", Ermittlung von Interpret, Album und Cover in
Analyse
, und Ausgabe aller Alben in dem Dictionary über
OpenGL. Die Verknüpfung von Anzeige und Daten geht hier etwas anders, aber das Modell im Hintergrund bleibt gleich.
Und für die Tagwolke?
Eine AudioFileCollection vom Typ "TagCloud". Hier gibt es allerdings eine paar Besonderheiten: Bei den anderen Typen gibt es einen eindeutigen Key pro Datei, hier gibt es mehrere - nämlich ein Key pro "Tag". Dadurch wird jede Datei in mehrere Sub-Collections eingefügt. Und während bei den anderen CollectionTypes die Verschachtelungstiefe klar definiert ist (oder zumindest wie bei Verzeichnissen klar begrenzt), kann hier die Verschachtelungstiefe beliebig groß werden. Daher werden SubCollections nur bei Bedarf erzeugt, und auch nur für den Tag, der grade aktiv betrachtet wird.
Und Playlisten? Und Webradio?
Auch das geht über Collections, allerdings über eine andere Ableitung vom gemeinsamen abstrakten Vorfahr
TAudioCollection
. Die Methode
GetFiles
funktioniert dann ein wenig anders, aber das Konzept bleibt.
Fazit: Besser wartbarer Code
bei deutlich mehr Funktionsumfang
, der auch noch schneller arbeitet
. Ganz perfekt ist Code sicherlich immer noch nicht, aber es ist eine deutliche Verbesserung zum Stand davor. Und man lernt ja
nie aus.
Soweit der Einblick in die Entwicklung der neuen Version. Und falls sich noch jemand wundert, warum die Anzahl der Kategorien
(eine weitere Neuheit, die ich hier nicht weiter erläutert habe) auf 32 beschränkt ist: Für die Zuordnung einer Datei zu einer Kategorie nutze ich Bitmasken.
Diese Beschränkung ist aber auch ausdrücklich gewollt. Eine feingliedrige Aufteilung ist hierbei nicht das Ziel gewesen.
Bug gefunden? Anmerkungen?
Fehler und andere Anmerkungen an mich. Antwort im Thread, PN, per Mail, ganz egal. Wer will, wird dann auch im About-Dialog eingetragen.
The angels have the phone box.