Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Sonstige Fragen zu Delphi (https://www.delphipraxis.net/19-sonstige-fragen-zu-delphi/)
-   -   Delphi Sehr großes Problem mit den verschiedenen Audiotags (https://www.delphipraxis.net/75463-sehr-grosses-problem-mit-den-verschiedenen-audiotags.html)

Nils_13 19. Aug 2006 17:17


Sehr großes Problem mit den verschiedenen Audiotags
 
Hi,

ich füge Lieder zu einer Playlist so zu:
Delphi-Quellcode:
TID3Tag = record
    ID: Array[0..3] of Char;
    Titel: Array[0..30] of Char;
    Artist: Array[0..30] of Char;
    Album: Array[0..30] of Char;
    Year: Array[0..4] of Char;
    Comment: Array[0..30] of Char;
    Genre: Byte;
    Filename: String;
    Endung: String;
  end;

  private
    { Private declarations }
    fFiles          : Array of TID3Tag;

// ...

procedure TfrmMain.AddFiles(files : Array of String);
var
  len : integer;
  i  : integer;
  s  : String;
begin
  len := Length(fFiles);
  SetLength(fFiles, len + Length(files));
  for i := 0 to pred(length(files)) do
  begin
    fFiles[len+i] := ReadID3Tag(files[i]);
  end;
  UpdateListBox();
end;

procedure TfrmMain.UpdateListBox;
var
  i : integer;
begin
  lbList.Items.BeginUpdate;
  lbList.Clear;
  for i := 0 to pred(length(fFiles)) do
  begin
    lbList.Items.Add(trim(fFiles[i].Artist) + ' - ' + trim(fFiles[i].Titel));
  end;
  lbList.Items.EndUpdate;
end;
Das Problem ist, dass ich für jedes Format anders die Tags auslesen muss. Der obige Code liest nur MP3 aus. Ich habe überall im Code querverteilt zugriffe auf fFiles (Mp3-spezifisch), deshalb die Frage: Gibt es eine Möglichkeit TId3Tag so zu erweitern, dass es alle Formate unterstützt und ich nicht alles umschreiben bzw. erweitern muss ?

Gausi 19. Aug 2006 17:25

Re: Sehr großes Problem mit den verschiedenen Audiotags
 
Ich habe hier mal ein Prinzip vorgestellt, wie man es machen kann.

Nils_13 19. Aug 2006 18:27

Re: Sehr großes Problem mit den verschiedenen Audiotags
 
Werde ich mir merken.

Ich hab es jetzt einfach gelöst: Einfach die ganzen Daten von allen Formaten in den Record packen, die Ladeprozedur erweritern und das war's dann auch schon.

Nils_13 20. Aug 2006 14:23

Re: Sehr großes Problem mit den verschiedenen Audiotags
 
Die Mp3 Dateien werden zwar korrekt ausgelesen, aber bei WMA-Dateien wird in die Listbox ein Strich hinzugefügt, statt Artist - Titel. AddFiles ruft für die Items ReadID3Tag (ich weiß, der Titel der Prozedur passt nicht mehr) auf und ReadId3Tag entscheidet weiter:
Delphi-Quellcode:
TID3Tag = record
  // MP3
  ID: Array[0..3] of Char;
  Titel: Array[0..30] of Char;
  Artist: Array[0..30] of Char;
  Album: Array[0..30] of Char;
  Year: Array[0..4] of Char;
  Comment: Array[0..30] of Char;
  Genre: Byte;
  Filename: String;
  Endung: String;
  // WMA
  FValid: Boolean;
  FFileSize: Integer;
  FChannelModeID: Byte;
  FSampleRate: Integer;
  FDuration: Double;
  FBitRate: Integer;
  FTitle: WideString;
  FArtist: WideString;
  FAlbum: WideString;
  FTrack: Integer;
  FYear: WideString;
  FGenre: WideString;
  FComment: WideString;
end;


procedure TfrmMain.AddFiles(files : Array of String);
var
  len : integer;
  i  : integer;
begin
  len := Length(fFiles);
  SetLength(fFiles, len + Length(files));
  for i := 0 to high(files) do
  begin
    fFiles[len+i] := ReadID3Tag(files[i]);
  end;
end;

procedure TfrmMain.xiAddClick(Sender: TObject);
var
  i : Integer;
begin
  if op1.Execute then
  begin
    setlength(f, op1.files.count);
    for i := 0 to pred(op1.files.count) do
    begin
      f[i] := op1.files[i];
    end;
    AddFiles(f);
  end;
end;

function TfrmMain.ReadID3Tag(const FileName: String): TID3Tag;
begin
  if LowerCase(ExtractFileExt(Filename)) = '.mp3' then
  begin
    FillChar(Result, SizeOf(Result), 0);
    with TFileStream.Create(FileName, fmOpenRead or fmShareDenyNone) do
    try
      Position := Size - 128;
      Read(Result.ID, 3);
      Read(Result.Titel, 30);
      Read(Result.Artist, 30);
      Read(Result.Album, 30);
      Read(Result.Year, 4);
      Read(Result.Comment, 30);
      Read(Result.Genre, 1);
      Result.Filename := Filename;
      Result.Endung  := LowerCase(ExtractFileExt(Filename));
    finally
      Free;
    end;
  end;

  if LowerCase(ExtractFileExt(Filename)) = '.wma' then
  begin
    if WMAfile.ReadFromFile(Filename) then
    if WMAfile.Valid then
    begin
      Result.FTitle := WMAfile.Title;
      Result.FArtist := WMAfile.Artist;
      Result.Filename := Filename;
      Result.Endung  := LowerCase(ExtractFileExt(Filename));
    finally
      Free;
    end;
  end;
end;

Gausi 20. Aug 2006 20:26

Re: Sehr großes Problem mit den verschiedenen Audiotags
 
Von welchem Typ ist denn die Variable WMAFile? Was ist das für eine Klasse, und wie liest diese die WMA-Tags aus der Datei aus? Haben die getesteten WMA-Dateien überhaupt Meta-Tags?

Jelly 20. Aug 2006 20:49

Re: Sehr großes Problem mit den verschiedenen Audiotags
 
Zitat:

Zitat von Nils_13
Das Problem ist, dass ich für jedes Format anders die Tags auslesen muss. Der obige Code liest nur MP3 aus.

Ja, aber warum arbeitest Du denn überhaupt mit Records.

Ich würd in etwa folgende Klassenhierarchie aufbauen (exemplarisch nur mit der Title Eigenschaft) :

Delphi-Quellcode:
type
  TAudioFile = class
  private
    FTitle : string ;
    FMusicFilename : string ;
  public
    constructor Create (AMusicFilename : string) ;
    property Title : string read FTitle write FTitle ;
    property MusicFilename : string read FMusicFilename write FMusicFilename ;

    procedure Read ; virtual ; abstract ;
    procedure Write ; virtual ; abstract ;
  end ;

  TMP3File = class (TAudioFile)
  public
     procedure Read ;
     procedure Write ;
  end ;

  // TWAVFile = class (TAudioFile) ... analog TMP3File
 

implementation
 
constructor TAudioFile.Create (AMusicFilename : string) ;
begin
     FMusicFilename := AMusicFilename ;
end ;

procedure TMP3Fil.Read ;
begin
     // Hier die Informationen auslesen, und in die geeigneten Properties (Title) ablegen
end ;

procedure TMP3Fil.Write ;
begin
     // Hier die Properties (Title) speichern
end ;
Die ganzen Audiofiles, egal in welchem Format sie vorliegen, würd ich in einer TObjectList speichern. Somit kannst du ganz bequem Audiofiles wie in einem Array verwalten, und musst nur jeweils die Read bzw. Write Methode aufrufen.

Vergiss also die Records und bau auf Objekte auf.

Nils_13 21. Aug 2006 09:15

Re: Sehr großes Problem mit den verschiedenen Audiotags
 
Ok, aber bedeutet das dann nicht gleich wieder eine komplette Programmüberarbeitung ?
Wie würdest du es dann denn auslesen ? Außerdem ist WmaFile eine Unit. Siehe hier.

Jelly 21. Aug 2006 09:25

Re: Sehr großes Problem mit den verschiedenen Audiotags
 
Naja, so gross ist dein Code ja bislang noch nicht. Durch die Überarbeitung gewinnst Du aber deutlich an Flexibilität.

Nils_13 21. Aug 2006 09:26

Re: Sehr großes Problem mit den verschiedenen Audiotags
 
Der Code ist bisher über 2742 Zeilen lang. Aber wie würdest du denn dann die Tags auslesen ?

Bisher mach ich das so:
Delphi-Quellcode:
if fFiles[playindex].Endung = '.mp3' then             <--
begin
  PlayFile(playindex);
  with fFiles[playindex] do   <--
  begin
    s := Artist + ' - ' + Titel + ' [' + trim(DurationMinSec(IntToStr(GetSongLength))) + ']';
    lLauftext.Text   := s;
    lLauftext.Active := True;
    lLauftext.Transparent := True;
    Application.Title := s;
   end;
   pause            := True;
end;

Gausi 21. Aug 2006 09:31

Re: Sehr großes Problem mit den verschiedenen Audiotags
 
Klar bedeutet das teilweise eine komplette Programmänderung, und solche sind immer lästig. Ich kann dir aber aus eigener Erfahrung sagen, dass es sehr sinnvoll ist, das Programm von vorneherein so zu gestalten, dass auch gröbere Änderungen im Funktionsumfang nur geringe Änderungen am Code nötig machen. Also: Besser jetzt alles einmal ändern, und später Ruhe damit haben, als sich ständig damit rumärgern zu müssen.

Ob du nun eine Klassenhierarchie aufbaust, wie Jelly es vorgeschlagen hat, oder nur eine Klasse (mein Vorschlag) mit einer Public-Lese/Schreibroutine, die dann selbst entscheidet, welche Private-Routine (mp3/ogg/wma/...) die richtige ist, bleibt dir überlassen ;-). Wenn es ein reiner Player ohne Tag-Editor werden soll, würde ich meine Methode vorziehen - zur Verarbeitung neuer Dateitypen musst du dann nur die Klasse selbst erweitern, der ganze Rest bleibt gleich.
Möchtest du auch einen umfangreichen Tag-Editor für die unterschiedlichen Typen implementieren, wäre wohl Jellys Methode besser, da mein Verfahren keinen Zugriff auf die internen Tag-Typen (z.B. id2v1 / v2) bietet.

Nachtrag: knappe 3000 Zeilen? Ich durfte mich letztens durch knapp 20.000 Zeilen quälen, weil ich einen Umbau vorgenommen habe. Und das ohne die Hilfe, dass eine vergessene Stelle einen Compilierfehler ergibt... :pale:

Edit: Mit der ATL-WMA-Unit hatte ich mal ein ganz ähnliches Problem. Die hat iirc einen kleinen Fehler, bzw. versteht die neue WMA-Version nicht richtig. Ich hab den glaube ich ausgebügelt, werde mal danach suchen, und dann hier posten ;-)

Jelly 21. Aug 2006 09:32

Re: Sehr großes Problem mit den verschiedenen Audiotags
 
Das Lesen der Tags hast Du doch schon wunderschön über TFileStream gelöst. Wie die Tags bei anderen Audioformaten aussehen, kann ich Dir nicht sagen, aber für MP3 solltest Du den Code quasi übernehmen können. Einfach in die Read Methode des TMP3File kopieren, und die Properties deiner Klasse füllen:

Delphi-Quellcode:
procedure TMP3Fil.Read ;
begin
    with TFileStream.Create(MusicFileName, fmOpenRead or fmShareDenyNone) do
    try
      Position := Size - 128;
      Read(ID, 3);
      Read(Titel, 30);
      Read(Artist, 30);
      Read(Album, 30);
      Read(Year, 4);
      Read(Comment, 30);
      Read(Genre, 1);
    finally
      Free;
    end;
end ;
So in etwa... Musst natürlich deine Basisklasse noch um die anderen Eigenschaften erweitern, bislang steht ja nur Title drin.

Nils_13 21. Aug 2006 09:34

Re: Sehr großes Problem mit den verschiedenen Audiotags
 
Zitat:

Nachtrag: knappe 3000 Zeilen? Ich durfte mich letztens durch knapp 20.000 Zeilen quälen, weil ich einen Umbau vorgenommen habe. Und das ohne die Hilfe, dass eine vergessene Stelle einen Compilierfehler ergibt... :pale:
:shock:



Der Player soll einen Tageditor besitzen, deshalb ziehe ich Jellys Methode vor.

Ahh..der rote Kasten, danke an alle, ich stürz mich ins coden :pale:

Gausi 21. Aug 2006 09:44

Re: Sehr großes Problem mit den verschiedenen Audiotags
 
Ich nutze folgende leicht korrigierte Version der ATL-WMA-Unit. Eine Funktion muss um eine Zeile ergänzt werden:
Delphi-Quellcode:
procedure ReadTagExtended(const Source: TStream; var Tag: TagData);
var
  Iterator1, Iterator2, FieldCount, DataSize, DataType: Word;
  FieldName, FieldValue: WideString;
begin
  { Read extended tag data }
  Source.ReadBuffer(FieldCount, SizeOf(FieldCount));
  for Iterator1 := 1 to FieldCount do
  begin
    { Read field name }
    Source.ReadBuffer(DataSize, SizeOf(DataSize));
    FieldName := ReadFieldString(Source, DataSize);
    { Read value data type }
    Source.ReadBuffer(DataType, SizeOf(DataType));
    { Read field value only if string }
    if DataType = 0 then
    begin
      Source.ReadBuffer(DataSize, SizeOf(DataSize));
      FieldValue := ReadFieldString(Source, DataSize);
    end
    else
     begin
      //***************************
      // Added By Daniel Gausi Gaußmann, 16.3.2006
      // Dass diese Zeile wichtig ist, sollte leicht verständlich sein ;-)
      // DataSize hat sonst einen falschen Wert, und es wird falsch gesprungen
      Source.ReadBuffer(DataSize, SizeOf(DataSize));
      //***************************
      Source.Seek(DataSize, soFromCurrent);
    end;
   
    { Set corresponding tag field if supported }
    for Iterator2 := 1 to WMA_FIELD_COUNT do
      if UpperCase(Trim(FieldName)) = WMA_FIELD_NAME[Iterator2] then
        Tag[Iterator2] := FieldValue;
  end;
end;

Nils_13 21. Aug 2006 10:02

Re: Sehr großes Problem mit den verschiedenen Audiotags
 
Dann muss aber Mp3File noch ein Create bekommen, damit ich damit arbeiten kann, oder ?
Delphi-Quellcode:
  TAudioFile = class
  private
    //Mp3
    FID: String;
    FTitel: String;
    FArtist: String;
    FAlbum: String;
    FYear: String;
    FComment: String;
    FGenre: Byte;
    FFilename: String;
    FEndung: String;
  public
    constructor Create (AMusicFilename : string) ;
    property ID : string read FID write FID;
    property Titel : string read FTitel write FTitel;
    property Artist : string read FArtist write FArtist;
    property Album : string read FAlbum write FAlbum;
    property Year : string read FYear write FYear;
    property Comment : string read FComment write FComment;
    property Genre : byte read FGenre write FGenre;
    property Filename : string read FFilename write FFilename;
    property Endung : string read FEndung write FEndung;

    function ReadTags(const FileName: String): TAudioFile;
  //  procedure Write;
  end;

  TMP3File = class(TAudioFile)
  public
    procedure ReadMp3;
    procedure Write;
  end;

constructor TAudioFile.Create (AMusicFilename: String);
begin
  FFilename := AMusicFilename;
end;

function TAudioFile.ReadTags(const FileName: String): TAudioFile;
begin
  MP3File := TMP3File.
  if LowerCase(ExtractFileExt(Filename)) = '.mp3' then

end;

procedure TMP3File.ReadMp3;
begin
  with TFileStream.Create(FileName, fmOpenRead or fmShareDenyNone) do
  try
    Position := Size - 128;
    Read(FID, 3);
    Read(FTitel, 30);
    Read(FArtist, 30);
    Read(FAlbum, 30);
    Read(FYear, 4);
    Read(FComment, 30);
    Read(FGenre, 1);
  finally
    Free;
  end;
end;

Jelly 21. Aug 2006 10:12

Re: Sehr großes Problem mit den verschiedenen Audiotags
 
Zitat:

Zitat von Nils_13
Dann muss aber Mp3File noch ein Create bekommen, damit ich damit arbeiten kann, oder ?

Nein, der Constructor ist doch schon in TAudioFile definiert.

Und nochwas... Nenn dein Methode nicht ReadMP3. Du implementierst doch gerne eine Methode die die Tags ausliest, und dies nicht auf mp3 beschränkt... Wie willst du denn bei WMA deine Methode nennen... Etwa ReadWMA. Dadurch ist dein ganzes Klassenmodell für die Katz.

Du speicherst später deine Songs z.B. in einer TObjectList... Jetzt willst du für alle Songs die Tags auslesen, unabhängig in welchem Format sie vorliegen, ob mp3 oder wma. Nach deiner Methode müsstest du etwa so vorgehen:

Delphi-Quellcode:
var
  AllSongs : TObjectList ;

procedure ReadAll ;
begin
     for i := 0 to AllSongs.count-1 do begin
        if AllSongs[i] is Tmp3File then (AllSongs[i] as Tmp3File).ReadMp3 ;
        if AllSongs[i] is TvmaFile then (AllSongs[i] as Tmp3File).Readwma ;
     end ;
end ;
Wenn Du morgen ein weiteres Audioformat aufnehmen willst, musst in deinem gesamten Quellcodes (immerhin ja doch 3000 Zeilen angeblich), darauf achten, dass du da neue Format auch korrekt ausliest... Und wehe, du vergisst dies an einer Stelle.

Stattdessen, wenn jede Klasse eine eigene Read Methode deklariert, läuft das alles wesentlich einfacher ab:
Delphi-Quellcode:
var
  AllSongs : TObjectList ;

procedure ReadAll ;
begin
     for i := 0 to AllSongs.count-1 do
        (AllSongs[i] as TAudioFile).Read ;
end ;
und gut ist. Neue Audioformate werden automatisch berücksichtigt.

Lies Dir mal bischen was über Klassenvererbund und Polymorphismus durch. Das sind eigentlich grundlegende Dinge, die du in jeder Delphi Anwendung immer wieder benötigst.

Nils_13 21. Aug 2006 10:16

Re: Sehr großes Problem mit den verschiedenen Audiotags
 
Ok, aber wozu brauch ich dann überhaupt noch TMP3File ? Das wird dann doch alles in TAudioFile.Read geregelt.

Jelly 21. Aug 2006 10:18

Re: Sehr großes Problem mit den verschiedenen Audiotags
 
Nein, geregelt wird das in Tmp3File... Und weil in TAudioFile die Methode Read als abstract definiert ist, ist auch sichergestellt, dass in Tmp3File die Methode auch wirklich definiert werden muss.

Nils_13 21. Aug 2006 10:21

Re: Sehr großes Problem mit den verschiedenen Audiotags
 
Ahso, das heißt mit der Schleife geht mir das Programm durch alle Reads durch ?

Jelly 21. Aug 2006 10:23

Re: Sehr großes Problem mit den verschiedenen Audiotags
 
Abhängig vom Klassentyp, wird die read Methode des Objekts aufgerufen, falls definiert.

Zieh dir mal das Tutorial von Luckie rein, über OOP.

Nils_13 21. Aug 2006 10:30

Re: Sehr großes Problem mit den verschiedenen Audiotags
 
Ok, hier ist der hoffentlich korrekte Code:
Delphi-Quellcode:
TAudioFile = class
  private
    //Mp3
    FID: String;
    FTitel: String;
    FArtist: String;
    FAlbum: String;
    FYear: String;
    FComment: String;
    FGenre: Byte;
    FFilename: String;
    FEndung: String;
  public
    constructor Create (AMusicFilename : string) ;
    property ID : string read FID write FID;
    property Titel : string read FTitel write FTitel;
    property Artist : string read FArtist write FArtist;
    property Album : string read FAlbum write FAlbum;
    property Year : string read FYear write FYear;
    property Comment : string read FComment write FComment;
    property Genre : byte read FGenre write FGenre;
    property Filename : string read FFilename write FFilename;
    property Endung : string read FEndung write FEndung;

    procedure Read; virtual; abstract;
    procedure Write; virtual; abstract;
  end;

  TMP3File = class(TAudioFile)
  public
    procedure Read;
    procedure Write;
  end;

...

constructor TAudioFile.Create (AMusicFilename: String);
begin
  FFilename := AMusicFilename;
end;

procedure TfrmMain.ReadAll;
var i : Integer;
begin
  for i := 0 to AllSongs.count-1 do
    (AllSongs[i] as TAudioFile).Read;
end;

procedure TMP3File.Read;
begin
  with TFileStream.Create(FileName, fmOpenRead or fmShareDenyNone) do
  try
    Position := Size - 128;
    Read(FID, 3);
    Read(FTitel, 30);
    Read(FArtist, 30);
    Read(FAlbum, 30);
    Read(FYear, 4);
    Read(FComment, 30);
    Read(FGenre, 1);
  finally
    Free;
  end;
end;

Jelly 21. Aug 2006 10:35

Re: Sehr großes Problem mit den verschiedenen Audiotags
 
Das kommt hin... Du musst Dir nur noch Gedanken machen, wie du die Songs in den Speicher lädst. TObjectList ist nur ein Vorschlag von mir. Die List würd ich aber nicht in der gleichen Unit unterbringen.

Nils_13 21. Aug 2006 10:36

Re: Sehr großes Problem mit den verschiedenen Audiotags
 
Hm, ich hatte früher dafür ein einfaches Array, warum eigentlich nicht in die gleiche Unit ? Außerdem ist Filename: String nicht gut, wenn ich mehrere Lieder hinzufügen will, knallt es.

Jelly 21. Aug 2006 10:47

Re: Sehr großes Problem mit den verschiedenen Audiotags
 
Zitat:

Zitat von Nils_13
Außerdem ist Filename: String nicht gut, wenn ich mehrere Lieder hinzufügen will, knallt es.

Zeig mal bischen Code... WAS knallt :gruebel:

Nils_13 21. Aug 2006 10:49

Re: Sehr großes Problem mit den verschiedenen Audiotags
 
Hier wird es knallen:
Delphi-Quellcode:
procedure TfrmMain.AddFiles(files : Array of String);
var
  len : integer;
  i  : integer;
begin
  len := Length(fFiles);
  SetLength(fFiles, len + Length(files));
  for i := 0 to high(files) do
  begin
    fFiles[len+i] := ReadID3Tag(files[i]);    <-- es müssen mehrere hintereinander ausgelesen werden können, das ist noch der alte Code, aber der neue macht das wahrscheinlich nicht mit
  end;
  if lbList.Count > 17 then
    xiScroll.Visible := True
  else
    xiScroll.Visible := False;
  UpdateListBox();
end;
Nachtrag:
Delphi-Quellcode:
fFiles : Array of TAudioFile;

Jelly 21. Aug 2006 11:41

Re: Sehr großes Problem mit den verschiedenen Audiotags
 
Ich würd weiterhin auf diese Arrays verzichten und bei TObjectList bleiben.

Delphi-Quellcode:
TfrmMain = class (TForm)
...
public
  MusicFiles : TObjectList ;
  procedure AddFiles (Files : array of string) ;
end ;


implementation

procedure TfrmMain.AddFiles (Files : array of string) ;
var
 AudioFile : TAudioFile ;
begin
     for i := low(Files) to high(Files) do begin
        if lowercase(ExtractFileExt(Files[i])) = '.mp3' then
           AudioFile := Tmp3File.create (Files[i])
        else if lowercase(ExtractFileExt(Files[i])) = '.wma' then
           AudioFile := TwmaFile.create (Files[i])
        else
           raise exception.create ('Fileextension unknown for Audiofile') ;
        MusicFiles.Add (AudioFile) ;  // MusicFiles nicht vergessen zu initialisieren
     end ;
end ;
end.


Alle Zeitangaben in WEZ +1. Es ist jetzt 08:14 Uhr.

Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz