Entwickler-Ecke

Open Source Units - MP3FileUtils 0.6a


Gausi - Fr 11.11.05 14:59
Titel: MP3FileUtils 0.6a
MP3FileUtils, v0.6a

Diese Unit liest aus einer MP3-Datei alle (?) gängigen Informationen aus. Wie bei vielen anderen auch wird der ID3v1-Tag gelesen, fast alle Teile des ID3v2-Tags und diverse Informationen über die Datei ansich, wie z.B. Bitrate und Spieldauer.

Download:


Bugfix in 0.6a:
Da war ein fieser Copy&Paste-Bug in SetPersonalPlayCounter drin, wenn zur Bewertung einer Datei ein neuer Frame angelegt werden muss: Statt

Delphi-Quelltext
1:
2:
if aRating <> -1 then newFrame.SetRating(aEMail, newRating);
if aCounter <> -1 then newFrame.SetRating(aEMail, newCounter);

muss es natürlich heißen

Delphi-Quelltext
1:
2:
if aRating <> -1 then newFrame.SetRating(aEMail, newRating);
if aCounter <> -1 then newFrame.SetPersonalPlayCounter(aEMail, newCounter);

Und ich wunder mich die ganze Zeit, warum Nemp neue Alben beim Abspielen do schlecht bewertet... :autsch:

Änderungen in 0.6:


Folgendes leisten die in dieser Unit implementierten Klassen:
  1. TID3v1Tag:
    • Lesen und Schreiben des ID3v1-Tags
    • Unterscheidung von v1- und v1.1-Tags

  2. TMpegInfo:
    • Lesen/Berechnen der MPEG-Informationen eines mp3-Files - z.B. Bitrate, Channelmode, Samplerate, vbr/cbr, Dauer
    • Schnelles Ermitteln der Daten: Auch bei vbr wird die Spieldauer (fast) immer korrekt ermittelt, ohne die ganze Datei lesen zu müssen

  3. TID3v2Tag:
    • Lesen und Schreiben des ID3v2-Tags
    • Unterstützung aller existierenden Versionen: v2.2, v2.3 und v2.4
    • Unterstützung der Standard-Frames wie Titel, Artist, Album
    • Unterstützung einiger weiterer Text-Frames wie orig. Artist, Composer, Mediatype, Encoder
    • Lesen und schreiben von Lyrics, Bildern, Bewertungen, URLs
    • Lesen und schreiben beliebiger Daten
    • Unterstützung von Unsynchronisation, GroupIDs und Datalength-Bytes
    • Unterstützung von Unicode
    • Unbekannte Frames (~ Informationsfelder) im Tag bleiben erhalten

Für Details verweise ich auf die beiliegende Dokumentation und die verschiedenen Demoprojekte.

Evtl. benötigte Zusatzkomponenten/Units:
Nur für Delphi 2007 oder früher. Verwendung kann in der Datei Config.inc geändert werden.
Für Fehlermeldungen oder Codeoptimierungen bin ich jederzeit offen - hier im Thread, per PN oder per Mail.

Edit: Text an die neue Version angepasst.


retnyg - Fr 11.11.05 15:14

der code ist ja riesig ^^
kann mir nicht vorstellen dass das alles benötigt wird....naja

vielleicht könntest du die exe und die 2. kopie der unit weglassen dann wird die datei n stück kleiner


Gausi - Fr 11.11.05 16:26

user profile iconretnyg hat folgendes geschrieben:
der code ist ja riesig ^^
kann mir nicht vorstellen dass das alles benötigt wird....naja

Wenn man den Krempel weglässt, den man zur Unterscheidung der diversen v2-Tag-Versionen weglässt, und den Teil mit Bildern und Lyrics, und ein paar Kommentare, dann kann man sich einiges sparen, ja.
Die eine oder andere Funktion kann man sicherlich noch kürzen/optimieren, aber prinzipiell braucht man das alles. (Ok, die Language-Codes am Ende könnte man weglassen ^^) Wie gesagt: Für Code-Optimierungen bin ich jederzeit offen!

user profile iconretnyg hat folgendes geschrieben:
vielleicht könntest du die exe und die 2. kopie der unit weglassen dann wird die datei n stück kleiner
Zum schnellen Ausprobieren der Demo finde ich eine mitgelieferte exe eigentlich ganz praktisch, und so riesig ist der Download ja nun auch nicht. Selbst mit nem Modem dürfte das in 2 Minuten erledigt sein.


retnyg - Fr 11.11.05 21:15

auf delphi.about.com gibts nen mp3player mit eingebautem id3-editor zum download, der id3 code ist dort ca. 200 zeilen lang...
weiss jetzt allerdings nicht mehr auswendig, ob damit auch id3v2 funktioniert.


Delete - Fr 11.11.05 21:57

Und ich darf den Moderator noch mal daraufhinweisen, dass man verpflichtet ist, die Unit komplett als Source im Posting zu posten. :mrgreen:


Gausi - Fr 11.11.05 22:27

Der 200 Zeilen Code ist nur id3v1. Dieser ist sehr einfach gehalten und benötigt auch bei mir nicht wesentlich mehr.

@Luckie: Ich weiss. Ich nehme mir auch nicht das Recht heraus, den Code hier nicht direkt zu posten, weil ich Moderator bin, sondern weil der komplette Code sehr lang ist, und der Code auch bei eingeklappten Quelltext-Fenster komplett geladen wird.
Insgesamt hat die Unit gut 130kB, und das möchte ich einem Modem User nur ungern beim Laden einer Webseite zumuten. Daher habe ich von der Einhaltung dieser Regel Abstand gehalten.


Und nochmal zu dem zugegebenermaßen enormen Umfang des Codes: Da steckt auch ne ganze Menge drin. Mir ist bisher keine ID3-Unit über den Weg gelaufen, die alle Sub-Versionen des v2 Tags lesen und schreiben kann und das Lesen und Schreiben von Bildern und Lyrics unterstützt. Selbst die von mir hochgeschätzten AudioTools unterstützen nur v2.3 lesend und schreibend - die anderen Versionen werden nur gelesen. Und nicht erkannte Frames gehen dort beim Speichern verloren. (Zumindest in der Version, die ich kürzlich runtergeladen habe.)
Außerdem denke ich, dass auch die Kommentare recht ausführlich sind.


Daniel G - Mo 14.11.05 23:35

user profile iconGausi hat folgendes geschrieben:

Insgesamt hat die Unit gut 130kB, und das möchte ich einem Modem User nur ungern beim Laden einer Webseite zumuten. Daher habe ich von der Einhaltung dieser Regel Abstand gehalten.


Dafür dankt dir der Daniel :lol: .
Dolles Ding!


larshgf - Do 17.11.05 21:44

Hi everybody,

Sorry, but my german language is even vorse than my english.
I just want to thank the creator of this Mp3FileUtils. It's a fantastic work you've done here! I have only programmed a little with the mp3 files as object, and that was only concerning the ID3v1, which is - as you all probably know - a lot simpler than the complex ID3v2 data format.
I wonder if it's possible to build som unicode support into this mp3fileUtils? Maybee with the help from the free TNTWare by Troy Wolbrink in USA. (http://www.tntware.com/delphicontrols/unicode/). Have the author considered to do that?

Kindest Regards
Lars


Gausi - Do 17.11.05 22:07

Hi,

Yes, there is _some_ unicode support in this Unit. It is possible to read unicode-formatted strings from the tags. These strings are automatically converted to ANSII (or better: I hope they are...;-)) - just because the DelphiVCL (without third-party components) has no support for Unicode strings.

I did not found many mp3Files with Unicode-tags (and do not know how to search for them), so this is not well tested.
I tested it with some unicode-files, but the contained chars are only pseudo-unicode - one byte of the two-byte-chars was always $00. I have absolutely no idea what happens, when this is used with japanese or chinese mp3files.

It is (yet) not supported to write Unicode formatted strings to the tag. One of the reasons is: I dont know any taggers with full unicode support, so I cant test wether the things i write to the file are correct. And another: There is afaik no way to type unicode formatted strings into a VCl-component...


oern - So 12.02.06 18:40

Zitat:
Diese Unit liest aus einer MP3-Datei alle (?) gängigen Informationen aus. Wie bei vielen anderen auch wird der ID3v1-Tag gelesen, einige Teile des ID3v2-Tags und diverse Informationen über die Datei ansich, wie z.B. Bitrate und Spieldauer.


Wie lese ich denn die Spieldauer aus ? Das find ich irgentwie nicht :(


Gausi - So 12.02.06 18:45

Die Dauer steht nicht im ID3 Tag, sondern wird aus den MPEG-Headern ermittelt. Daher steckt die Dauer in den MpegInfos mit drin:

Delphi-Quelltext
1:
2:
3:
4:
mpegInfo := TMpegInfo.create;
mpeginfo.loadFromFile(meineDatei);
showmessage(inttostr(mpeginfo.dauer));
mpegInfo.Free;


oern - So 12.02.06 21:02

Danke klappt wunderbar :)


Ironwulf - Mo 13.02.06 19:04

unter delphi 3 kann ich das leider nich compilieren, das einige probleme gibt
z.b. hier:

Delphi-Quelltext
1:
TBuffer = Array of byte;                    


ich vermute mal das man sowas als dynamischen array bezeichnet="?" kp zumindest kann das mein delphi nit

oder der teil hier:

Delphi-Quelltext
1:
2:
3:
function GetFrameIndex(ID:integer):integer; Overload;
    function GetFrameIndex(ID:integer;Language:string;Description:string):integer; Overload;
    function GetFrameIndex(Description:TPictureFrameDesription):integer; Overload;


und mit Longword und Int64 kanns auch nichts anfangene aber ich glaub die könnte man notfalls noch durch normales word und integer ersetzen

gibts da alternativen für oder muss ich mir selbst eine ähnliche unit ausm kopf drücken


Gausi - Mo 13.02.06 19:21

Ja, TBuffer ist ein dynamisches Array, und die gabs bei D3 noch nicht. Evtl. kannst du das so umbauen, dass du da auch ne statische Größe dafür verwenden kannst. Hab das jetzt nicht genau im Kopf, wo ich welche Buffer-Länge brauche. Evtl. nimmst du anstelle eines Types TBuffer mehrere verschiedene, die sich in der Länge unterscheiden.
Die Int64-Variablen kannst du durch Integer ersetzen, bekommst dann aber unter Umständen Probleme bei großen Dateien, die theoretisch möglich sind (die Grenze war da glaube ich 2 oder 4 GB).
Die LongWords könnten Probleme machen, da z.B. die Funktion "Int28ToInt32" auf die interne Struktur des Longwords zurückgreift, um die Werte schnell zu konvertieren. Da musst du dann ggf. diese Routinen ändern, damit hinterher die Größenangaben im ID3v2-Header und den einzelnen Frames stimmt.

Also: das Umbauen auf D3 ist mit Sicherheit möglich, aber nicht trivial.


Jakob Schöttl - Di 11.07.06 07:56

Sehr gut!
Ich hab nur eine Frage dazu: Wenn ich nicht weiß, ob meine mp3 version 1 oder 2 ist, was mach ich dann. Zeigt Id3v2Tag auch die Tags einer Version 1 an?


Gausi - Di 11.07.06 08:33

Nein. ID3v1 und v2 sind ja nicht nur einfach unterschiedliche Versionen ein und deselben Dings, sondern vollkommen unterschiedlich in ihrem Aufbau, Umfang, und vor allem auch erstmal ihrer Position in der Datei.
Daher kann TID3v2Tag die Tags der Version 1 nicht anzeigen, da die beiden nichts gemeinsam haben (wenn man mal davon absieht, dass beide Konstrukte zur Speicherung gewisser Informationen genutzt werden). Das macht auch eigentlich keinen Sinn, denn:
Es können in einer mp3-Datei immer auch beide Versionen vorhanden sein - v2 am Anfang, und v1 am Ende der Datei. Ob und welche Tags vorhanden sind, kann man mit dem Exists-Flag abfragen, der True ist, wenn der Tag vorhanden ist.

Für eine bloße Anzeige der Meta-Informationen (z.B. in einem mp3-Player) ist evtl. dieser Beitrag [http://www.delphi-library.de/topic_MP3Informationen+auslesen_16228.html] noch hilfreich, wo ein System vorgestellt wird, das sich auch recht leicht auf andere Dateitypen (ogg, wma,..) erweitern lässt, ohne den eigentlichen Code des Players später verändern zu müssen.
Bei einen Tag-Editor sollte man einstellen können, welche Version (ggf. beide oder nur die vorhandene(n)) geschrieben werden soll. Außerdem ist da eine separate Anzeige beider Tags sinnvoll.

Nochwas: Deine "mp3 Version" dürfte fast immer 1 sein - aber das hat mit dem ID3-Tag nichts zu tun, sondern mit dem MPEG-Codec dahinter. Und der dürfte in 99% aller Fälle MPEG1, Layer 3 sein ;-)


Jakob Schöttl - Di 11.07.06 10:06

Ok, Vielen dank!
der Link hat mir schon geholfen. Ich muss mir den Quellcode noch genauer anschauen, vielleicht kann ich dann auch diese AudioTags schreiben, nicht nur lesen.


Gausi - Di 11.07.06 10:32

Zum schreiben kannst du die Routinen aus Mp3FileUtils benutzen. Wie man die benutzt, sollte in dem Demo-Projekt klar werden. Das Vorgehen sollte prinzipiell so sein:

- Lesen des alten Tags aus der Datei,
- Setzen/Ändern der Informationen,
- Schreiben in die Datei.

Zuerst Lesen ist deshalb dringend empfohlen, weil im ID3v2Tag jede Menge Informationen stecken können, die nicht "standard" sind. Diese würden verloren gehen, wenn du einfach einen neues Objekt TID3v2Tag erstellst, mit ein paar Informationen füllst und dann in die Datei reinschreibst.
Wenn du zuerst das vorhandene aus der Datei ausliest und dann einige Sachen änderst, bleiben die anderen Informationen erhalten.


Jakob Schöttl - Di 11.07.06 13:26

Ja, aber mein Problem ist ja auch wieder, dass ich nicht weiß, wann ich TId3v1Tag und TId3v2Tag zum schreiben der Tags nehmen muss. Wenn ich das wüsste, könnte ich ja auch gleich mit diesen Objekten die Tags lesen.


Gausi - Di 11.07.06 14:29

Hmmm...ich versuche, es mal von Anfang an zu erklären. Also. Eine mp3-Datei sieht - sehr stark vereinfacht - so aus:

Quelltext
1:
2:
3:
[optional] ID3-Tag (Version 2)
[immer] Audiodaten
[optional] ID3-Tag (Version 1)


Wenn du nun bei einer gegebenen mp3-Datei den ID3-Tag in der Version 1 auslesen willst, dann geht das mit dieser Klasse hier ganz einfach so:

Delphi-Quelltext
1:
2:
3:
meinID3v1Tag := TID3v1Tag.create;
// evtl. heißen die Routinen etwas anders
meinID3v1Tag.ReadfromFile('c:\meinmp3.mp3');

Version 2 geht ganz analog:

Delphi-Quelltext
1:
2:
3:
meinID3v2Tag := TID3v2Tag.create;
// evtl. heißen die Routinen etwas anders
meinID3v2Tag.ReadfromFile('c:\meinmp3.mp3');

Wenn du wissen willst, ob überhaupt ein ID3-Tag vorhanden ist, kannst du anschließend sowas machen:

Delphi-Quelltext
1:
2:
if meinID3v1Tag.exists then showmessage('ID3-Tag in Version 1 vorhanden');
if meinID3v2Tag.exists then showmessage('ID3-Tag in Version 2 vorhanden');

Zum Schreiben kannst du dann entweder den Tag nehmen, der schon vorhanden ist, oder einfach immer v1 oder immer v2 nehmen. Das is vollkommen egal. Beide Tags können in jede mp3-Datei geschrieben werden. Es muss keiner geschrieben werden, und welche Version, ist vollkommen dir (bzw. besser dem User) überlassen.
Ein meinID3v2Tag.WriteToFile('c:\meinmp3.mp3') schreibt das Objekt an den Anfang der Datei genauso wie ein meinID3v1Tag.WriteToFile('c:\meinmp3.mp3') ein etwas anders aufgebautes Objekt ans Ende schreibt. Das die Informationen in diesen Objekten, die da geschrieben werden, einigermaßen sinnvoll sind, dafür ist der Programmierer der Enduser-Software verantwortlich. Es ist mit den Methoden aus MP3FileUtils durchaus so etwas möglich:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
meinID3v1Tag := TID3v1Tag.create;
meinID3v2Tag := TID3v2Tag.create;
meinID3v1Tag.ReadFromFile('C:\Klassik\Eine kleine Nachtmusik.mp3')
meinID3v2Tag.ReadFromFile('C:\Kinderlieder\Hänschen klein.mp3');
meinID3v1Tag.WriteToFile('C:\Metal\Metallica\Nothing Else Matters.mp3');
meinID3v2Tag.WriteToFile('C:\Metal\Metallica\Nothing Else Matters.mp3');

Aber ob das nun sinnvoll ist, ist eine ganz andere Frage. Das Ergebnis wäre hier wahrscheinlich, dass die Datei im Autoradio als "eine kleine nachtmusik" erscheint, und in Winamp als "Hänschen klein", obwohl beidesmal "Nothing else matters" gespielt wird.

Daher empfiehlt sich z.B. sowas:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
//alte Tags lesen
meinid3v1.ReadfromFile('c:\meinmp3.mp3');
meinid3v2.ReadfromFile('c:\meinmp3.mp3');
// Informationen setzen
meinid3v1.Titel := Edit1.text;
meinid3v2.Titel := Edit1.text;
meinid3v1.Artist := Edit2.text;
meinid3v2.Artist := Edit2.text;
// schreiben
meinid3v1.WriteToFile('c:\meinmp3.mp3');
meinid3v2.WriteToFile('c:\meinmp3.mp3');


Alternativ zu den letzten beiden Zeilen kann man auch sowas machen:

Delphi-Quelltext
1:
2:
3:
4:
5:
if meinid3v1.exists then meinid3v1.WriteToFile('c:\meinmp3.mp3');
if meinid3v2.exists then meinid3v2.WriteToFile('c:\meinmp3.mp3');
//oder auch:
if WriteID3v1Checkbox.Checked then meinid3v1.WriteToFile('c:\meinmp3.mp3');
if WriteID3v2Checkbox.Checked then meinid3v2.WriteToFile('c:\meinmp3.mp3');

Ich hoffe, es wird langsam etwas klarer ;-)


Jakob Schöttl - Mi 12.07.06 13:13

Super, Vielen vielen Dank Gausi.
Da hast du dir jetzt viel mühe gegeben, und ich habs auch super verstanden.

Ich brauch das übrigens für ein Programm, das alle MP3s sucht, und eine ExcelTabelle anlegt (mit OLE). Später will ich, dass man die Daten auch in Excel ändern kann, und diese dann von meinem Programm wieder in die MP3s geschrieben werden.


Gausi - Mo 14.08.06 15:25

Gewissermaßen als Nebenprodukt bei der Weiterentwicklung meines MP3-Players [http://www.delphi-forum.de/topic_MP3Verwaltung_35607.html] ist eine neue Version dieser Unit entstanden.

Warum es diesmal direkt zwei Varianten gibt, ist etwas schwer zu erklären. Der Unterschied kommt nur dann zum Vorschein, wenn man auch "exotische" MP3-Dateien damit bearbeiten will. Also z.B. koreanische, russische oder hebräische.
Benutzen die ID3v2Tags Unicode für die Textinformationen, so sind beide Versionen gleichwertig. Benutzen sie aber den lokalen Zeichensatz (was eigentlich nicht so sein sollte), dann kann die INT-Version diesen entsprechend konvertieren - entweder so, wie es der User vorgibt (vorausgesetzt ist eine passende Eingabemöglichkeit für die Option im Programm), oder aber bis zu einem gewissen Grad automatisch anhand des Dateinamens (wenn dieser Unicode-Zeichen enthält, und gewisse Parameter übergeben werden, die der User der Endanwendung ebenfalls eingeben können sollte).
Dafür ist aber eine recht umfangreiche De- und Entkodierungs-Unit nötig, die alleine das Programm um ca. 200-300kb größer macht. Daher gibt es auch eine abgespeckte Version.

Ich hoffe, dass es die Demos den Unterschied klar machen, und wenn nicht, dann hilft evtl. ein Blick in die mp3s mit einem Hexeditor. Wichtig dabei ist die "00" oder die "01" unmittelbar vor den Textinformationen. (Also in den Demo-mp3s das siebte Byte nach den Frame-IDs "TIT2", "TPE1", "COMM" und "TALB").
Steht da eine "01", kann man die WE-Version benutzen. Steht da eine "00", und dahinter unlesbarer Krempel, dann braucht man die INT-Version.

Für "einheimische mp3s" reicht die WE-Version locker aus und bietet keinen Voreil gegenüber der INT-Version.

Download wie immer im ersten Beitrag.


Calculon - Do 14.09.06 02:22

Hallo Gausi!
Ich habe eine Frage bzgl. der Bilder, die mit einer mp3-Datei verknüpft werden. Seit dem ich den Windows Media Player 11 beta benutze, lädt der Player von irgendeiner Quelle automatisch die Cover meiner mp3-Lieder herunter. Da das überraschend gut funktioniert wurde ich sofort ein Fan dieses Features. Ich dachte die Bilder werden mit in den Tags abgespeichert. Aber mit deinem Tool kann man sie nicht mehr auslesen. Auch mit anderen Playern, wie z.B. winamp konnte ich den mp3-Files die Bilder nicht mehr entlocken. Nur interessehalber frage ich, weißt du wie das beim WMP funktioniert und vor allem, wo die Coverbilder abgelegt werden?
Gruß


Gausi - Do 14.09.06 08:57

Wo der Windows-Media-Player die Cover her hat, weiß ich nicht. Wahrscheinlich gibt es auf irgendeinem MS-Server eine recht umfangreiche Datenbank, auf dem für jedes Album ein entsprechendes Cover hinterlegt ist. Möglicherweise holt er sich die von einem Onlineshop, mit dem MS zusammenarbeitet. Zumindest steht in der 10er Version über dem Cover, was angezeigt wird "Bei MSN Music kaufen"

Wo der WMP die Bilder ablegt, weiß ich noch weniger. Kann der WMP die Bilder aus den mp3s auch auf einem anderen Computer (ohne Verbindung ins Netz) anzeigen? Wenn dem nicht so ist, dann werden die Bilder wahrscheinlich nur irgendwo in der Medienbibliothek des Players gespeichert, oder sogar gar nicht - d.h. das geht nur mit Verbindung ins Netz.

Wenn du da keine Testmöglichkeit hast, oder dir nicht sicher bist, kannst du mir gerne auch ein mp3 mit Bild per Mail schicken, ich werde mir das dann mal genauer ansehen. (Wenn du Angst vor Raupkopiererei und Knast hast, kann ich dir gerne eine Liste mit meinen CDs schicken, evtl. gibt es da Überschneidungen.)
Möglicherweise speichert der WMP die Bilder in einem Format in die Tags ab, die ich nicht verarbeiten kann. Ich habe die Beta 11 nicht, und werde sie mir deswegen auch nicht installieren. Da gibt es bessere Player, die eher meinen Bedürfnissen entsprechen.


Gausi - Do 14.09.06 12:11

Es tut mir unendlich leid, aber die Version 0.2 hatte einen groben peinlichen Schnitzer, der möglicherweise den gesamten ID3v2-Tag unbrauchbar machte. :oops:

Den Fehler habe ich heute durch Zufall entdeckt, und er sollte nun behoben sein. Beim Umstellen auf Unicode habe ich an einer Stelle eine Codezeile aus Versehen gelöscht oder vergessen. Sie ist nun wieder drin.


0xCC - Sa 16.09.06 16:39

hi, wie wäre es compilerschalter bzgl TNT-Kompos einzubauen ?


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
// WE UNIT
{$DEFINE USE_TNT_COMPOS}
uses
  SysUtils, Classes, Windows {$IFDEF USE_TNT_COMPOS}, tntcompo1, tntcompo2{$ENDIF};

type
  {$IFNDEF USE_TNT_COMPOS}
  TTNTFileStream = TFileStream;
  {$ENDIF} 
//--------------------------------------------------------------------   }
// Teil 1: Allgemeine Hilfstypen
//--------------------------------------------------------------------

...

//zeile 606
{$IFNDEF USE_TNT_COMPOS}
function  WideFileExists(s:string):boolean;
begin
  result := fileexists(s);
end;
{$ENDIF}


so könnte man mit einer simplen änderung {.$DEFINE USE_TNT_COMPOS} die nutzung der TNTs abschalten.

Nachtrag:
übrigens wird durch den verweis auf die unit im dpr-file deines demoporgrammes die compilierung erst möglich, wenn man die unit in das selbe verzeichnis wie das demo-projekt kopiert, nicht aber wenn sie in einem bibliothekspfad vorliegt.


Gausi - Sa 16.09.06 17:20

In der DP kam auch schon der Vorschlag beide Versionen über Compilerschalter zu vereinen, da sie sich auch nur an einigen wenigen Stellen unterscheiden.
Das würde ich dann hiermit kombinieren, wenn ich mal wieder Zeit und Lust habe, mich an diese Unit dranzusetzen...


terryk - Mi 27.09.06 09:34
Titel: "Fehler" in TMpegInfo.LoadFromStream
Hallo!
Ich habe ein Problem in MpegInfo.LoadFromStream gefunden. Bei bestimmten MP3-Files wird eine Exception (Fehler bei Bereichsprüfung) in

Delphi-Quelltext
1:
2:
3:
    // Schritt 1: Test, ob hier ein Mpeg-Header beginnt.
    // ---------------------------------------------------------------------------
    tmpMpegHeader := GetValidatedHeader(Buffer, bufferpos);

ausgelöst. Anscheinend liegt es daran, dass in der Routine GetValidatedHeader der "Beginn" des MPEG-Headers fälschlicher weise erkannt wird und dann bitrateindex=0 ist, was die Exception in

Delphi-Quelltext
1:
Result.bitrate := MPEG_BIT_RATES[Result.version][Result.layer][bitrateindex];                    

auslöst. Der Fehler tritt nur dann auf, wenn bufferpos=0 ist, also von Anfang an gesucht wird, ohne vorher (wie in der Beispielanwendung) den ID3v2-tag zu lesen. Im Beispiel NormaleDemo funktioniert es, weil in BtnShowInfosClick folgende Reihenfolge eingehalten wird, der Tag existiert und somit die bufferpos auf Id3v2Tag.size statt auf 0 gesetzt wird:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
  // ID3v2-Tag auslesen
  Id3v2Tag.ReadFromStream(FS);

  // Position neu setzen
  if Not Id3v2Tag.exists then                                <= Ja, der Tag existiert
    FS.Seek(0, sobeginning)
  else
    FS.Seek(Id3v2Tag.size, soFromBeginning);                 <= Position des Streams NICHT null

  // MPEG-Informationen lesen
  MpegInfo.LoadFromStream(FS);                               <= Auslesen ist OK

Wenn man aber nur die MPEG-Infos will und im gleichen Beispiel folgendes macht, geht es bei manchen MP3s schief:

Delphi-Quelltext
1:
2:
  // MPEG-Informationen lesen                                // Ohne vorher ID3-Tag lesen, StreamPosition am Anfang=0
  MpegInfo.LoadFromStream(FS);                               <= Auslesen erzeugt manchmal Exception


Wie kann man den MPEG-Header in GetValidatedHeader verlässlicher erkennen?

Gruß und danke!


Gausi - Mi 27.09.06 09:55

user profile iconterryk hat folgendes geschrieben:
Wie kann man den MPEG-Header in GetValidatedHeader verlässlicher erkennen?
Eine Möglichkeit wäre, nach dem Bestimmen von result.version das einzufügen:

Delphi-Quelltext
1:
2:
3:
4:
5:
if result.version=0 then
begin
  result.valid := false; // Bad Value !
  exit;
end;

Allerdings ist es sehr empfehlenswert, immer zunächst den ID3-Tag zu lesen, aus folgenden Gründen:

Zu deinem Problem: Ich werde das wohl demnächst mal ausbessern, würde dich aber bitten, mir ein Beispiel-mp3 zu schicken (per PN oder eMail), wo das Verfahren bisher schiefgeht und diese Exception auftritt.


terryk - Mi 27.09.06 11:33

Hallo!
Habe die MP3 als PN geschickt, ist leider 11MB groß.
Die Prüfung auf die Version alleine reicht nicht, da in diesem Fall (der geschickten MP3) das Byte nach $FF nämlich $E0 ist und die Version 3 (bzw 2.5) und somit gültig ist. Ich prüfe deshalb auf die zwei nach MPEG-Standard reservierten Bit-Kombinationen BB=01 für die Version und CC=00 für den Layer (dieser wird dann 4-0=4).

Also folgender Workaround als Vorschlag (damit lösen meine MP3-Files keine Exceptions mehr aus!):


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
function TMpegInfo.GetValidatedHeader(aBuffer: TBuffer; position: integer): TMpegheader;

   ... bla bla ...

  Result.Layer := 4-((abuffer[position+1shr 1and 3);
  Result.protection := (abuffer[position+1AND 1)=0;

// 27.09.2006 TK >>
  if (Result.version = 0or (Result.Layer = 4then
  begin
    Result.valid := False;
    Exit;
  end;
// 27.09.2006 TK <<

  // Byte 3: EEEEFFGH
  // E: Bitrate-Index

   ... bla bla ...

Gruß


terryk - Mo 30.10.06 11:57
Titel: Bug + Bugfix in TID3v2Tag.ReadFrames
Hallo Gausi!
Habe in MP3FileUtils_WE v0.2a (Westeuropa) noch einen Bug gefunden, der zu Fehlern bei Bereichsprüfung führt. Bugfix siehe Code unten.
Gruß


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
function TID3v2Tag.ReadFrames(From: LongInt; Stream: TStream): TMP3Error;

   ... bla bla ...

        with FFrames[newframeIDX] do // im Array speichern
        begin
          // Header kopieren
          setLength(Header,length(aHeader));
          move(aHeader[0], Header[0], length(Header));
          // Daten kopieren
// 30.10.2006 TK >>
          //SetLength(Data, FrameSize);             // org. Code   
          //Stream.ReadBuffer(Data[0], FrameSize);  // org. Code <= Fehler, wenn 
                                                    //              FrameSize=0 !!
          Data := nil;                              // Bugfix
          if FrameSize > 0 then                     // Bugfix
          begin                                     // Bugfix
            SetLength(Data, FrameSize);             // Bugfix 
            Stream.ReadBuffer(Data[0], FrameSize);  // Bugfix
          end;                                      // Bugfix
// 30.10.2006 TK <<
          //Im Gegensatz zu UltimaTag ist hier das Encoding-Byte enthalten!!!}
          // restliche Infos setzen
          FFrameID := FrameIDstr;
          FSize := FrameSize;
        end;

   ... bla bla ...

end;


Gausi - Mo 30.10.06 12:08

Danke, das werde ich demnächst einbauen. Allerdings ist das nicht wirklich ein Bug, der zu einer fehlerhaften Darstellung führt. Zumindest nicht bei korrekten Tags, denn:
http://www.id3.org hat folgendes geschrieben:
A tag must contain at least one frame. A frame must be at least 1 byte big, excluding the header.


Da man davon aber natürlich nicht ausgehen sollte, ist die Meldung durchaus berechtigt. (In der anderen Version dürfte der Fehler übrigens auch drin sein ;-))


terryk - Mo 30.10.06 12:44
Titel: Noch ein Bugfix
Hallo Gausi!
Ja, Probleme gibt es i.d.R. nur bei eingeschalteter Bereichs-/Überlaufprüfung, aber manchmal hängt mein Programm deswegen. ich habe noch einen anderen "Fehler" gefunden und behoben; im XingHeader wird auf Felder in abuffer zugegriffen, die größer als dessen Länge sind (siehe Code unten):


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
function TMpegInfo.GetXingHeader(aMpegheader: TMpegHeader; aBuffer: TBuffer; position: integer ): TXingHeader;

  ...bla...

// 30.10.2006 TK >>
  if Length(abuffer) <= (position + xing_offset + 11then
  begin
    Result.valid := False;
    Exit;
  end;
// 30.10.2006 TK <<

  if (abuffer[position+xing_offset]=$58)        {'Xing'}
     AND (abuffer[position+xing_offset+1]=$69)
     AND (abuffer[position+xing_offset+2]=$6E)
     AND (abuffer[position+xing_offset+3]=$67)
     then // Xing Tag vorhanden
  begin

  ...bla...
end;


Gausi - Mo 30.10.06 13:17

Wenn du so weiter machst, kennst du dich bald besser in meinem Code aus als ich :oops:.

Wenn ich die nächste version veröffentliche, werde ich wohl das eine oder andere von dir einbauen - mit einem entsprechendem Hinweis natürlich :D

Wobei es mir zur Zeit bei dem Problem von eben (Framsize = 0) sinnvoller erscheint, dann den Datenteil künstlich auf 1-Byte aufzublasen. Sonst könnte es unter Umständen an anderer Stelle zu Problemen führen. Aber dafür müsste ich noch malgenauer in den Code gucken :nixweiss:.


Manina - Di 21.11.06 02:22

Hallo Gausi !

Etwas ähnliches passierte mir nach folgendem Szenario:

1. Auswahl einer mp3.Datei
2. Löschen v1 und v2-Tag --> "Clear all tags" :)
3. Speichern
4. Anhängen von "Cover(front)" und "Cover(back)" (ok. waren ziemlich große Jpeg's... btw. spielt die Größe der Bilder eigentlich eine Rolle ?)
5. wieder speichern
6. Programm beenden --> nur die beiden Bilder im Id3v2-Tag, sonst nix...
7. Programm neu starten und gleiche Datei auswählen --> Bilder wurden angezeigt
8: Id3v2-Tag löschen ----->>>> INTEGERÜBERLAUF

Der Debugger sagte mir, in
TID3v2Tag.RemoveFromStream
kam die Exception an der Stelle
// shorten the file by the size of the tag
Stream.Seek(-FTagSize, soFromEnd);

Ich habs behoben, indem ich sowohl "private FTagSize" als auch "property size" von LongWord zu Int64 gemacht habe.... Allerdings weiß ich jetzt net, hängt die Exception nur mit der eingeschalteten Überlaufprüfung zusammen, oder wäre es nicht besser, generell zu Int64 zu wechseln bei den heutigen Dateigrößen, gerade von Bildern ?

Gruß, Bernd.


Manina - Di 21.11.06 02:27

Achja noch ne Frage :oops:

Unbekannte Frames bleiben ja erhalten (so sie denn nicht Framesize = 0 haben :D ). Wie kann ich feststellen, WELCHE unbekannten Frames er denn so findet ? Mir sind so Sachen wie PRIV, WOAS, WOAR usw. untergekommen - die hätte ich gerne einfach in einer Liste angezeigt, daß sie da sind, aber halt net gehandled werden...

Gruß Bernd.


Gausi - Di 21.11.06 09:49

Zu den JPEGs: Prinzipiell spielt die Größe der Bilder keine Rolle. Ich muss aber nochmal genauer nachsehen, ob die maximal erlaubte Framegröße evtl. überschritten werden kann. Die dürfte aber auch bei mehreren MB liegen.
Ein Ändern von LongWord zu Int64 ist unnötig, wenn nicht sogar gefährlich. Die Größenangaben von Tag und Frames im ID3v2Tag sind nunmal 4Byte groß - der enstprechende Datentyp in Delphi ist dann LongWord. Dieser geht laut OH von 0..4,294,967,295. D.h. man bekommt erst bei Bildgrößen von 4 Gigabyte Probleme (eigentlich schon bei ca. 500MB, da im Tag die Integer nur mit 28 Bit gespeichert werden).
Ob der Fehler nun an der Überlaufsprüfung liegt, oder ob (und wenn ja wo genau) da ein Fehler steckt, kann ich jetzt nicht auf Anhieb sagen. Die Stelle (und ein paar andere auch) wollte ich bei Gelegenheit aber sowieso ändern.

Zu den unbekannten Frames: Ja, da könnte man evtl. was machen. WOAS, WOAR sind iirc URLs (die man eigentlich sowieso einbauen könnte :oops:). PRIV sind Private Frames und können mit einer entsprechenden Beschreibung mehrfach vorkommen. Eine mögliche Auflistung der nicht verarbeiteten Frames ist eine gute Idee.


Manina - Di 21.11.06 11:30

nochmal zur TagSize:

Vielleicht sollte man Beides kombinieren ? Die internen Variablen kann man durchaus zu Int64 machen, damit es keine Compilerprobleme wie Überlauf usw. gibt. Denn was würde passieren, wenn zB. FTagSize >= $80000000 ist ? Ein

Stream.Seek(-FTagSize, soFromEnd)

pointed dann doch nur ein "paar" Bytes vom Dateiende rückwärts... :?
Ich glaube, genau das ist mir passiert. Daher der Gedanke mit den langen Integers.

Beim Hinzufügen neuer Frames muß man dann halt darauf achten, daß die "echte" FrameSize von 32 Bit nicht überschritten wird.

Gruß, Bernd


Gausi - Di 21.11.06 11:55

Hmm... Ich hab das nochmal mit dem Testprojekt ausprobiert. Ich habe zwei Bilder mit je rund 2MB eingefügt und wieder gelöscht - ohne Probleme.

Hattest du das Problem bei Benutzung eines der Demo-Projekte, oder bei einem eigenen? Wie groß waren die Bilder? Kannst du den Fehler reproduzieren, und mir evtl. eine Kopie der Datei mit dem Bilder-Tag, der zu dem Überlauf führt per Mail schicken?
ich vermute nämlich fast, dass da beim Schreiben des Tags irgendwas schiefläuft. :nixweiss:


Manina - Di 21.11.06 12:20

Gerade merke ich, der Fehler hat gar nix mit irgendwelches Bildern zu tun... :shock:

Also...

Ich habe Deine "Normal-Demo" benutzt; in den Projektoptionen der IDE sind alle Prüfungen eingeschaltet. Das Szenario war jetzt:

1. Datei auswählen (Datei enthält gar keine Bilder !)
2. Id3v1-Tag löschen (id3v1Tag.RemoveFromFile(Edit1.Text))
3. Zeige Infos (TForm1.BtnShowInfosClick(Sender: TObject)) --> v1-Tag ist leer
4. Id3v2-Tag löschen (id3v2Tag.RemoveFromFile(Edit1.Text)) --> INTEGERÜBERLAUF, obwohl TagSize nur 1627 ist (???)

Wieder passiert es beim Stream.Seek(-FTagSize, soFromEnd);

Die Datei schick ich Dir per Mail... Sie enthält, so wie ich beim Laden gesehen habe, außer den üblichen Frames nur einen WXXX der Länge 2.

Wohlgemerkt, ich wollte keine einzelnen Frames löschen, sondern den kompletten v2-Tag. :nixweiss:


Manina - Di 21.11.06 15:48

ich nerve schon wieder...

Ich glaube, wir kommen nicht um ein Typecasting herum, um Laufzeitfehler auszuschließen.
Allerdings können die Variablendefinitionen so bleiben, wie sie sind. :wink:

Ich hab mir mal folgendes überlegt und getestet und es scheint wirklich so zu sein :roll:

Angenommen, FTagSize (LongWord !) ist 77824 (dec), also 13000 (hex). Dann bedeutet Stream.Seek(-FTagSize, ...) also
(-77824) (dec) bzw. FFFED000 (hex). Da aber LongWord, wird das als vorzeichenlos interpretiert, macht also 4.294.889.472 (dec).
Das sprengt beim Seeken sicher jeden Rahmen :wink:

Ich hab jetzt nur mal bei RemoveFromStream so gecastet:

Stream.Seek(-Int64(FTagSize), soFromEnd);

Damit klappts, egal wie die Prüfoptionen gesetzt sind, weil die 64-Bit-Variante der Seek-Procedure aus TStream benutzt wird. :)
Nur war ich zu faul, die komplette Unit nach solchen Stellen zu durchforsten; das ist Dein Job, Gausi... :D

Ich hoffe, auf diese Art sind wir alle zufrieden...

Gruß, Bernd.


Gausi - Di 21.11.06 16:19

Ich hab mir die Datei (die aus der Mail) mal angeschaut. Der Tag scheint soweit in Ordnung zu sein, wenn auch diverse leere Frames drin sind. Aber das macht ja nix. Es liegt also nicht an einem fehlerhaften Tag, an dem sich die Unit verschluckt.

Ich kann den Fehler bei mir aber nicht reproduzieren. :nixweiss: Ich hab jetzt auch ein bissel in den Debugger-Optionen rumgespielt, aber immer dasselbe Ergebnis: Es funktioniert ohne Fehlermeldung. Den v1-tag löschen ist sinnfrei, weil der gar nicht da ist. Und der v2-Tag wird anstandslos und korrekt entfernt.

Erläuter mal bitte Schritt für Schritt, was du wo für Optionen in der IDE gesetzt hast. So wie ich das sehe, sollte eigentlich bei etwas wie seek(-MyLongWordVar) der Parameter als negativer Wert aufgefasst werden. Wenn ich allerdings soetwas mache: Stream.Seek(LongWord(-FTagSize), soFromEnd);, dann bekomme auch ich einen Integerüberlauf...


Manina - Di 21.11.06 17:05

es geht nur um die Compilerschalter {$R} (Bereichsüberprüfung) und {$Q} (Überlaufprüfung), die ich unter

Projekt / Optionen / Compiler

an- oder ausschalte. Der Konstrukt


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
        try
          TmpStream.Seek(0, soFromBeginning);
          if StreamToStream(Stream, TmpStream, Stream.Size -
                                Stream.Position) <> MP3ERR_None then
          begin
            result := ID3ERR_Cache;
            Exit;
          end;

          // shorten the file by the size of the tag
          Stream.Seek(-FTagSize, soFromEnd);
          SetEndOfFile((Stream as TFileStream).Handle);

          // return the audio data to the stream
          Stream.Seek(0, soFromBeginning);
          TmpStream.Seek(0, soFromBeginning);
          if StreamToStream(TmpStream, Stream, TmpStream.Size) <> MP3ERR_None then
          begin
            result := ID3ERR_Cache;
            Exit;
          end;
        except
          on EWriteError do result := MP3ERR_SWrite;
          on E: Exception do
          begin                              // hier lande ich .....
            result := ID3ERR_Unclassified;
            MessageBox(0, PChar(E.Message), PChar('Error'), MB_OK or MB_ICONERROR or MB_TASKMODAL or MB_SETFOREGROUND);
          end;
        end;


in TID3v2Tag.RemoveFromStream landet immer in Zeile 24, außer ich setze beide Schalter auf aus.

{$R+}, {$Q+} --> "Fehler bei Bereichsprüfung"
{$R+}, {$Q-} --> dito, {$R} hat also scheinbar Vorrang
{$R-}, {$Q+} --> "Integerüberlauf"
{$R-}, {$Q-} --> alles i.O.

:nixweiss:

Das hier sind die Standardeinstellungen in meiner IDE:


jakobwenzel - Do 01.02.07 17:44

Ich nutze deine Unit auch, und finde, dass die wirklich "das beste auf dem Markt" ist.

Ich hab nur ein Problem: Bei meinem aktuellen Projekt versuche ich, die Angewohnheit von iTunes, Coverbilder in allen möglichen (für MP3-Player nicht lesbaren) Formaten abzuspeichern, auszubügeln. Das klappt schon ganz gut, jedoch wird am Schluss der Lieder immer ein Stück wiederholt, ich vermute mal, es liegt daran, dass die Bilder vorher BMP waren und jetzt kleiner geworden sind und da irgendwas durcheinanderkommt. :nixweiss:

Hier mein (ziemlich wirrer) Code:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
procedure ConvertPic(stream:TMemoryStream; delete:Boolean=false);
var
  tag:TID3v2Tag;
  PictureDescriptions: TPictureFrameDescriptionArray;
  i:Integer;
  sList:TSTringList;
  jpg:TJPEGImage;
  picture:TPicture;
  picStream:TMemoryStream;
begin
  picture:=TPicture.Create;
  tag:=TID3v2Tag.Create;
  sList:=TStringList.Create;
  try
    stream.Position:=0;
    tag.ReadFromstream(stream);
    if tag.exists then
    begin
      //Load Picture
      PictureDescriptions := Tag.GetAllPictureFrameDescriptions;
      sList.Clear;
      for i:=0 to length(PictureDescriptions)-1 do
        try
          sList.Add(Picture_Types[PictureDescriptions[i].bildtyp] +  ' [' + PictureDescriptions[i].Description + '] ');
        except
          sList.Add(Picture_Types[0] +  ' [' + PictureDescriptions[i].Description + '] ');
        end;
      if sList.Count > 0 then
      begin
        PicStream := TMemoryStream.Create;
        try
          Tag.GetPicture(PicStream, PictureDescriptions[0]);
          PicStream.Seek(0, soFromBeginning);
          try
            LoadPic(PicStream,Picture);
          except
            Picture.Assign(NIL);
          end;
        finally
          PicStream.Free;
        end;

        //Delete pic?
        if delete then
        begin
          for I := 0 to sList.Count do
            Tag.DeletePicture(PictureDescriptions[i]);
          Stream.Position:=0;
          Tag.WriteToStream(stream);
        end
        else
        begin
          PicStream:=TMemoryStream.Create;
          //Convert raw data to JPEG
          jpg:=TJPEGImage.Create;
          jpg.Assign(Picture.Bitmap);
          jpg.SaveToStream(PicStream);
          PicStream.Position:=0;
          Tag.SetPicture(PicStream,PictureDescriptions[0]);
          Stream.Position:=0;
          Tag.WriteToStreaM(Stream);
          jpg.Free;
          PicStream.Free;
        end;
      end
      else begin
        Picture.Assign(NIL);
      end;
    end;
  finally
    tag.Free;
    sList.Free;
    picture.Free;
  end;
  stream.Position:=0;
end;

LoadPic ist ne selbstgeschriebene Prozedur, die alle möglichen Grafikformate in ein TPicture lädt. (Ich glaub nich, dass da der Fehler liegt, deshalb hab ich den Code jetz nich gepostet)


Gausi - Do 01.02.07 18:04

Hmm... da habe ich eine ganz böse Befürchtung :gruebel:

Du bearbeitest also die Tags so, dass sie (signifikant) kleiner werden, und wenn du speicherst, dann ist das mp3-File insofern beschädigt, dass hinterher beim abspielen am Ende was doppelt abgespielt wird?

Würdest du mir evtl. ein Beispiel-Lied mailen, und am besten auch eine kompilierfähige Version deines Projektes (ggf. auf diesen Code/dieses spezielle File beschränkt), damit ich mich mal dahintersetzen kann, was da schiefläuft?
Das mit dem Projekt muss nicht unbedingt sein, aber eine fehlerhafte Datei (und ein passendes Original) wäre schon hilfreich.

Edit: Was schiefläuft, weiß ich jetzt. Dürfte ne Kleinigkeit im Code sein, aber da muss ich mal etwas genauer drüber nachdenken. Da fehlt einfach ein SetStreamEnd oder so ähnlich. In den vorhandenen Datenstrom wird der Tag geschrieben, dann die Audiodaten. Wenn der Tag wesentlich kürzer geworden ist, dann stehen am Ende noch Audiodaten vom alten Stream, die aber wieder mit gespeichert werden.

Sorry: :oops: Ein böser Fehler, ganz wie ich vermutet hatte. Wird so schnell wie möglich gefixed.


Gausi - Mo 12.02.07 21:33

So, Fehler behoben und ne neue Version mit ein paar anderen Änderungen hochgeladen. Die Anmerkungen von Manina sind aber noch nicht drin (die hab ich grade beim posten erst wieder entdeckt :oops:) - im Zweifelsfall die angesprochenen Copmilerschalter abschalten, wie es auch Vorgabe ist. Ich guck da aber nochmal drüber.
Da Seek auf jeden Fall einen Typ mit Vorzeichen erwartet, funktioniert das auch so. Intern wird richtig rumgecastet, so dass der Integerüberlauf/Bereichsüberschreitung hier nicht schlimm ist, bzw. das richtige Ergebnis liefert.


Manina - Mo 02.04.07 23:48

Mal wieder was von mir :D

diesmal nur ein klitzeklitzeklitzeklitzekleiner Schönheitsfehler in den Definitionen:


Delphi-Quelltext
1:
2:
3:
4:
5:
  // Beschreibung für Bild-Frames
  TPictureFrameDesription = record
    Description: String;
    Bildtyp: Byte;
  end;


soll doch (hoffentlich) wohl heißen:


Delphi-Quelltext
1:
2:
3:
4:
5:
  // Beschreibung für Bild-Frames
  TPictureFrameDescription = record 
    Description: String;
    Bildtyp: Byte;
  end;


Oder war das Absicht von Dir ? Wenn ja, dann sorry ... :roll:


MrSaint - Mi 13.06.07 08:42

Hallo!
Bin auch grad am rumexperimentieren mit deiner Komponente und mi ID3v2Tag funktionierrt bisher eigentlich alles toll. Leider hab ich etwas Probleme mit dem ID3V1Tag. Du Speicehrst hier Titel etc. intern als String30 ab und castest das bei einer Abfrage auf id3v1.Title auf WideString (ich hab den TNT-Compilerschalter an). Wenn ich jetzt sowas mache:

Delphi-Quelltext
1:
shwomessage('Titel: ' + id3v1.Titel + ' blabla');                    

wird das 'blabla' abgeschnitten, weil er die #0-Zeichen aus dem String30 bei der Konvertierung zu WideString mitnimmt :( Was helfen würde wäre u.U. folendes "rumgecaste":

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
function TID3v1Tag.GetTitle: WideString;
begin
  {$IFDEF USE_DIConverters}
  result := GetConvertedUnicodeText(FTitle);
  {$ELSE}
  result := WideString(PChar(string(FTitle)));
  {$ENDIF}
end;

Dann wird alles korrekt zurückgegeben (leider kann man FTitle nicht direkt in PChar casten, das mag er nicht...). Jetzt habe ich nur nicht allzu viel Ahnung von Unicode und weiß deshalb nicht, ob bei so einer Methode hier irgendwas verloren geht...
Gleiches gilt für Artist, Album & Comment...




MrSaint


Chatfix - Mi 13.06.07 08:51

Wenn ich mich recht erinnere hatte ich das selbe Problem.
Versuchs mal mit Trim. Damit sollten die #0-Zeichen verschwinden :)


Gausi - Mi 13.06.07 09:31

Ja, ein Trim in den Gettern sollte helfen, also result := Trim(WideString(FArtist));. Wichtig wäre möglicherweise auch, für die Eigenschaft Year auch einen Getter zu bauen, der mit Trim arbeitet. Werde wohl die Tage (oder schon heut nachmittag) mal was daran ändern - dann kann ich auch endlich die ganzen PNs löschen :lol:.


MrSaint - Mi 13.06.07 11:13

Hmm.. Trim... da war was ;) Oh man, man kann's sich ja auch kompliziert machen.. lol ;)


Gausi - Mi 13.06.07 15:36

Die neue Version 0.3a ist da. Ich hoffe, ich hab auf die Schnelle nichts falsch gemacht.

Für die, die damit schon Bilder auslesen: TPictureFrameDesription heißt jetzt TPictureFrameDescription - da also ggf. bei der Variablen-Deklaration ein c einfügen.


Chatfix - Do 14.06.07 10:30

Leider kommt beim ID3v1.Year immernoch "#0#0#0#0" wenn kein Jahr drin steht.


EDIT: Habs grad nochmal mit den anderen Probiert. Leider ist der Fehler mit dem RightTrim nicht behoben.

Hat es einen bestimmten Grund das du nicht Trim verwendet hast?


Gausi - Do 14.06.07 16:29

Hmm... Ich kann das nicht nachvollziehen. :nixweiss: Beim Auswerten des RawTags nutze ich TrimRight, ja, und beim Setzen auch. Nen bestimmten Grund hat das iirc nicht. Aber beim Getter für Jahr ist ein Trim zusätzlich mit drin. Und Konstrukte wie

Delphi-Quelltext
1:
2:
tmp := Id3v1Tag.year;
showmessage(PChar('@' + tmp + '@'));
zeigen brav zwei @ an, wenn das Jahr nur aus #0 besteht. Auch wenn tmp ein WideString ist (dann muss man aber das PChar weglassen, aber das ist ne andere Geschichte). Kannst du mir mal ein Minimal-Projekt schicken, wo sich dieser Fehler bemerkbar macht?


Chatfix - Do 14.06.07 22:03

Ok, ich schau mir das morgen mal an und versuch mal aus meinem Colde ein Testprogramm zu schreiben.
Vielleicht hab ich ja auch einen Fehler drin.

Ich nutze auf jedenfall die Unicode-Variante mit den TNT-Komponenten.

PS: Du kannst aber morgen ruhig in Ruhe feiern. Sooo dringend ist das Problem nicht, da die Version 0.3 mit Trim ja vorerst funktioniert.


Chatfix - Fr 15.06.07 09:03

[schlechte Singstimme]
Weil Du heut Geburtstag hast da hab ich mir gedacht,
ich probier es einfach nochmal aus und siehe da: es klappt.
[/schlechte Singstimme]

:party: Erstmal alles Gute zum Geburtstag! Vorallem viel Gesundheit! :party:

Nun wieder zum Thema:
Ich habe mich heute gleich an ein Beispielprogramm gemacht und dort trat der Fehler nicht auf.
Ich habe also nochmal im eigentlichen Projekt die Mp3FileUtils ausgetauscht. Und nun funktionierts auch hier.

Ich weis nicht warum es vorher nicht ging. Vielleicht hab ich die DCU nicht gelöscht, aber die hätte ja Delphi neu kompilieren müssen, da ich in der neuen Unit ja die Tnt's eingeschaltet habe. Nunja jetzt scheints zu funktionieren.

Danke trotzdem für deine Mühen!


Lazarus - Mi 11.07.07 18:06

Gausi, RESPEKT für deine Arbeit. Funzt ja auch soweit hganz gut.
Beim Bilder auslesen und bei Unicode baut das Programm bei mir dann mist. Letzteres, weil ich die anderen Komponenten (TNT) nicht habe. Bei mir sind in iTunes alle Bilder da und auch alle Titel etc. Bei deinen Routinen fällt mein programm manchmal auf die Nase... Wenn ich geneueres weiß, sag ich dir bescheid...
Schade das die TNT Kompos kommerziell sind, kennt da jemand ne alternative ???


Chatfix - Mi 11.07.07 18:11

Weis nich obs legal is aber ich habe noch die alten kostenlosen TNT-Kompos, die könnte ich dir zukommen lassen, wenn mal jemand klärt ob das zulässig ist.


Gausi - Mi 11.07.07 18:32

@iTunes und Bilder: Ich habe keine Ahnung, wo iTunes die Bilder hinpackt. Wahrscheinlich gibts da irgendwo auf der Platte ne Datenbank, in der die gespeichert sind, oder der ruft die direkt aus dem Netz ab. Diese Unit sucht nur im ID3v2Tag nach den PICTURE-Frames. Ich kann mir aber gerne mal ein mp3-File ansehen, wo iTunes Bilder anzeigt. Schicks per Mail, dann schau ich mal drüber. Wenn eins drin ist, dann werde ich das wahrscheinlich erkennen, und ggf. den Fehler in der Unit ausbügeln. Allerdings glaube ich, dass iTunes da was anderes verwendet. ;-)

@TNTs: Bei mir steht in der License.txt so etwas:
Zitat:
Redistribution and use in binary forms, with or without modification, are permitted.
Redistribution and use in source forms, with or without modification, are permitted provided that the redistributions of source code retain the above copyright.

Meiner Meinung nach dürfte ein Kopieren und Weiterverteilen kein Problem sein.


jakobwenzel - Mi 11.07.07 18:50

Zumindest in ver. 7.3.0.54 speichert iTunes die Bilder im Tag (wobei ich auch alle Bilder manuell eigefügt hab), wenn auch nicht unbedingt im JPG-Format (was ja laut Spezifikation das einzige zulässige Format ist)


Gausi - Mi 11.07.07 19:03

user profile iconjakobwenzel hat folgendes geschrieben:
[...] wenn auch nicht unbedingt im JPG-Format (was ja laut Spezifikation das einzige zulässige Format ist)
Das trifft für diese Unit zu, da ich beim Schreiben der Bilder der Einfachheit halber keinen Parameter für den Mime-Typ übergebe, sondern immer '/image/jpeg' schreibe.
Beim lesen wird der Bilddaten-Strom in einen Stream kopiert, der dann von einem Programm beliebig interpretiert werden kann. In den Demos gehe ich davon aus, dass es Jpgs sind. Ich könnte die Unit mal so erweitern, dass in dem Typ TPictureframeDescription der Mimetyp mit einfließt, um so ein differenzierteres Verhalten zu ermöglichen.

Im ID3v2Tag können beliebige Bildtypen gespeichert werden, laut id3.org sollte man sich aber auf PNG und JPEG beschränken. ("The "image/png" or "image/jpeg" picture format should be used when interoperability is wanted.") Diese Unit beschränkt sich auf Jpeg.


Chatfix - Do 12.07.07 08:43

Hier die Tnt-Kompos, du könntest diese ja mit im ersten Posting reinnehmen...


Lazarus - Do 12.07.07 20:19

Hallo Gausi,

ich suche nach einer Möglichkeit auch die Coverbilder anzuzeigen, die möglicherweise nicht JPG sind.
Es gab zum beispiel mal den Typen 3. Komischerweise werden tlw auch keine Bilder angezeigt, wenn der Typ 0 ist *kratzamkopf*
Manchmal ja, manchmal nicht. Ich denke aber das liegt noch bei mir (Momentan Testroutinen Quick&Dirty)...

Was bedeuten die Bildtypen ??? 0..9 ??

Übrigens, wollte dir mal was mailen, aber dein Postfach hat abgelehnt (Dateigrösse)


Gausi - Do 12.07.07 21:08

Die Bildtypen sollten eigentlich 0..20 sein. Zumindest das, was in dem Konstrukt TPictureFrameDescription drinsteckt. Dieser Typ hat aber nichts mit dem Format zu tun, sondern ist eine grobe Klassifizierung, um was für ein Bild es sich handelt. Welcher Typ das ist, kann man in dem Konstanten-Array Picture_Types ablesen. Typ 3 wäre dann ein "Cover (front)", 0 "Other". Das ist so im ID3-Standard definiert.

Die Bilddaten werden ja generell in einen Stream geladen. Und diesen müsste man dann entsprechend als jpg/png/sonstwas interpretieren. Bisher gibt es keine Unterscheidungsmöglichkeit, weil ich den Mimetypen da nicht mit einbeziehe. Man müsste den Code durchgehen und u.a. den Typ TPictureFrameDescription und Routinen wie GetPictureDescription so erweitern, dass auch das Bildformat nach außen geliefert wird. Das werde ich bei Gelegenheit mal in Angriff nehmen.

@Postfach: Per PN gehts nicht, da gibts afaik ne Beschränkung auf 2 MB oder so, aber per E-Mail (gausi@entwickler-ecke.de)sollte das gehen.


jmh124 - So 12.08.07 23:12

ich hab ein Problem mit der Mp3FileUtils unit.
Ein bestimmtes Album eines Mp3 Files enthält komische Daten:
laut winamp steht da der normale Name drin,
nach dem Id3v1 allerdings etwas in der Art:
"Albumname'#0'o teilweiseArtistname"
ich hab jetzt bissi hin und herkonvertiert aber eigentlich keine Ahnung wie ich damit umgehen soll?
Da ich das auch in eine SQL tabelle werfen möchte sind die quotes dadrin irgendwie problematisch. hier funktioniert auch keine QuoteStr...

Einer eine Idee ich kann sonst die mp3 datei auch mal zur verfügung stellen ;)

Mfg Julian


Morkel - So 16.09.07 12:05
Titel: Anregungen
Hallo Gausi,

zunächst einmal vielen Dank für die tolle Arbeit!

Ich hätte noch zwei Anregungen:
- Es wäre sinnvoll, den BPM (beats per minute)-Tag aufzunehmen, da er von vielen Anwendungen
genutzt wird.
- Um die Geschwindigkeit beim Verarbeiten geänderter Tags zu erhöhen, erscheint es mir nützlich zu sein,
statt temporärer Dateien lieber TMemoryStreams zu verwenden.

Viele Grüße
Fredo


Yogu - Do 22.11.07 20:53

Nicht schlecht. Ich habe aber auch eine Frage: Wo kann man sich die anderen 9 Sachen herunterladen, die kein Mensch braucht?


Gausi - Do 22.11.07 21:04

Was für andere 9 Sachen? :gruebel:


Yogu - Do 22.11.07 21:18

Dein Album, namens "10 Tolle Sachen, die kein Mensch braucht". Der einzige Song, den ich davon kenn ist "11 Sekunden feines Rauschen" von "Rainer Zufall". Ich find die Idee super. :D


Gausi - Do 22.11.07 21:29

Autsch. Ne, das ist ne Single-Auskopplung von einem Album, das nie erschienen ist :mrgreen:.


YouAreDelphinated - So 06.01.08 00:35
Titel: BUG!!
Mein erster Beitrag :D und gleich einer mit nem Bug :oops: ...
Naja, in Zeile 1971 deiner Unit, Gausi, steht

Delphi-Quelltext
1:
FPadding := GetPaddingSize(AudioDataSize + FDataSize + v1AdditionalPadding, FFilename, FUseClusteredPadding);                    

Wo die Variable FFilename deklariert ist, weiß ich nicht. Auf jeden Fall ist diese Variable leer. Das führt beim ändern der Tags zu einem Fehler. Meine Lösung wäre ( :?: falsch :?: ):

Delphi-Quelltext
1:
FPadding := GetPaddingSize(AudioDataSize + FDataSize + v1AdditionalPadding, FrameName, FUseClusteredPadding);                    

Trotzdem: Klasse Unit, sehr schnell und: Die 9 anderen Sachen sollte man mal machen :lol:


Gausi - So 06.01.08 11:55

Hallo und :welcome: in der Entwickler-Ecke!

Der Fehler tritt auf, wenn man nicht mit Dateien, sondern mit Streams arbeitet und ich dachte eigentlich, ich hätte den schon gefixed. Hab ich auch, aber nur lokal und dan irgendwann vergessen, die korrigierte Fassung hochzuladen.
Problem ist die Funktion GetPaddingsize, bei der das Padding auch anhand der Clustergröße bestimmt wird. Die dazu gehörende API-Funktion wirft eine Exception, wenn man keinen Laufwerk/Dateinamen übergibt. Das private Feld fFilename wird beim lesen/schreiben des Tags aus/in eine(r) Datei gesetzt. Wenn man nur mit Streams arbeitet und so die Klasse nicht ermitteln kann, wo der Stream liegt, fehlt da ein Abfangen des Fehlers.

Deine Lösung ist also falsch. Richtig wäre so eine Korrektur der Funktion GetPaddingSize:


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
function GetPaddingSize(DataSize: Int64; aFilename: WideString; UseClusterSize: Boolean): Cardinal;
var
   Drive: string;
   ClusterSize           : Cardinal;
   SectorPerCluster      : Cardinal;
   BytesPerSector        : Cardinal;
   NumberOfFreeClusters  : Cardinal;
   TotalNumberOfClusters : Cardinal;
begin
  Drive := WideExtractFileDrive(aFileName);
  if UseClusterSize  and (trim(Drive) <> '' then
  begin
      if Drive[Length(Drive)]<>'\' then Drive := Drive+'\';
      try
          if GetDiskFreeSpace(PChar(Drive),
                              SectorPerCluster,
                              BytesPerSector,
                              NumberOfFreeClusters,
                              TotalNumberOfClusters) then
            ClusterSize := SectorPerCluster * BytesPerSector
          else
            ClusterSize := 2048;
      except
        ClusterSize := 2048;
      end;

  end else
    ClusterSize := 2048;
  Result := (((DataSize DIV ClusterSize) + 1) * Clustersize) - DataSize;
end;


Yogu - So 06.01.08 13:09

Ich denke, meine Frage kannst du gleich mit "nein" beantworten, aber ich stelle sie trotzdem mal :wink:

Hast du zufällig die Unit auch im Delph 3-Format? Ich würde sie gerne benutzen, aber da wären eine Menge Sachen zu ändern, und zum Beispiel offene Arrays gehen gar nicht. Da müsste ich schon Listen einsetzen, und ich glaube, dann könnte ich die Unit gleich selber machen :?

Also, wenn du irgendetwas in der Art kennst, das für Delphi 3 geschrieben ist, wäre ich dir sehr dankbar.


Gausi - So 06.01.08 13:16

Als Alternativen kenne ich die ATL (Audio Tools Library), die ich aber nur für den Read-Only-Einsatz empfehlen kann. Zum Schreiben ist die imho Murks, weil Informationen verloren gehen können. Besser ist auf jeden Fall noch die ID3Lib von Muetze1 aus der Delphi-Praxis, auch wenn ich mir die noch nicht genauer angeguckt habe (iirc kann die Version 2.2 des ID3Tags nicht schreiben, aber alle Informationen bleiben beim schreiben erhalten). Zuletzt ist bei den Jedis auch sowas dabei.

Ob eins der drei für D3 geeignet ist, weiß ich nicht. :nixweiss:


Yogu - So 06.01.08 13:19

Ok, danke, ich werd's mir mal anschauen. Ein bisschen Umschreiben macht mir nichts, aber ich will halt nicht die komplette Unit neu verfassen. :wink:


YouAreDelphinated - Mo 07.01.08 02:10

Zitat:
Deine Lösung ist also falsch. Richtig wäre so eine Korrektur der Funktion GetPaddingSize:

äh... Genau diese Funktion habe ich... Trotzdem crashed sie (aufgerufen wie original) bei

Delphi-Quelltext
1:
if Drive[Length(Drive)]<>'\' then Drive := Drive+'\';                    

mit einer Zugriffsverletzung (Lesen von Adresse FFFFFFFF). aFilename ist dabei übrigens ''.


dsJena - Mo 28.01.08 00:01

Hallo,
vielen Dank an Gausi für die gute Arbeit!

Nun habe ich ein Problem..
Und zwar klappt das Auslesen bzw. Berechnen der Spiellänge per


Delphi-Quelltext
1:
2:
3:
4:
mpegInfo:=TMpegInfo.create;
mpeginfo.loadFromFile(playlist1.Items[0]);
label3.Caption:=(inttostr(mpeginfo.dauer));
mpegInfo.Free;


super. Allerdings scheitere ich beim Auslesen per


Delphi-Quelltext
1:
2:
3:
4:
Id3v2Tag:=TId3v2Tag.Create;
Id3v2Tag.ReadFromFile(playlist1.Items[0]);
label4.Caption:=id3v2tag.artist;
id3v2tag.Free;


Im Label wird nämlich nichts angezeigt. Auch keine Fehlermeldung o.ä., einfach ein leeres Label.

Zur Ergänzung: Playlist1.items[0] gibt den ersten Eintrag der Playlist zurück, das ist direkt der Dateiname. Label4 ist das betroffene Label.

Ich habe mir schonmal den umfangreichen Beispielcode angeschaut, aber bis jetzt ohne Erfolg. Wie mache ich es richtig?


Danke für die Hilfe!

Viele Grüße,

Daniel



EDIT: Bei einigen Dateien funktioniert es komischerweise. Könnte es was mit den Versionen (V1/V2) zu tun haben?


Gausi - Mo 28.01.08 09:54

Ich tippe einfach mal darauf, dass in den Dateien, wo es nicht funktioniert, kein ID3v2-Tag vorhanden ist, oder das Artist-Feld leer ist. Zum Auslesen solcher Infos mit dieser Unit für einen Player ist dieser Eintrag [http://www.delphi-library.de/topic_MP3Informationen+auslesen_16228.html] in der Library hilfreich, oder das 2.Kapitel in diesem Tutorial [http://www.gausi.de/downloads/memp_tutorial.pdf].

Da wird erklärt, wie man recht komfortabel (d.h. ohne umfangreiche Code-Umbauten) einfach den "Artist" rausfinden kann, selbst wenn man hinterher ogg, wma oder sonstwas für Dateien hat.


dsJena - Mo 28.01.08 10:40

Hallo,
danke für die Antwort. Mit V1 geht es. Da lasse ich es erstmal so.


Gausi - Mo 28.01.08 10:46

Da wirst du dann bei anderen Dateien dasselbe Problem haben. Einige Dateien haben den v2-Tag, andere den v1-Tag, ziemlich viele haben beide, sehr wenige keinen. Deswegen ist es sinnvoll, beide Tags zu lesen, und das zu nehmen, was da ist. ;-)


Yogu - Mo 28.01.08 16:18

Ein Problem dabei ist es leider, dass man das Feld auch mal absichtlich leer lassen kann.

Ein Beispiel:Die Wahl ist schwierig, und nicht unbedingt erwünscht.

Hat jemand eine Lösung? Wie macht ihr denn das? Und wie sieht's beim Schreiben aus?


Gausi - Mo 28.01.08 16:59

Das hat jetzt aber nichts mit dieser Unit zu tun, oder? :gruebel:

Wenn das nicht nur ne Frage nebenher war, dann würde ich das in ein eigenes Topic abtrennen. Ansonsten würde ich das hier einfach ignorieren :mrgreen:


Yogu - Mo 28.01.08 17:12

Ok, ok, war nicht so wichtig. :|


Gausi - Fr 01.02.08 19:54

Habe gerade mal eine korrigierte Version online gesetzt, bei der der Fehler in der Funktion GetPaddingSize behoben ist, der bei manchen Anwendungen zu einer Exception führte. Programme, die mit der alten Version laufen, müssen nicht unbedingt neu kompiliert werden - der Fehler tritt bei einem Programm entweder immer oder gar nicht auf - kommt drauf an, was man programmiert hat. ;-)

Download im ersten Beitrag oder auf http://www.gausi.de/delphi/mp3fileutils.php


Gausi - Mo 09.06.08 16:26

Habe gerade mal eine neue Version hochgeladen (Version 0.4). Die ist noch im Beta-Stadium, also mit Vorsicht zu genießen. Ich habe bisher beim Testen keine Probleme festgestellt, wenn man mal davon absieht, dass gewisse Schreibeinstellungen auf "Level 3" (siehe Doku/Demos) dazu führen, dass sich das eine oder andere Programm am Tag verschluckt (Winamp, WMP, TagIt, Mp3Tag, Mp3-TagStudio, Mediamonkey, Nemp, ... eigentlich habe ich keins gefunden, was immer funktioniert). Das liegt imho aber an den Programmen, nicht an mir. :mrgreen:

Download im ersten Beitrag oder auf http://www.gausi.de/delphi/mp3fileutils.php


Gausi - Mo 13.10.08 19:59

Kleines Mini-Update. Da sich fast keiner über Bugs beschwert hat, denke ich, dass man das Beta rausnehmen kann. Einen Bug habe ich auch entfernt, und ein paar andere kleine Änderungen vorgenommen.

Wer die Unit nutzt, um auch Tracknummern aus dem ID3v2-Tag anzuzeigen, sollte auf die neue Version updaten. ;-)

Download im ersten Beitrag oder auf http://www.gausi.de/delphi/mp3fileutils.php


Wolle92 - Sa 29.11.08 17:18

Steht in der TID3v2.Artist der Albuminterpret oder der Einzelinterpret drin?
Kann das hier grad leider nicht ausprobieren!


Gausi - Sa 29.11.08 17:32

Ich bin auch nach dieser Diskussion [http://www.delphi-forum.de/viewtopic.php?t=86548&highlight=albuminterpret] nach wie vor der Meinung, dass es sowas wie den Albuminterpreten nicht unbedingt gibt, aber ok. Laut user profile iconDunkel schreibt Winamp dafür den TPE2-Frame, der laut ID3v2-Spezifikationen für "Band/Orchestra/Accompaniment" steht. Wenn man das mit Albuminterpret übersetzen möchte - ok.

Diese Information sollte man mit

Delphi-Quelltext
1:
myAlbumInterpret := myID3v2Tag.GetText(IDv2_BANDACCOMPANIMENT);                    
bekommen. Ansonsten liefert TID3v2.Artist den Interpreten des einzelnen Stückes - so wie es auch Sinn macht.


Wolle92 - Sa 29.11.08 17:44

wieso macht das sinn, wenn ich doch nach Interpreten/Alben sortiere, und dann son Various Artists-Album dabei hab?


Gausi - Sa 29.11.08 17:53

Wenn ich wissen möchte, wer ein Lied singt, dann möchte ich da nicht "VA" als Antwort haben, weil das Lied zufällig auf der neuen Bravo-Hits drauf ist, sondern ich möchte wissen, wer das singt. Deswegen macht es Sinn, als Artist den Einzelinterpreten zu liefern.

Wenn man das Lied im Kontext eines Albums sehen möchte, kann man immer noch IDv2_BANDACCOMPANIMENT benutzen.


Gravitar - Fr 05.12.08 13:18
Titel: Auch joinen von mp3s möglich
Hi,

tolle Unit, nimmt mir viel arbeit ab.

Beim durchscrollen habe ich mich gefragt, ob man vorhandene v1 oder v2-Tags aus einer mp3 auch löschen kann (nicht nur die Tags auf "Null" setzen, sondern komplet den Datenblock löschen).

Hintergrund ist, dass ich mehrere mp3s in eine große mp3 zusammenführen möchte. Natürlich hat jede einzelne mp3 v1 und/oder v2-Tags. Damit die neue "große" mp3 nun nicht hunderte von id3-Tags enthält, wäre ein vorheriges Löschen nicht schlecht.

Unterstützt deine Unit dies auch?

Gruß, Andreas


Gausi - Fr 05.12.08 13:24

Ja, dafür gibt es jeweils die Methoden RemoveFromStream und RemoveFromFile bei den beiden Klassen für ID3v1 und v2-Tag.


Gausi - So 28.12.08 18:00

Hier gabs jetzt auch ein kleines Update: Mp3FileUtils 0.4a [http://www.gausi.de/downloads/mp3fileutils_04a.zip]

Einzige Änderung: Bei UTF-16-kodierten Textframes mit BOM und Byteorder FE FF wurde das letzte Zeichen im Frame überlesen. Jetzt nicht mehr.


tartare - Mo 19.01.09 02:56

hi, war immer sehr zu frieden mit den MP3FileUtils, aber jetz in delphi 2009 gibts nur noch fehler und warnungen:


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
[DCC Warnung] Id3v2Frames.pas(1092): W1000 Symbol 'UTF8Decode' ist veraltet : 'Use UTF8ToWideString or UTF8ToString'
[DCC Warnung] Id3v2Frames.pas(1092): W1058 Implizite String-Umwandlung mit potenziellem Datenverlust von 'string' zu 'RawByteString'
[DCC Hinweis] MP3FileUtils.pas(296): H2365 Schreibweise der Überschreiben-Methode TID3v1Tag.destroy muss exakt ihrem Vorfahren TObject.Destroy entsprechen
[DCC Hinweis] MP3FileUtils.pas(734): H1054 You should change the default rating description for your projects
[DCC Warnung] MP3FileUtils.pas(777): W1050 WideChar in Set-Ausdrücken auf ByteChar verkürzt.  Ziehen Sie die Verwendung der Funktion 'CharInSet' aus der Unit 'SysUtils' in Betracht.
[DCC Fehler] MP3FileUtils.pas(981): E2008 Inkompatible Typen
[DCC Fehler] MP3FileUtils.pas(982): E2008 Inkompatible Typen
[DCC Fehler] MP3FileUtils.pas(983): E2008 Inkompatible Typen


und das is nur ein kleiner auszug...kann ich da irgendwie etwas mit meinen compilereinstellungen verändern oder warum gibts da auf einmal so große unterschiede, wenn in Delphi 7 das ganze einwandfrei kompiliert wird? würde die unit echt gerne weiterbenutzen....

mfg tartare


Gausi - Mo 19.01.09 08:44

Der Unterschied ist, dass in Mp3FileUtiles viel mit Strings gearbeitet wird, und in Delphi2009 ist ein String nicht mehr ein AnsiString, sondern ein WideString (oder UnicodeString?).

Da ich selber kein Delphi2009 habe, kann ich auch noch kein Update Delphi2009 kompatibles Update rausbringen. Ob es reicht, alle Strings explizit durch AnsiStrings zu ersetzen, weiß ich nicht.


tartare - Mo 19.01.09 17:10

OK Problem gelöst: einfach alle array of char in array of AnsiChar umgewandelt....


Gausi - Mo 19.01.09 18:55

user profile icontartare hat in etwa geschrieben:
Jippieh, es kompiliert - wir können ausliefern!


Vorsicht! :mahn: In den ID3Tags können viele verschiedene Textformate auftreten. UTF8, Ansii, sowie UTF16 mit und ohne BOM. Intern nutzt Mp3FileUtils teilweise Methoden, um das eine in das andere umzuwandeln, die mit einer bloßen Umdeklaration von Array of Char nicht behoben sein dürften. Zum Beispiel auch autoamtische Typcasts von WideString zu String, die mit Delphi2009 nicht mehr funktionieren. Auch die automatische Zeichensatzerkennung bei Verwendung von DIConverters dürfte in Delphi2009 nicht unbedingt die gewünschten Ergebnisse liefern.

Ich rate dringend davon ab, eine solche einfache Modifikation auf echte Daten loszulassen und/oder ein Programm zu verteilen, welches nicht ausdrücklich als Beta-Version gekennzeichnet ist.

Es könnte sein, dass Mp3-Dateien dadurch irreparabel beschädigt werden können. Ok, du wärest damit in guter Gesellschaft, aber das muss ja nicht sein.

Ich verlinke einfach mal nach Holland [http://www.prisman.nl/] rüber. Der Autor hat mich vor einiger Zeit mal angeschrieben, weil er Teile von Mp3FileUtils (hauptsächlich den MPEG-Teil, wenn ich das richtig überblickt habe) übernommen hat für seine Delphi2009-ID3Tag-Bibliothek. Inwiefern der Rest darin was taugt, weiß ich allerdings nicht. ;-)


Heiko - Fr 23.01.09 22:58

user profile iconGausi hat folgendes geschrieben Zum zitierten Posting springen:
Vorsicht! :mahn: In den ID3Tags können viele verschiedene Textformate auftreten. UTF8, Ansii, sowie UTF16 mit und ohne BOM. Intern nutzt Mp3FileUtils teilweise Methoden, um das eine in das andere umzuwandeln, die mit einer bloßen Umdeklaration von Array of Char nicht behoben sein dürften. Zum Beispiel auch autoamtische Typcasts von WideString zu String, die mit Delphi2009 nicht mehr funktionieren.


Ergibt imho kein Problem. Ich müsste mir mal TurboDelphi wieder installieren,. aber ich habe in Erinnerung, wenn man sich mal von String durchklickt auf dessen Definition stößt man auch auf String=AnsiString. Wenn man also AnsiString statt String macht, dürfte es keine Änderungen ergeben. Bei Char das gleiche. Da du damit wieder mal AnsiChar und mal WideChar hast, dürfte auch die konvertierung funkltionieren. Im Prinzip dürfte der erzeuge Code unter D2009 zu dessen Vorgängern inhaltlich gleich sein ;).

Btw: Installier dir doch die D2009-Demo in einer VM, dann kannste es hin und wieder einmal probieren ohne deinen Rechner mit einer Demo zu "versauen" ;)


Gausi - Sa 24.01.09 10:55

Ja, das mag sein. Aber nur die einzige Änderung mit dem Array of Char, damit es keine Compilierfehler mehr gibt, reicht mit sehr hoher Wahrscheinlichkeit nicht aus. ;-)

Außerdem weiß ich nicht, wie sich die optional verwendbare Fremdunit (DiConverters) damit verträgt.


Gausi - Do 09.04.09 15:53

Eine neue Version (0.5) ist draußen. Änderungen im ersten Posting.

Im Wesentlichen ist der Code jetzt kompatibel Delphi 2009, und eine der optional verwendbaren Zusatzunits, die wahrscheinlich kein Mensch außer mir benutzt hat, ist rausgeflogen. Die Funktion dieser Unit wird jetzt (wenn auch nicht ganz) von einer API-Funktion erledigt, was das ganze natürlich etwas einfacher macht.

Unter Delphi 2007 oder früher können weiterhin die TNT-Controls für Unicode-Dateizugriff benutzt werden, unter Delphi 2009 ist das natürlich sinnfrei.

Die im ersten Posting verlinkten Demos sind aktuell mit der D2009 Trial kompiliert - es kann daher sein, dass die bald (d.h. Ende des Monats) nicht mehr laufen. Sie werden ersetzt, wenn die Vollversion da ist.


Gausi - So 16.08.09 15:29

Überarbeitete Version (0.5b) ist draußen. Wer die Unit zum Bearbeiten von ID3-Tags unter Delphi 2009 einsetzt, sollte dringend die neue Version benutzen.

Download-Links sind gleichgeblieben, siehe erstes Posting.

Danke nochmal an der Stelle an den netten Menschen aus Wien, der mir das Problem gemeldet hat. :D


Teekeks - Mo 19.10.09 07:44

Hallo Gausi!
Ich habe mir mal erlaubt deinen "Kram" mal etwas zu verkürzen und nur die Stelle rauskopiert und angepasst welche die Trackinfos ausgibt.

Die will ich euch natürlich nicht vorenthalten.
Sie geht auf jeden Fall für Lazarus.
Alles andere weiß ich nicht.

Gruß Teekeks


Gausi - Mo 19.10.09 10:50

Dieser Code kann aber manchmal Probleme machen bzw. dauert unnötig lang, da ein (möglicherweise sehr umfangreicher) ID3v2-Tag am Anfang für die Suche nach einem MPEG-Header nicht übersprungen wird.

Die Original-Unit ist höchstwahrscheinlich nicht direkt Lazarus-kompatibel, da ich für das Auslesen von Texten im ANSI/ISO8859-Format eine Heuristik verwende, die auf gewisse Windows-API-Methoden zurückgreift.
Wenn ich mal Zeit dazu finde, kann ich mal schauen, inwiefern man das Ding für Lazarus umbauen muss - ggf. mit leichten Einschränkung dafür.


Teekeks - Mo 19.10.09 15:29

Wäre schön wenn du das machen könntest...

Solange arbeite ich mit meiner variante, fürs erste geht das so.
Ich würde mich aber darüber freuen wenn du das Ändern würdest.

Gruß Teekeks


DjVinny - Di 21.09.10 19:13

Hi,

I have an issue. I recently upgraded to Delphi 2009 a while back, and decided to redo a program I have been personally using for a while. As I loaded it up into Delphi I noticed I had an old version of mp3fileutils (0.3) which was not compiling correctly. So I upgraded to mp3fileutils 0.5b. Now I get an access violation here;


Quelltext
1:
2:
3:
4:
5:
6:
7:
         stream := TFileStream.Create(memo1.Lines.Strings[i], fmOpenRead or fmShareDenyWrite);
         id3v2Tag.ReadFromStream(stream);    <--------- ACCESS VIOLATION gets thrown.
         if Not Id3v2Tag.exists then
          stream.Seek(0, sobeginning)
          else
          stream.Seek(Id3v2Tag.size, soFromBeginning);
          stream.Free;


Any help would be appreciated. Thank you.


Gausi - Di 21.09.10 19:21

Hello and :welcome: in the Entwickler-Ecke. :wave:

As written in the reply to your mail a few moments ago:

The two (ok, here are some more ;-)) lines of code seems to be correct - I have no idea what goes wrong there.
Have you created the id3v2Tag-Object properly by id3v2Tag := TId3v2Tag.Create? Are there at least (i+1) lines in the memo? Was tFileStream.Create completed successful? Was the Access Violation in this line of code, or is it raised somewhere deep in mp3FileUtils?

It is often easier to find such bugs with tools like Madexcept [http://www.madshi.net/] (free for non-commercial use). Just install it, activate the madExcept-Settings (there will be a menu-item in the Delphi-IDE after install). This will give some more information. :)


DjVinny - Di 21.09.10 22:03

It is weird. As it worked before with the old version.

But here are the answers to your questions.

memo1 is populated, as far as I can see I but some popups in mp3fileutils on readfromstream and it doesnt even get to that point.

I also put a messagedlg between these two lines;

stream := TFileStream.Create(memo1.Lines.Strings[i], fmOpenRead or fmShareDenyWrite);
<--- popup dialog was here.
id3v2Tag.ReadFromStream(stream);

and the popup displayed. So it happens after the filestream.create


and Id3v2Tag := TId3v2Tag.Create; is in Form.Show


DjVinny - Di 21.09.10 22:35

I do not know what you need from the madexcept But;


Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
79:
80:
81:
82:
83:
84:
85:
86:
87:
88:
89:
90:
91:
92:
93:
94:
95:
96:
97:
98:
99:
100:
101:
102:
103:
104:
105:
106:
107:
108:
109:
110:
111:
112:
113:
114:
115:
116:
117:
118:
119:
120:
121:
compiled with     : Delphi 2009
madExcept version : 3.0l
callstack crc     : $9b971e14, $cf1ff7a6, $cf1ff7a6
exception number  : 1
exception class   : EAccessViolation
exception message : Access violation at address 004EDE70 in module 'DmVinnyMp3Tools.exe'. Write of address 00000009.

main thread ($ec4):
004ede70 +000 DmVinnyMp3Tools.exe Mp3FileUtils    2036  +0 TID3v2Tag.Clear
004ed68e +00a DmVinnyMp3Tools.exe Mp3FileUtils    1649  +2 TID3v2Tag.ReadFromStream
004f53c1 +349 DmVinnyMp3Tools.exe Unit4            217 +35 TForm4.Button1Click
004c2c33 +06f DmVinnyMp3Tools.exe Controls                 TControl.Click
00496786 +01e DmVinnyMp3Tools.exe StdCtrls                 TCustomButton.Click
004971e8 +010 DmVinnyMp3Tools.exe StdCtrls                 TCustomButton.CNCommand
004c26ca +2d2 DmVinnyMp3Tools.exe Controls                 TControl.WndProc
004c6bcf +513 DmVinnyMp3Tools.exe Controls                 TWinControl.WndProc
0049644c +06c DmVinnyMp3Tools.exe StdCtrls                 TButtonControl.WndProc
004c22f0 +024 DmVinnyMp3Tools.exe Controls                 TControl.Perform
004c6d1f +023 DmVinnyMp3Tools.exe Controls                 DoControlMsg
004c771b +00b DmVinnyMp3Tools.exe Controls                 TWinControl.WMCommand
004dcc34 +02c DmVinnyMp3Tools.exe Forms                    TCustomForm.WMCommand
004c26ca +2d2 DmVinnyMp3Tools.exe Controls                 TControl.WndProc
004c6bcf +513 DmVinnyMp3Tools.exe Controls                 TWinControl.WndProc
004d9c58 +594 DmVinnyMp3Tools.exe Forms                    TCustomForm.WndProc
0047c028 +014 DmVinnyMp3Tools.exe Classes                  StdWndProc
004c62e8 +02c DmVinnyMp3Tools.exe Controls                 TWinControl.MainWndProc
0047c028 +014 DmVinnyMp3Tools.exe Classes                  StdWndProc
7569cd7c +047 USER32.dll                                   SendMessageW
756a0ad1 +016 USER32.dll                                   CallWindowProcW
004c6ccb +0d7 DmVinnyMp3Tools.exe Controls                 TWinControl.DefaultHandler
004c3050 +010 DmVinnyMp3Tools.exe Controls                 TControl.WMLButtonUp
004c26ca +2d2 DmVinnyMp3Tools.exe Controls                 TControl.WndProc
004c6bcf +513 DmVinnyMp3Tools.exe Controls                 TWinControl.WndProc
0049644c +06c DmVinnyMp3Tools.exe StdCtrls                 TButtonControl.WndProc
004c62e8 +02c DmVinnyMp3Tools.exe Controls                 TWinControl.MainWndProc
0047c028 +014 DmVinnyMp3Tools.exe Classes                  StdWndProc
75697df5 +00a USER32.dll                                   DispatchMessageW
004e216f +0f3 DmVinnyMp3Tools.exe Forms                    TApplication.ProcessMessage
004e21b2 +00a DmVinnyMp3Tools.exe Forms                    TApplication.HandleMessage
004e24dd +0c9 DmVinnyMp3Tools.exe Forms                    TApplication.Run
00500ef2 +06e DmVinnyMp3Tools.exe DmVinnyMp3Tools   26  +7 initialization
75e43675 +010 kernel32.dll                                 BaseThreadInitThunk

modules:
00400000 DmVinnyMp3Tools.exe                    D:\[_APPLICATIONS AND UTILITIES_]\[PROGRAMMING]\[[_DELPHI PROJECT FILES_]]\DELPHI PROJECTS\CD CATALOGER
6dff0000 mpr.dll             6.1.7600.16385     C:\windows\system32
6e140000 DUser.dll           6.1.7600.16385     C:\windows\system32
6e690000 msimg32.dll         6.1.7600.16385     C:\windows\system32
71ed0000 dwmapi.dll          6.1.7600.16385     C:\windows\system32
728c0000 uxtheme.dll         6.1.7600.16385     C:\windows\system32
73260000 comctl32.dll        6.10.7600.16385    C:\windows\WinSxS\x86_microsoft.windows.common-controls_6595b64144ccf1df_6.0.7600.16385_none_421189da2b7fabfc
73400000 winmm.dll           6.1.7600.16385     C:\windows\system32
735f0000 version.dll         6.1.7600.16385     C:\windows\system32
739a0000 wsock32.dll         6.1.7600.16385     C:\windows\system32
74dd0000 CRYPTBASE.dll       6.1.7600.16385     C:\windows\syswow64
74de0000 SspiCli.dll         6.1.7600.16484     C:\windows\syswow64
74e40000 RPCRT4.dll          6.1.7600.16385     C:\windows\syswow64
74f30000 NSI.dll             6.1.7600.16385     C:\windows\syswow64
74f40000 IMM32.DLL           6.1.7600.16385     C:\windows\system32
74fa0000 ADVAPI32.dll        6.1.7600.16385     C:\windows\syswow64
75260000 MSCTF.dll           6.1.7600.16385     C:\windows\syswow64
75330000 msvcrt.dll          7.0.7600.16385     C:\windows\syswow64
753f0000 comdlg32.dll        6.1.7600.16385     C:\windows\syswow64
75680000 USER32.dll          6.1.7600.16385     C:\windows\syswow64
75780000 SHLWAPI.dll         6.1.7600.16385     C:\windows\syswow64
757e0000 CLBCatQ.DLL         2001.12.8530.16385 C:\windows\syswow64
75870000 GDI32.dll           6.1.7600.16385     C:\windows\syswow64
75900000 USP10.dll           1.626.7600.16385   C:\windows\syswow64
759a0000 LPK.dll             6.1.7600.16385     C:\windows\syswow64
759b0000 ole32.dll           6.1.7600.16385     C:\windows\syswow64
75df0000 WS2_32.dll          6.1.7600.16385     C:\windows\syswow64
75e30000 kernel32.dll        6.1.7600.16385     C:\windows\syswow64
75fe0000 shell32.dll         6.1.7600.16644     C:\windows\syswow64
76c30000 KERNELBASE.dll      6.1.7600.16385     C:\windows\syswow64
76d70000 oleaut32.dll        6.1.7600.16567     C:\windows\syswow64
76e00000 sechost.dll         6.1.7600.16385     C:\windows\SysWOW64
77270000 ntdll.dll           6.1.7600.16559     C:\windows\SysWOW64



cpu registers:
eax = 00000000
ebx = 00000000
ecx = 00000000
edx = 01e06de8
esi = 00000000
edi = 01e06de8
eip = 004ede70
esp = 0018f4f4
ebp = 0018f570

stack dump:
0018f4f4  93 d6 4e 00 70 f5 18 00 - d0 2e d4 01 5c 1d 49 00  ..N.p.......\.I.
0018f504  00 00 00 00 c6 53 4f 00 - ec f8 18 00 5c 4b 40 00  .....SO.....\K@.
0018f514  70 f5 18 00 10 f7 18 00 - 68 67 49 00 40 9d d2 01  p.......hgI.@...
0018f524  00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00  ................
0018f534  00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00  ................
0018f544  00 00 00 00 00 00 00 00 - 00 00 00 00 00 00 00 00  ................
0018f554  0e 00 00 00 e8 6d e0 01 - e8 cd df 01 0c 00 00 00  .....m..........
0018f564  00 00 00 00 00 00 00 00 - 00 00 00 00 b4 f6 18 00  ................
0018f574  39 2c 4c 00 40 9d d2 01 - 8b 67 49 00 10 f7 18 00  9,L.@....gI.....
0018f584  ed 71 49 00 40 9d d2 01 - cd 26 4c 00 10 f7 18 00  .qI.@....&L.....
0018f594  1c f9 18 00 40 9d d2 01 - 27 70 69 75 5f f8 28 73  ....@...'piu_.(s
0018f5a4  d8 08 41 00 00 00 00 00 - 00 00 00 00 2b f8 28 73  ..A.........+.(s
0018f5b4  0b f9 28 73 30 7a 96 00 - fc f5 18 00 00 00 00 00  ..(s0z..........
0018f5c4  30 7a 96 00 00 00 00 00 - 00 00 00 00 00 00 00 00  0z..............
0018f5d4  00 00 00 00 2b f8 28 73 - 01 00 00 00 78 f6 18 00  ....+.(s....x...
0018f5e4  00 00 00 00 f4 f5 18 00 - d8 08 41 00 00 00 00 00  ..........A.....
0018f5f4  00 00 00 00 b4 88 f5 22 - 28 f6 18 00 38 62 69 75  ......."(...8biu
0018f604  d8 08 41 00 15 02 00 00 - 00 00 00 00 00 00 00 00  ..A.............
0018f614  2b f8 28 73 cd ab ba dc - 00 00 00 00 00 00 00 00  +.(s............
0018f624  2b f8 28 73 38 f6 18 00 - 54 f6 18 00 30 02 70 75  +.(s8...T...0.pu

disassembling:
004ede70      public Mp3FileUtils.TID3v2Tag.Clear:  ; function entry point
004ede70 2036 > mov     byte ptr [eax+9], 3
004ede74 2037   mov     byte ptr [eax+$a], 0
004ede78 2038   xor     edx, edx
004ede7a        mov     [eax+$18], edx
004ede7d 2039   xor     edx, edx
[...]


Yogu - Di 21.09.10 23:36

Are you really sure that id3v2Tag is not nil at the line you call ReadFromStream? As I see in the call stack, the exception occurs in the Clear method which is pretty simple. Nearly the only object accessed in there is the TId3v2Tag itself.


DjVinny - Di 21.09.10 23:44

Im absolutely sure, because before I call the id3 tag, I parse the filename which is displayed on the listview prior to the error... so I know its getting the input.

To make it more clear, I have 5 columns in my listview. The first column holds the filename which is parsed from memo1. The second and third column holds the mp3 file title and artist which the program attempts to grab by splitting the (-) minus symbol (if this was a fairy tale all mp3s would be named in this manner: artist - title.mp3) in the filename. Then the fourth and fifth columns hold the actual call to the id3 tag.

So therefore memo1 has to be populated or columns 1 through 3 would raise an error.

I also currently have it working with JEDI tools, but it seems faster with your utility, and I would rather switch back to yours.


Gausi - Mi 22.09.10 10:37

Ok, this is strange. The call of ReadFromStream is ok, but Clear (first line in the ReadFromStream-metod) raises the Exception? :gruebel:

You can try the ReadFromFile-method instead, but I don't think that this will really solve the problem (as this will call ReadFromStream). You wrote, that you are porting the project to Delphi2009. I suppose that the real reason for this Access Violation is somewhere else. Maybe you read/write some strings assuming every char is represented by one single byte which somehow corrupts the memory of your application. :nixweiss:


Gausi - Do 23.06.11 16:40

Nur zur Info ein kleiner Push: Ich habe eben Version 0.6 hochgeladen, die auch schon in meinem Player zum Einsatz kommt. Darin u.a. enthalten Unterstützung für "Private Frames" und ein paar Bugfixes, die allerdings nur bei einigen wenigen mp3-Dateien aufgefallen sein dürften. Daher war das nicht so furchtbar dringend.

Außerdem gibt es jetzt eine Portierung der alten Version nach Lazarus, die mir freundlicherweise zugeschickt wurde. Die ist nicht von mir getestet, sieht aber auf den ersten Blick ganz gut aus. Die Bugfixes sind da noch nicht eingepflegt, das muss man ggf. selber nachholen.

Wer schon mit Bewertungen und Abspielzählern gearbeitet hat, muss seinen Code evtl. leicht modifizieren. Da gab es eine kleine Änderung.


Gausi - Sa 03.12.11 19:22

Hier habe ich einen doofen Bug gefunden und gefixed, der mit der neuen Bewertungsmethode zusammenhängt.

Damit ist dann auch der Fehler in Nemp behoben, dass die automatische Bewertung neue Dateien erstmal mit "voll der Schrott" bewertet hat, anstelle von "joah, ganz nett".

Download hier: Mp3FileUtils, Version 0.6a [http://www.gausi.de/downloads/mp3fileutils_06a.zip].