Autor Beitrag
Gausi
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 8535
Erhaltene Danke: 473

Windows 7, Windows 10
D7 PE, Delphi XE3 Prof, Delphi 10.3 CE
BeitragVerfasst: Mi 10.09.03 15:15 
Hinweis: An dieser Stelle stand eine ganze Zeitlang ein anderer Beitrag. Im Laufe der Zeit habe ich diese Methode weiterentwickelt, Fehler ausgemerzt, große Teile neu oder umgeschrieben und die Verwendung vereinfacht. In der alten Fassung war immer noch eine gewisse Beschäftigung mit der Materie "mp3-Datei" nötig - das ist jetzt nicht mehr so.
Die alte Methode habe ich als .pas Datei im Anhang angefügt - kompilierfähig ist sie so aber nicht (es ist einfach nur ne Kopie des alten Beitrags). Dies nur für den unwahrscheinlichen Fall, dass sie noch von jemandem gebraucht wird.
In Absprache mit den Bibliothekaren habe ich diesen Beitrag hier komplett neu geschrieben.

Grundlage hierfür ist die Unit MP3FileUtils (die sich übrigens u.a. aus dem alten FAQ-Beitrag entwickelt hat). Diese Unit liest aus einem MP3File die ID3-Tags und die MPEG-Audio-Eigenschaften aus. Diese Unit steht unter der LGPL, daher unterliegt die Software, die diese Unit verwendet, gewissen Einschränkungen.
Man könnte hier auch einfach andere ID3- und MPEG-Units verwenden (ggf. muss man den dann Code leicht modifizieren oder einige Properties löschen), die ggf. nicht unter der LGPL oder Lizenzen mit ähnlichen Einschränkungen stehen.
Als Alternativen seien hier UltimaTag von tommie-lie oder die AudioToolsLibrary genannt (allerdings stehen auch diese unter der LGPL).

Die hier vorgestellte Klasse TAudioFile darf aber beliebig verwendet, modifiziert und verbreitet werden. Insbesondere dürfen weitere "GetXXXInfo"-Proceduren eingefügt werden.


Die Frage "Wie ermittle ich den Titel einer mp3-Datei?" ist insofern nicht ganz so einfach zu beantworten, weil in vielen mp3-Files der Titel und andere Infos zweimal gespeichert wird, und beide Versionen müssen nicht übereinstimmen. Es gibt einmal den ID3Tag in der Version 1, und dann gibt es den ID3Tag in der Version 2.
Dass es gewisse Unterschiede bei den Tags gibt, kann man z.B. dann bemerken, wenn eine mp3-CD im Mediaplayer "schöne Titel" hat, aber das Display des Autoradios nur den Dateinamen anzeigt, obwohl es eine ID3-Tag-Anzeige hat. Diese beschränkt sich nämlich häufig auf die Version 1, oftmals ist in mp3-Dateien aber nur der weitaus flexiblere v2-Tag vorhanden.

Häufig interessiert es einen aber nicht, ob der Titel nun im ID3v1, oder im ID3v2-Tag steht, oder ob der eine oder der andere enthalten ist. Wenn man z.B. einfach einen Player schreiben möchte, dann möchte man nur den "Titel" wissen, nicht den ID3v1-Titel.
Oftmals ist es sogar egal, ob man gerade ein Mp3-File, oder eine Ogg-Datei abspielt. In beiden Fällen möchte man in seinem Player grundlegende Eigenschaften dieser Audio-Datei ermitteln um diese anzuzeigen.

Ich habe mich daher entschieden, eine Klasse TAudioFile zu schreiben:
ausblenden volle Höhe 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:
TAudioFile = class
    private
        FPfad: string;
        FDateiname: string;
        FOrdner: string;
        FDateiGroesse: Integer;

        // aus den ID3-Tags (oder andere Meta-Tags)
        FArtist: string;
        FTitel:  string;
        FAlbum:  string;
        FYear:   string;  // Ja, ein String!
        FGenre:  string;
        FKommentar: string;
        FLyrics: string;

        // aus den MPEG-Infos
        FDauer: Integer;
        FBitrate: word;
        Fvbr: boolean;
        FChannelMode: String;
        FSamplerate:Integer;

        procedure SetNA(filename: String);

        procedure GetMp3Info(filename:string);
        //procedure GetWMAInfo(filename:string);
        //procedure GetWAVInfo(filename:string);
        //procedure GetOGGInfo(filename:string);

    public
        property Pfad:      string read FDateiname;
        property Dateiname: string read FDateiname;
        property Ordner:    string read FOrdner;
        property DateiGroesse: Integer read FDateiGroesse;
        // aus den ID3-Tags (oder andere Meta-Tags)
        property Artist: string read FArtist;
        property Titel:  string read FTitel;
        property Album:  string read FAlbum;
        property Year:   string read FYear;  
        property Genre:  string read FGenre;
        property Kommentar: string read FKommentar;
        property Lyrics: string read FLyrics;
        // aus den MPEG-Infos
        property Dauer: Integer read FDauer;
        property Bitrate: word read FBitrate;
        property vbr: boolean read Fvbr;
        property ChannelMode: String read FChannelMode;
        property Samplerate:Integer read FSamplerate;

        constructor create;
        destructor destroy; override;
        procedure GetAudioData(filename:string);
    end;
Diese Klasse kann natürlich um weitere Eigenschaften erweitert werden, je nachdem, was man so für nötig hält.
Schon hier kann man erkennen, dass ich es darauf angelegt habe, die Klasse einfach erweiterbar zu halten. Public ist nur eine Routine: GetAudioData. Diese Routine ruft dann die richtige private-Routine auf, um aus einer Audio-Datei die gewünschten Informationen zu ermitteln - je nachdem, um welchen Dateityp es sich handelt:
ausblenden 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:
procedure TAudioFile.GetAudioData(filename:string);
begin
  try
      if (AnsiLowerCase(ExtractFileExt(filename)) = '.mp3')
        or (AnsiLowerCase(ExtractFileExt(filename)) = '.mp2')
        or (AnsiLowerCase(ExtractFileExt(filename)) = '.mp1')
      then
        GetMp3Info(Filename)
      else
        // Evtl. andere Dateitypen untersuchen
        //if AnsiLowerCase(ExtractFileExt(filename)) = '.wma' then
        //  GetWMAInfo(Filename)
        //else
        //  if AnsiLowerCase(ExtractFileExt(filename)) = '.wav' then
        //    GetWavInfo(Filename)
        //  else
        //    if AnsiLowerCase(ExtractFileExt(filename)) = '.ogg' then
        //      GetOGGInfo(Filename)
        //    else

        SetNA(filename);

  except
    SetNA(filename);
  end;
end;
Wir beschränken uns hier auf Mp3-Dateien:
ausblenden volle Höhe 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:
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:
procedure TAudioFile.GetMp3Info(filename:String);
var MpegInfo:TMpegInfo;  // Diese Typen sind in der Unit
    ID3v2Tag:TID3V2Tag;  // MP3Files deklariert
    ID3v1tag:TID3v1Tag;  //
    Stream: TFilestream;
begin
    FPfad      := filename;
    FOrdner    := ExtractFileDir(filename);
    FDateiname := ExtractFileName(Pfad);
    if NOT FileExists(filename) then exit;

    MpegInfo := TMpegInfo.Create;
    ID3v2Tag := TID3V2Tag.Create;
    ID3v1tag := TID3v1Tag.Create;

    // Datei als Stream öffnen und Daten auslesen
    stream := TFileStream.Create(filename, fmOpenRead or fmShareDenyWrite);
    ID3v1tag.ReadFromStream(stream);
    stream.Seek(0, sobeginning);
    ID3v2Tag.ReadFromStream(stream);

    if Not ID3v2Tag.exists then
      stream.Seek(0, sobeginning)
    else
      stream.Seek(ID3v2Tag.size, soFromBeginning);

    mpeginfo.LoadFromStream(Stream);
    stream.free;

    // ausgelesene Daten aufarbeiten, falls die Datei als mp3-File erkannt wurde:
    //------------------------------------------------------------------
    if mpeginfo.FirstHeaderPosition>-1 then begin
        // aus dem ID3 Tag
        //------------------------------------------------------------------
        // Bei Mp3-Dateien stehen ggf. die Informationen über Titel und Artist
        // doppelt in der Datei. Diese Daten müssen aber nciht norwendigerweise
        // übereinstimmen, und tun dies auch oft nicht.
        // Das liegt daran, dass der neuere ID3V2-Tag auch längere Strings speichern
        // kann als der ältere v1-tag, der auf 30 Zeichen pro Feld beschränkt ist.
        //
        // Da unsere Klasse TAudioFile aber nur ein Feld für Artist etc. hat, müssen
        // wir aus diesen beiden Informationen eine auswählen.
        // Hier hat der v2-tag Vorrang. Ist dieser nicht vorhanden, oder das entsprechende
        // Feld leer, wird der v1-tag genommen.
        if ID3v2Tag.artist <> '' then
          FArtist := ID3v2Tag.artist
        else
          if ID3v1tag.artist <> '' then
            FArtist := ID3v1tag.artist
          else
            FArtist := '';

        if ID3v2Tag.title <> '' then
          FTitel := ID3v2Tag.title
        else
          if ID3v1tag.title <> '' then
            FTitel := ID3v1tag.title
          else
            // An dieser Stelle nehmen wir den Dateinamen als Titel
            FTitel := Dateiname;

        if ID3v2Tag.album <> '' then
          FAlbum := ID3v2Tag.album
        else
          if ID3v1tag.album <> '' then
            FAlbum := ID3v1tag.album
          else FAlbum := '';

        if ID3v2Tag.Year <> '' then
          FYear := ID3v2Tag.Year
        else
          if ID3v1tag.Year <> '' then
            FYear := ID3v1tag.Year
          else FYear := '';

        if ID3v2Tag.genre <> '' then
          Fgenre := ID3v2Tag.genre
        else
          Fgenre := ID3v1tag.genre;

        if ID3v2Tag.Comment <> '' then
          FKommentar := ID3v2Tag.Comment
        else
          if ID3v1tag.Comment <> '' then
            FKommentar := ID3v1tag.Comment
          else
            FKommentar := '';

        // Lyrics können nur im v2-Tag gespeichert werden.
        FLyrics := ID3v2Tag.GetLyrics('*','*');

        // aus den MPEG-Infos
        FDauer        := MpegInfo.dauer;
        FBitrate      := MpegInfo.bitrate;
        Fvbr          := MpegInfo.vbr;
        FDateiGroesse := MpegInfo.FileSize;
        FSampleRate   := MpegInfo.samplerate;

        case MpegInfo.channelmode of
            0: FChannelmode := 'Stereo';
            1: FChannelmode := 'Joint Stereo';
            2: FChannelmode := 'Dual Channel Stereo';
            3: FChannelmode := 'Mono';
            else FChannelmode := 'Unknown';
        end;
    end else
    begin
        // andernfalls: Kein gültiges Mp3File.
        // Standardwerte nehmen
        SetNA(filename);
    end;
    MpegInfo.Free;
    Id3v2Tag.Free;
    Id3v1Tag.Free;
end;


Um nun in einer Anwendung den Titel eines Liedes anzeigen zu lassen, genügen vier einfache Zeilen:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
procedure TForm1.Button1Click(Sender: TObject);
var aAudioFile: tAudioFile;
begin
  aAudioFile := TAudioFile.create;
  aAudioFile.GetAudioData('Test1.mp3');
  showmessage(aAudioFile.Artist + ' - ' + aAudioFile.Titel);
  aAudioFile.Free;
end;


Die komplette Unit ist im Anhang, dort sind auch die Konstruktor, Destruktor und die SetNA-Prozedur mit drin. Einfach diese Unit im Projekt-Ordner speichern, in die Uses-Klausel aufnehmen und benutzen. Einfacher gehts kaum, oder?

Außerdem benötigt wird die Unit MP3FileUtils.


_UAudioFileFAQ.pas  (8.02 KB) Download (Rev 0)
 (2029x, 2029x gesamt)
Beschreibung: Die hier beschriebene Unit.
FAQ_BACKUP.pas  (29.81 KB) Download (Rev 0)
 (1191x, 1191x gesamt)
Beschreibung: alter FAQ-Beitrag, der nicht mehr verwendet werden sollte.
_________________
We are, we were and will not be.