Einzelnen Beitrag anzeigen

Alex_ITA01

Registriert seit: 22. Sep 2003
1.115 Beiträge
 
Delphi 12 Athens
 
#1

ID3v1-Tag, ID3v2-Tag und MPEG-Header einer MP3 auslesen

  Alt 27. Sep 2005, 11:10
Hallo erstmal,
ich schon wieder
Naja, wie gestern versprochen, hier der Source für mein Programm ID3Tag-Verwalter.
Nur mal kurz zu meinem Beweggründen, warum ich den Source hier rein stelle. Ihr (DP) habt mir schon ziemlich oft geholfen und ich hoffe natürlich das ihr das weiter so macht
Daher will ich anderen genauso die Chance geben etwas zu lernen bzw. zu verbessern. Schließlich muss man sich in einem solchen Forum helfen und helfen lassen (Nobody is perfect...)

Ich habe den Source ein wenig abgespeckt, weil dort meine Kompos und ein ziemlich großer Standard von mir drinne war aber in dem nachfolgenden Beispiel ist alles gezeigt.
Lesen und Schreiben des ID3v1 + ID3v2-Tag's und lesen des MPEG-Headers.
Ihr werdet sicher damit etwas anfangen können (nachdem so viele gestern schon nach den Source gefragt haben )

Wenn ihr Verbesserungen oder Änderungen am Source vornehmt, könnt ihr mir ja Bescheid sagen.Vielleicht hat es ja auch Nutzen für mich.

MFG
Alex

ID3v1.pas
Delphi-Quellcode:
unit ID3v1;

interface

uses
  Classes, SysUtils;

const
  MAX_MUSIC_GENRES = 148;
  DEFAULT_GENRE = 255;

  TAG_VERSION_1_0 = 1;
  TAG_VERSION_1_1 = 2;

var
  MusicGenre: array [0..MAX_MUSIC_GENRES - 1] of string;

type
  String04 = String[4];
  String30 = String[30];

  TID3v1 = class(TObject)
    private
      { Private declarations }
      FExists : Boolean;
      FVersionID : Byte;
      FTitle : String30;
      FArtist : String30;
      FAlbum : String30;
      FYear : String04;
      FComment : String30;
      FTrack : Byte;
      FGenreID : Byte;
      procedure FSetTitle (const NewTitle : String30);
      procedure FSetArtist (const NewArtist : String30);
      procedure FSetAlbum (const NewAlbum : String30);
      procedure FSetYear (const NewYear : String04);
      procedure FSetComment(const NewComment : String30);
      procedure FSetTrack (const NewTrack : Byte);
      procedure FSetGenreID(const NewGenreID : Byte);
      function FGetGenre: String;
    public
      { Public declarations }
      constructor Create;
      procedure ResetData;
      function ReadFromFile (const FileName: string): Boolean;
      function RemoveFromFile(const FileName: string): Boolean;
      function SaveToFile (const FileName: string): Boolean;
      property Exists : Boolean read FExists;
      property VersionID : Byte read FVersionID;
      property Title : String30 read FTitle write FSetTitle;
      property Artist : String30 read FArtist write FSetArtist;
      property Album : String30 read FAlbum write FSetAlbum;
      property Year : String04 read FYear write FSetYear;
      property Comment : String30 read FComment write FSetComment;
      property Track : Byte read FTrack write FSetTrack;
      property GenreID : Byte read FGenreID write FSetGenreID;
      property Genre : String read FGetGenre;
  end;
{ --------------------------------------------------------------------------- }
implementation
{ --------------------------------------------------------------------------- }
type
  TagRecord = record
    Header : array [1..3] of Char;
    Title : array [1..30] of Char;
    Artist : array [1..30] of Char;
    Album : array [1..30] of Char;
    Year : array [1..4] of Char;
    Comment : array [1..30] of Char;
    Genre : Byte;
  end;
{ --------------------------------------------------------------------------- }
function ReadTag(const FileName: string; var TagData: TagRecord): Boolean;
var
  SourceFile : File;
begin
  try
    Result := true;
    AssignFile(SourceFile, FileName);
    FileMode := 0;
    Reset(SourceFile, 1);
    Seek(SourceFile, FileSize(SourceFile) - 128);
    BlockRead(SourceFile, TagData, 128);
    CloseFile(SourceFile);
  except
    Result := false;
  end;
end;
{ --------------------------------------------------------------------------- }
function RemoveTag(const FileName: string): Boolean;
var
  SourceFile : File;
begin
  {$IFDEF MSWINDOWS}
  try
    Result := true;
    FileSetAttr(FileName, 0);
    AssignFile(SourceFile, FileName);
    FileMode := 2;
    Reset(SourceFile, 1);
    Seek(SourceFile, FileSize(SourceFile) - 128);
    Truncate(SourceFile);
    CloseFile(SourceFile);
  except
    Result := false;
  end;
  {$ENDIF}
end;
{ --------------------------------------------------------------------------- }
function SaveTag(const FileName: String; TagData: TagRecord): Boolean;
var
  SourceFile : File;
begin
  try
    Result := true;
    FileSetAttr(FileName, 0);
    AssignFile(SourceFile, FileName);
    FileMode := 2;
    Reset(SourceFile, 1);
    Seek(SourceFile, FileSize(SourceFile));
    BlockWrite(SourceFile, TagData, SizeOf(TagData));
    CloseFile(SourceFile);
  except
    Result := false;
  end;
end;
{ --------------------------------------------------------------------------- }
function GetTagVersion(const TagData: TagRecord): Byte;
begin
  Result := TAG_VERSION_1_0;
  if ((TagData.Comment[29] = #0) and (TagData.Comment[30] <> #0)) or
    ((TagData.Comment[29] = #32) and (TagData.Comment[30] <> #32)) then
    Result := TAG_VERSION_1_1;
end;
{ --------------------------------------------------------------------------- }
procedure TID3v1.FSetTitle(const NewTitle: String30);
begin
  FTitle := TrimRight(NewTitle);
end;
{ --------------------------------------------------------------------------- }
procedure TID3v1.FSetArtist(const NewArtist: String30);
begin
  FArtist := TrimRight(NewArtist);
end;
{ --------------------------------------------------------------------------- }
procedure TID3v1.FSetAlbum(const NewAlbum: String30);
begin
  FAlbum := TrimRight(NewAlbum);
end;
{ --------------------------------------------------------------------------- }
procedure TID3v1.FSetYear(const NewYear: String04);
begin
  FYear := TrimRight(NewYear);
end;
{ --------------------------------------------------------------------------- }
procedure TID3v1.FSetComment(const NewComment: String30);
begin
  FComment := TrimRight(NewComment);
end;
{ --------------------------------------------------------------------------- }
procedure TID3v1.FSetTrack(const NewTrack: Byte);
begin
  FTrack := NewTrack;
end;
{ --------------------------------------------------------------------------- }
procedure TID3v1.FSetGenreID(const NewGenreID: Byte);
begin
  FGenreID := NewGenreID;
end;
{ --------------------------------------------------------------------------- }
function TID3v1.FGetGenre : String;
begin
  Result := '';
  if FGenreID in [0..MAX_MUSIC_GENRES - 1] then Result := MusicGenre[FGenreID];
end;
{ --------------------------------------------------------------------------- }
constructor TID3v1.Create;
begin
  inherited;
  ResetData;
end;
{ --------------------------------------------------------------------------- }
procedure TID3v1.ResetData;
begin
  FExists := false;
  FVersionID := TAG_VERSION_1_0;
  FTitle := '';
  FArtist := '';
  FAlbum := '';
  FYear := '';
  FComment := '';
  FTrack := 0;
  FGenreID := DEFAULT_GENRE;
end;
{ --------------------------------------------------------------------------- }
function TID3v1.ReadFromFile(const FileName: String): Boolean;
var
  TagData : TagRecord;
begin
  ResetData;
  Result := ReadTag(FileName, TagData);
  if (Result) and (TagData.Header = 'TAG') then
  begin
    FExists := true;
    FVersionID := GetTagVersion(TagData);
    FTitle := TrimRight(TagData.Title);
    FArtist := TrimRight(TagData.Artist);
    FAlbum := TrimRight(TagData.Album);
    FYear := TrimRight(TagData.Year);
    if FVersionID = TAG_VERSION_1_0 then
      FComment := TrimRight(TagData.Comment)
    else
    begin
      FComment := TrimRight(Copy(TagData.Comment, 1, 28));
      FTrack := Ord(TagData.Comment[30]);
    end;
    FGenreID := TagData.Genre;
  end;
end;
{ --------------------------------------------------------------------------- }
function TID3v1.RemoveFromFile(const FileName: String): Boolean;
var
  TagData : TagRecord;
begin
  Result := ReadTag(FileName, TagData);
  if (Result) and (TagData.Header = 'TAG') then Result := RemoveTag(FileName);
end;
{ --------------------------------------------------------------------------- }
function TID3v1.SaveToFile(const FileName: String): Boolean;
var
  TagData : TagRecord;
begin
  FillChar(TagData, SizeOf(TagData), 0);
  TagData.Header := 'TAG';
  Move(FTitle[1], TagData.Title, Length(FTitle));
  Move(FArtist[1], TagData.Artist, Length(FArtist));
  Move(FAlbum[1], TagData.Album, Length(FAlbum));
  Move(FYear[1], TagData.Year, Length(FYear));
  Move(FComment[1], TagData.Comment, Length(FComment));
  if FTrack > 0 then
  begin
    TagData.Comment[29] := #0;
    TagData.Comment[30] := Chr(FTrack);
  end;
  TagData.Genre := FGenreID;
  Result := (RemoveFromFile(FileName)) and (SaveTag(FileName, TagData));
end;
{ --------------------------------------------------------------------------- }
initialization
begin
  MusicGenre[0] := 'Blues';
  MusicGenre[1] := 'Classic Rock';
  MusicGenre[2] := 'Country';
  MusicGenre[3] := 'Dance';
  MusicGenre[4] := 'Disco';
  MusicGenre[5] := 'Funk';
  MusicGenre[6] := 'Grunge';
  MusicGenre[7] := 'Hip-Hop';
  MusicGenre[8] := 'Jazz';
  MusicGenre[9] := 'Metal';
  MusicGenre[10] := 'New Age';
  MusicGenre[11] := 'Oldies';
  MusicGenre[12] := 'Other';
  MusicGenre[13] := 'Pop';
  MusicGenre[14] := 'R&B';
  MusicGenre[15] := 'Rap';
  MusicGenre[16] := 'Reggae';
  MusicGenre[17] := 'Rock';
  MusicGenre[18] := 'Techno';
  MusicGenre[19] := 'Industrial';
  MusicGenre[20] := 'Alternative';
  MusicGenre[21] := 'Ska';
  MusicGenre[22] := 'Death Metal';
  MusicGenre[23] := 'Pranks';
  MusicGenre[24] := 'Soundtrack';
  MusicGenre[25] := 'Euro-Techno';
  MusicGenre[26] := 'Ambient';
  MusicGenre[27] := 'Trip-Hop';
  MusicGenre[28] := 'Vocal';
  MusicGenre[29] := 'Jazz+Funk';
  MusicGenre[30] := 'Fusion';
  MusicGenre[31] := 'Trance';
  MusicGenre[32] := 'Classical';
  MusicGenre[33] := 'Instrumental';
  MusicGenre[34] := 'Acid';
  MusicGenre[35] := 'House';
  MusicGenre[36] := 'Game';
  MusicGenre[37] := 'Sound Clip';
  MusicGenre[38] := 'Gospel';
  MusicGenre[39] := 'Noise';
  MusicGenre[40] := 'AlternRock';
  MusicGenre[41] := 'Bass';
  MusicGenre[42] := 'Soul';
  MusicGenre[43] := 'Punk';
  MusicGenre[44] := 'Space';
  MusicGenre[45] := 'Meditative';
  MusicGenre[46] := 'Instrumental Pop';
  MusicGenre[47] := 'Instrumental Rock';
  MusicGenre[48] := 'Ethnic';
  MusicGenre[49] := 'Gothic';
  MusicGenre[50] := 'Darkwave';
  MusicGenre[51] := 'Techno-Industrial';
  MusicGenre[52] := 'Electronic';
  MusicGenre[53] := 'Pop-Folk';
  MusicGenre[54] := 'Eurodance';
  MusicGenre[55] := 'Dream';
  MusicGenre[56] := 'Southern Rock';
  MusicGenre[57] := 'Comedy';
  MusicGenre[58] := 'Cult';
  MusicGenre[59] := 'Gangsta';
  MusicGenre[60] := 'Top 40';
  MusicGenre[61] := 'Christian Rap';
  MusicGenre[62] := 'Pop/Funk';
  MusicGenre[63] := 'Jungle';
  MusicGenre[64] := 'Native American';
  MusicGenre[65] := 'Cabaret';
  MusicGenre[66] := 'New Wave';
  MusicGenre[67] := 'Psychadelic';
  MusicGenre[68] := 'Rave';
  MusicGenre[69] := 'Showtunes';
  MusicGenre[70] := 'Trailer';
  MusicGenre[71] := 'Lo-Fi';
  MusicGenre[72] := 'Tribal';
  MusicGenre[73] := 'Acid Punk';
  MusicGenre[74] := 'Acid Jazz';
  MusicGenre[75] := 'Polka';
  MusicGenre[76] := 'Retro';
  MusicGenre[77] := 'Musical';
  MusicGenre[78] := 'Rock & Roll';
  MusicGenre[79] := 'Hard Rock';
  MusicGenre[80] := 'Folk';
  MusicGenre[81] := 'Folk-Rock';
  MusicGenre[82] := 'National Folk';
  MusicGenre[83] := 'Swing';
  MusicGenre[84] := 'Fast Fusion';
  MusicGenre[85] := 'Bebob';
  MusicGenre[86] := 'Latin';
  MusicGenre[87] := 'Revival';
  MusicGenre[88] := 'Celtic';
  MusicGenre[89] := 'Bluegrass';
  MusicGenre[90] := 'Avantgarde';
  MusicGenre[91] := 'Gothic Rock';
  MusicGenre[92] := 'Progessive Rock';
  MusicGenre[93] := 'Psychedelic Rock';
  MusicGenre[94] := 'Symphonic Rock';
  MusicGenre[95] := 'Slow Rock';
  MusicGenre[96] := 'Big Band';
  MusicGenre[97] := 'Chorus';
  MusicGenre[98] := 'Easy Listening';
  MusicGenre[99] := 'Acoustic';
  MusicGenre[100]:= 'Humour';
  MusicGenre[101]:= 'Speech';
  MusicGenre[102]:= 'Chanson';
  MusicGenre[103]:= 'Opera';
  MusicGenre[104]:= 'Chamber Music';
  MusicGenre[105]:= 'Sonata';
  MusicGenre[106]:= 'Symphony';
  MusicGenre[107]:= 'Booty Bass';
  MusicGenre[108]:= 'Primus';
  MusicGenre[109]:= 'Porn Groove';
  MusicGenre[110]:= 'Satire';
  MusicGenre[111]:= 'Slow Jam';
  MusicGenre[112]:= 'Club';
  MusicGenre[113]:= 'Tango';
  MusicGenre[114]:= 'Samba';
  MusicGenre[115]:= 'Folklore';
  MusicGenre[116]:= 'Ballad';
  MusicGenre[117]:= 'Power Ballad';
  MusicGenre[118]:= 'Rhythmic Soul';
  MusicGenre[119]:= 'Freestyle';
  MusicGenre[120]:= 'Duet';
  MusicGenre[121]:= 'Punk Rock';
  MusicGenre[122]:= 'Drum Solo';
  MusicGenre[123]:= 'A capella';
  MusicGenre[124]:= 'Euro-House';
  MusicGenre[125]:= 'Dance Hall';
  MusicGenre[126]:= 'Goa';
  MusicGenre[127]:= 'Drum & Bass';
  MusicGenre[128]:= 'Club-House';
  MusicGenre[129]:= 'Hardcore';
  MusicGenre[130]:= 'Terror';
  MusicGenre[131]:= 'Indie';
  MusicGenre[132]:= 'BritPop';
  MusicGenre[133]:= 'Negerpunk';
  MusicGenre[134]:= 'Polsk Punk';
  MusicGenre[135]:= 'Beat';
  MusicGenre[136]:= 'Christian Gangsta Rap';
  MusicGenre[137]:= 'Heavy Metal';
  MusicGenre[138]:= 'Black Metal';
  MusicGenre[139]:= 'Crossover';
  MusicGenre[140]:= 'Contemporary Christian';
  MusicGenre[141]:= 'Christian Rock';
  MusicGenre[142]:= 'Merengue';
  MusicGenre[143]:= 'Salsa';
  MusicGenre[144]:= 'Trash Metal';
  MusicGenre[145]:= 'Anime';
  MusicGenre[146]:= 'JPop';
  MusicGenre[147]:= 'Synthpop';
end;
{ --------------------------------------------------------------------------- }
end.
ID3v2.pas
Delphi-Quellcode:
unit ID3v2;

interface

uses
  Classes, SysUtils;

const
  TAG_VERSION_2_2 = 2;
  TAG_VERSION_2_3 = 3;
  TAG_VERSION_2_4 = 4;

type
  TID3v2 = class(TObject)
  private
    { Private declarations }
    FExists: Boolean;
    FVersionID: Byte;
    FSize: Integer;
    FTitle: string;
    FArtist: string;
    FAlbum: string;
    FTrack: Word;
    FTrackString: string;
    FYear: string;
    FGenre: string;
    FComment: string;
    FComposer: string;
    FEncoder: string;
    FCopyright: string;
    FLanguage: string;
    FLink: string;
    procedure FSetTitle(const NewTitle: string);
    procedure FSetArtist(const NewArtist: string);
    procedure FSetAlbum(const NewAlbum: string);
    procedure FSetTrack(const NewTrack: Word);
    procedure FSetYear(const NewYear: string);
    procedure FSetGenre(const NewGenre: string);
    procedure FSetComment(const NewComment: string);
    procedure FSetComposer(const NewComposer: string);
    procedure FSetEncoder(const NewEncoder: string);
    procedure FSetCopyright(const NewCopyright: string);
    procedure FSetLanguage(const NewLanguage: string);
    procedure FSetLink(const NewLink: string);
  public
    { Public declarations }
    constructor Create;
    procedure ResetData;
    function ReadFromFile (const FileName: string): Boolean;
    function SaveToFile (const FileName: string): Boolean;
    function RemoveFromFile(const FileName: string): Boolean;
    property Exists : Boolean read FExists;
    property VersionID : Byte read FVersionID;
    property Size : Integer read FSize;
    property Title : String read FTitle write FSetTitle;
    property Artist : String read FArtist write FSetArtist;
    property Album : String read FAlbum write FSetAlbum;
    property Track : Word read FTrack write FSetTrack;
    property TrackString : String read FTrackString;
    property Year : String read FYear write FSetYear;
    property Genre : String read FGenre write FSetGenre;
    property Comment : String read FComment write FSetComment;
    property Composer : String read FComposer write FSetComposer;
    property Encoder : String read FEncoder write FSetEncoder;
    property Copyright : String read FCopyright write FSetCopyright;
    property Language : String read FLanguage write FSetLanguage;
    property Link : String read FLink write FSetLink;
  end;
{ --------------------------------------------------------------------------- }
implementation
{ --------------------------------------------------------------------------- }
const
  ID3V2_ID = 'ID3';
  ID3V2_FRAME_COUNT = 16;
  ID3V2_FRAME_NEW: array [1..ID3V2_FRAME_COUNT] of string =
    ('TIT2', 'TPE1', 'TALB', 'TRCK', 'TYER', 'TCON', 'COMM', 'TCOM', 'TENC',
     'TCOP', 'TLAN', 'WXXX', 'TDRC', 'TOPE', 'TIT1', 'TOAL');
  ID3V2_FRAME_OLD: array [1..ID3V2_FRAME_COUNT] of string =
    ('TT2', 'TP1', 'TAL', 'TRK', 'TYE', 'TCO', 'COM', 'TCM', 'TEN',
     'TCR', 'TLA', 'WXX', 'TOR', 'TOA', 'TT1', 'TOT');
  ID3V2_MAX_SIZE = 4096;
  UNICODE_ID = #1;
{ --------------------------------------------------------------------------- }
type
  FrameHeaderNew = record
    ID : array [1..4] of Char;
    Size : Integer;
    Flags : Word;
  end;

  FrameHeaderOld = record
    ID : array [1..3] of Char;
    Size : array [1..3] of Byte;
  end;

  TagInfo = record
    ID : array [1..3] of Char;
    Version : Byte;
    Revision : Byte;
    Flags : Byte;
    Size : array [1..4] of Byte;
    FileSize : Integer;
    Frame : array [1..ID3V2_FRAME_COUNT] of string;
    NeedRewrite : Boolean;
    PaddingSize : Integer;
  end;
{ --------------------------------------------------------------------------- }
function ReadHeader(const FileName: string; var Tag: TagInfo): Boolean;
var
  SourceFile: file;
  Transferred: Integer;
begin
  try
    Result := true;
    AssignFile(SourceFile, FileName);
    FileMode := 0;
    Reset(SourceFile, 1);
    BlockRead(SourceFile, Tag, 10, Transferred);
    Tag.FileSize := FileSize(SourceFile);
    CloseFile(SourceFile);
    if Transferred < 10 then Result := false;
  except
    Result := false;
  end;
end;
{ --------------------------------------------------------------------------- }
function GetTagSize(const Tag: TagInfo): Integer;
begin
  Result :=
    Tag.Size[1] * $200000 +
    Tag.Size[2] * $4000 +
    Tag.Size[3] * $80 +
    Tag.Size[4] + 10;
  if Tag.Flags and $10 = $10 then Inc(Result, 10);
  if Result > Tag.FileSize then Result := 0;
end;
{ --------------------------------------------------------------------------- }
procedure SetTagItem(const ID, Data: string; var Tag: TagInfo);
var
  Iterator: Byte;
  FrameID: string;
begin
  for Iterator := 1 to ID3V2_FRAME_COUNT do
  begin
    if Tag.Version > TAG_VERSION_2_2 then
      FrameID := ID3V2_FRAME_NEW[Iterator]
    else
      FrameID := ID3V2_FRAME_OLD[Iterator];
    if (FrameID = ID) and (Data[1] <= UNICODE_ID) then
      Tag.Frame[Iterator] := Data;
  end;
end;
{ --------------------------------------------------------------------------- }
function Swap32(const Figure: Integer): Integer;
var
  ByteArray: array [1..4] of Byte absolute Figure;
begin
  Result :=
    ByteArray[1] * $1000000 +
    ByteArray[2] * $10000 +
    ByteArray[3] * $100 +
    ByteArray[4];
end;
{ --------------------------------------------------------------------------- }
procedure ReadFramesNew(const FileName: string; var Tag: TagInfo);
var
  SourceFile: file;
  Frame: FrameHeaderNew;
  Data: array [1..500] of Char;
  DataPosition, DataSize: Integer;
begin
  try
    AssignFile(SourceFile, FileName);
    FileMode := 0;
    Reset(SourceFile, 1);
    Seek(SourceFile, 10);
    while (FilePos(SourceFile) < GetTagSize(Tag)) and (not EOF(SourceFile)) do
    begin
      FillChar(Data, SizeOf(Data), 0);
      BlockRead(SourceFile, Frame, 10);
      if not (Frame.ID[1] in ['A'..'Z']) then break;
      DataPosition := FilePos(SourceFile);
      if Swap32(Frame.Size) > SizeOf(Data) then DataSize := SizeOf(Data)
      else DataSize := Swap32(Frame.Size);
      BlockRead(SourceFile, Data, DataSize);
      if Frame.Flags and $8000 <> $8000 then SetTagItem(Frame.ID, Data, Tag);
      Seek(SourceFile, DataPosition + Swap32(Frame.Size));
    end;
    CloseFile(SourceFile);
  except
  end;
end;
{ --------------------------------------------------------------------------- }
procedure ReadFramesOld(const FileName: string; var Tag: TagInfo);
var
  SourceFile: file;
  Frame: FrameHeaderOld;
  Data: array [1..500] of Char;
  DataPosition, FrameSize, DataSize: Integer;
begin
  try
    AssignFile(SourceFile, FileName);
    FileMode := 0;
    Reset(SourceFile, 1);
    Seek(SourceFile, 10);
    while (FilePos(SourceFile) < GetTagSize(Tag)) and (not EOF(SourceFile)) do
    begin
      FillChar(Data, SizeOf(Data), 0);
      BlockRead(SourceFile, Frame, 6);
      if not (Frame.ID[1] in ['A'..'Z']) then break;
      DataPosition := FilePos(SourceFile);
      FrameSize := Frame.Size[1] shl 16 + Frame.Size[2] shl 8 + Frame.Size[3];
      if FrameSize > SizeOf(Data) then DataSize := SizeOf(Data)
      else DataSize := FrameSize;
      BlockRead(SourceFile, Data, DataSize);
      SetTagItem(Frame.ID, Data, Tag);
      Seek(SourceFile, DataPosition + FrameSize);
    end;
    CloseFile(SourceFile);
  except
  end;
end;
{ --------------------------------------------------------------------------- }
function GetANSI(const Source: string): string;
var
  Index: Integer;
  FirstByte, SecondByte: Byte;
  UnicodeChar: WideChar;
begin
  if (Length(Source) > 0) and (Source[1] = UNICODE_ID) then
  begin
    Result := '';
    for Index := 1 to ((Length(Source) - 1) div 2) do
    begin
      FirstByte := Ord(Source[Index * 2]);
      SecondByte := Ord(Source[Index * 2 + 1]);
      UnicodeChar := WideChar(FirstByte or (SecondByte shl 8));
      if UnicodeChar = #0 then break;
      if FirstByte < $FF then Result := Result + UnicodeChar;
    end;
    Result := Trim(Result);
  end
  else
    Result := Trim(Source);
end;
{ --------------------------------------------------------------------------- }
function GetContent(const Content1, Content2: string): string;
begin
  Result := GetANSI(Content1);
  if Result = 'then Result := GetANSI(Content2);
end;
{ --------------------------------------------------------------------------- }
function ExtractTrack(const TrackString: string): Word;
var
  Track: string;
  Index, Value, Code: Integer;
begin
  Track := GetANSI(TrackString);
  Index := Pos('/', Track);
  if Index = 0 then Val(Track, Value, Code)
  else Val(Copy(Track, 1, Index - 1), Value, Code);
  if Code = 0 then Result := Value
  else Result := 0;
end;
{ --------------------------------------------------------------------------- }
function ExtractYear(const YearString, DateString: string): string;
begin
  Result := GetANSI(YearString);
  if Result = 'then Result := Copy(GetANSI(DateString), 1, 4);
end;
{ --------------------------------------------------------------------------- }
function ExtractGenre(const GenreString: string): string;
begin
  Result := GetANSI(GenreString);
  if Pos(')', Result) > 0 then Delete(Result, 1, LastDelimiter(')', Result));
end;
{ --------------------------------------------------------------------------- }
function ExtractText(const SourceString: string; LanguageID: Boolean): string;
var
  Source, Separator: string;
  EncodingID: Char;
begin
  Source := SourceString;
  Result := '';
  if Length(Source) > 0 then
  begin
    EncodingID := Source[1];
    if EncodingID = UNICODE_ID then Separator := #0#0
    else Separator := #0;
    if LanguageID then Delete(Source, 1, 4)
    else Delete(Source, 1, 1);
    Delete(Source, 1, Pos(Separator, Source) + Length(Separator) - 1);
    Result := GetANSI(EncodingID + Source);
  end;
end;
{ --------------------------------------------------------------------------- }
procedure BuildHeader(var Tag: TagInfo);
var
  Iterator, TagSize: Integer;
begin
  TagSize := 10;
  for Iterator := 1 to ID3V2_FRAME_COUNT do
    if Tag.Frame[Iterator] <> 'then
      Inc(TagSize, Length(Tag.Frame[Iterator]) + 11);
  Tag.NeedRewrite :=
    (Tag.ID <> ID3V2_ID) or
    (GetTagSize(Tag) < TagSize) or
    (GetTagSize(Tag) > ID3V2_MAX_SIZE);
  if Tag.NeedRewrite then Tag.PaddingSize := ID3V2_MAX_SIZE - TagSize
  else Tag.PaddingSize := GetTagSize(Tag) - TagSize;
  if Tag.PaddingSize > 0 then Inc(TagSize, Tag.PaddingSize);
  Tag.ID := ID3V2_ID;
  Tag.Version := TAG_VERSION_2_3;
  Tag.Revision := 0;
  Tag.Flags := 0;
  for Iterator := 1 to 4 do
    Tag.Size[Iterator] := ((TagSize - 10) shr ((4 - Iterator) * 7)) and $7F;
end;
{ --------------------------------------------------------------------------- }
function ReplaceTag(const FileName: string; TagData: TStream): Boolean;
var
  Destination: TFileStream;
begin
  Result := false;
  if (not FileExists(FileName)) or (FileSetAttr(FileName, 0) <> 0) then exit;
  try
    TagData.Position := 0;
    Destination := TFileStream.Create(FileName, fmOpenReadWrite);
    Destination.CopyFrom(TagData, TagData.Size);
    Destination.Free;
    Result := true;
  except
  end;
end;
{ --------------------------------------------------------------------------- }
function RebuildFile(const FileName: string; TagData: TStream): Boolean;
var
  Tag: TagInfo;
  Source, Destination: TFileStream;
  BufferName: string;
begin
  Result := false;
  if (not FileExists(FileName)) or (FileSetAttr(FileName, 0) <> 0) then exit;
  if not ReadHeader(FileName, Tag) then exit;
  if (TagData = nil) and (Tag.ID <> ID3V2_ID) then exit;
  try
    BufferName := FileName + '~';
    Source := TFileStream.Create(FileName, fmOpenRead);
    Destination := TFileStream.Create(BufferName, fmCreate);
    if Tag.ID = ID3V2_ID then Source.Seek(GetTagSize(Tag), soFromBeginning);
    if TagData <> nil then Destination.CopyFrom(TagData, 0);
    Destination.CopyFrom(Source, Source.Size - Source.Position);
    Source.Free;
    Destination.Free;
    if (DeleteFile(FileName)) and (RenameFile(BufferName, FileName)) then
      Result := true
    else
      raise Exception.Create('');
  except
    if FileExists(BufferName) then DeleteFile(BufferName);
  end;
end;
{ --------------------------------------------------------------------------- }
function SaveTag(const FileName: string; Tag: TagInfo): Boolean;
var
  TagData: TStringStream;
  Iterator, FrameSize: Integer;
  Padding: array [1..ID3V2_MAX_SIZE] of Byte;
begin
  TagData := TStringStream.Create('');
  BuildHeader(Tag);
  TagData.Write(Tag, 10);
  for Iterator := 1 to ID3V2_FRAME_COUNT do
    if Tag.Frame[Iterator] <> 'then
    begin
      TagData.WriteString(ID3V2_FRAME_NEW[Iterator]);
      FrameSize := Swap32(Length(Tag.Frame[Iterator]) + 1);
      TagData.Write(FrameSize, SizeOf(FrameSize));
      TagData.WriteString(#0#0#0 + Tag.Frame[Iterator]);
    end;
  FillChar(Padding, SizeOf(Padding), 0);
  if Tag.PaddingSize > 0 then TagData.Write(Padding, Tag.PaddingSize);
  if Tag.NeedRewrite then Result := RebuildFile(FileName, TagData)
  else Result := ReplaceTag(FileName, TagData);
  TagData.Free;
end;
{ --------------------------------------------------------------------------- }
procedure TID3v2.FSetTitle(const NewTitle: string);
begin
  FTitle := Trim(NewTitle);
end;
{ --------------------------------------------------------------------------- }
procedure TID3v2.FSetArtist(const NewArtist: string);
begin
  FArtist := Trim(NewArtist);
end;
{ --------------------------------------------------------------------------- }
procedure TID3v2.FSetAlbum(const NewAlbum: string);
begin
  FAlbum := Trim(NewAlbum);
end;
{ --------------------------------------------------------------------------- }
procedure TID3v2.FSetTrack(const NewTrack: Word);
begin
  FTrack := NewTrack;
end;
{ --------------------------------------------------------------------------- }
procedure TID3v2.FSetYear(const NewYear: string);
begin
  FYear := Trim(NewYear);
end;
{ --------------------------------------------------------------------------- }
procedure TID3v2.FSetGenre(const NewGenre: string);
begin
  FGenre := Trim(NewGenre);
end;
{ --------------------------------------------------------------------------- }
procedure TID3v2.FSetComment(const NewComment: string);
begin
  FComment := Trim(NewComment);
end;
{ --------------------------------------------------------------------------- }
procedure TID3v2.FSetComposer(const NewComposer: string);
begin
  FComposer := Trim(NewComposer);
end;
{ --------------------------------------------------------------------------- }
procedure TID3v2.FSetEncoder(const NewEncoder: string);
begin
  FEncoder := Trim(NewEncoder);
end;
{ --------------------------------------------------------------------------- }
procedure TID3v2.FSetCopyright(const NewCopyright: string);
begin
  FCopyright := Trim(NewCopyright);
end;
{ --------------------------------------------------------------------------- }
procedure TID3v2.FSetLanguage(const NewLanguage: string);
begin
  FLanguage := Trim(NewLanguage);
end;
{ --------------------------------------------------------------------------- }
procedure TID3v2.FSetLink(const NewLink: string);
begin
  FLink := Trim(NewLink);
end;
{ --------------------------------------------------------------------------- }
constructor TID3v2.Create;
begin
  inherited;
  ResetData;
end;
{ --------------------------------------------------------------------------- }
procedure TID3v2.ResetData;
begin
  FExists := false;
  FVersionID := 0;
  FSize := 0;
  FTitle := '';
  FArtist := '';
  FAlbum := '';
  FTrack := 0;
  FTrackString := '';
  FYear := '';
  FGenre := '';
  FComment := '';
  FComposer := '';
  FEncoder := '';
  FCopyright := '';
  FLanguage := '';
  FLink := '';
end;
{ --------------------------------------------------------------------------- }
function TID3v2.ReadFromFile(const FileName: string): Boolean;
var
  Tag: TagInfo;
begin
  ResetData;
  Result := ReadHeader(FileName, Tag);
  if (Result) and (Tag.ID = ID3V2_ID) then
  begin
    FExists := true;
    FVersionID := Tag.Version;
    FSize := GetTagSize(Tag);
    if (FVersionID in [TAG_VERSION_2_2..TAG_VERSION_2_4]) and (FSize > 0) then
    begin
      if FVersionID > TAG_VERSION_2_2 then ReadFramesNew(FileName, Tag)
      else ReadFramesOld(FileName, Tag);
      FTitle := GetContent(Tag.Frame[1], Tag.Frame[15]);
      FArtist := GetContent(Tag.Frame[2], Tag.Frame[14]);
      FAlbum := GetContent(Tag.Frame[3], Tag.Frame[16]);
      FTrack := ExtractTrack(Tag.Frame[4]);
      FTrackString := GetANSI(Tag.Frame[4]);
      FYear := ExtractYear(Tag.Frame[5], Tag.Frame[13]);
      FGenre := ExtractGenre(Tag.Frame[6]);
      FComment := ExtractText(Tag.Frame[7], true);
      FComposer := GetANSI(Tag.Frame[8]);
      FEncoder := GetANSI(Tag.Frame[9]);
      FCopyright := GetANSI(Tag.Frame[10]);
      FLanguage := GetANSI(Tag.Frame[11]);
      FLink := ExtractText(Tag.Frame[12], false);
    end;
  end;
end;
{ --------------------------------------------------------------------------- }
function TID3v2.SaveToFile(const FileName: string): Boolean;
var
  Tag: TagInfo;
begin
  FillChar(Tag, SizeOf(Tag), 0);
  ReadHeader(FileName, Tag);
  Tag.Frame[1] := FTitle;
  Tag.Frame[2] := FArtist;
  Tag.Frame[3] := FAlbum;
  if FTrack > 0 then Tag.Frame[4] := IntToStr(FTrack);
  Tag.Frame[5] := FYear;
  Tag.Frame[6] := FGenre;
  if FComment <> 'then Tag.Frame[7] := 'eng' + #0 + FComment;
  Tag.Frame[8] := FComposer;
  Tag.Frame[9] := FEncoder;
  Tag.Frame[10] := FCopyright;
  Tag.Frame[11] := FLanguage;
  if FLink <> 'then Tag.Frame[12] := #0 + FLink;
  Result := SaveTag(FileName, Tag);
end;
{ --------------------------------------------------------------------------- }
function TID3v2.RemoveFromFile(const FileName: string): Boolean;
begin
  Result := RebuildFile(FileName, nil);
end;
{ --------------------------------------------------------------------------- }
end.
MPEG-Header.pas
Delphi-Quellcode:
unit MPEGHeader;

interface
{ ---------------------------------------------------------------------------- }
type
  TMPEGInfo = record
    Position: integer;
    Version: integer;
    Layer: integer;
    Protection: boolean;
    Bitrate: integer;
    Samplerate: integer;
    ChannelMode: byte;
    Extension: byte;
    Copyright: boolean;
    Original: boolean;
    Emphasis: byte;
    Frames: longint;
    Dauer: longint;
    VBR: boolean;
  end;
{ ---------------------------------------------------------------------------- }
const
  MPEG_BIT_RATES: array[1..3] of array[1..3] of array[0..15] of word = ((
      { Version 1, Layer I }
      (0,32,64,96,128,160,192,224,256,288,320,352,384,416,448,0),
      { Version 1, Layer II }
      (0,32,48,56, 64, 80, 96,112,128,160,192,224,256,320,384,0),
      { Version 1, Layer III }
      (0,32,40,48, 56, 64, 80, 96,112,128,160,192,224,256,320,0)),
      { Version 2, Layer I }
      ((0,32,48, 56, 64, 80, 96,112,128,144,160,176,192,224,256,0),
      { Version 2, Layer II }
      (0, 8,16,24, 32, 40, 48, 56, 64, 80, 96, 112,128,144,160,0),
      { Version 2, Layer III }
      (0, 8,16,24, 32, 40, 48, 56, 64, 80, 96, 112,128,144,160,0)),
      { Version 2.5, Layer I }
      ((0,32,48, 56, 64, 80, 96,112,128,144,160,176,192,224,256,0),
      { Version 2.5, Layer II }
      (0, 8,16,24, 32, 40, 48, 56, 64, 80, 96, 112,128,144,160,0),
      { Version 2.5, Layer III }
      (0, 8,16,24, 32, 40, 48, 56, 64, 80, 96, 112,128,144,160,0)
    ));

  MPEG_SAMPLE_RATES: array[1..3] of array [0..3] of word = (
      (44100, 48000, 32000, 0),
      (22050, 24000, 16000, 0),
      (11025, 12000, 8000, 0)
    );
{ ---------------------------------------------------------------------------- }
type
  TMPEGHeader = class(TObject)
    private
      { Private declarations }
      F : File;
      id3v1_tag : array[1..128] of char;
      mp3_header : array[1..4] of char;
      buffer : array of char;
      fsize : longint;
      id3v1_size : integer;
      xing_header_size : integer;
      id3_size : longint;

      bitrateindex, versionindex : byte;
      valid : boolean;
      position : integer;
      padding,samplerateindex : byte;
      framelength : longint;
      Xing_Offset : integer;
      Xing_Flags : byte;
    public
      { Public declarations }
      MPEGInfo : TMpeginfo;
      constructor Create;
      procedure MPEG_Header(FileName : String); { liest alle Informationen einer MP3 aus }
  end;
{ ---------------------------------------------------------------------------- }
implementation
{ ---------------------------------------------------------------------------- }
{ TMPEGHeader }
{ ---------------------------------------------------------------------------- }
constructor TMPEGHeader.Create;
begin
  inherited Create;
end;
{ ---------------------------------------------------------------------------- }
procedure TMPEGHeader.MPEG_Header(FileName: String);
begin
  xing_header_size := 0;
  id3_size := 0;
  id3v1_size := 0;
  AssignFile(F, filename);
  FileMode := 0;
  Reset(F,1);
  fsize := filesize(f);

  if fsize>=6000 then setlength(buffer,6000) else
      setlength(buffer,fsize);

  blockread(f,buffer[0],length(buffer));
  Seek(F,FileSize(F)-128);
  BlockRead(F, id3v1_tag, 128);
  CloseFile(F);
  //*******************Start des MPEG-Headers***********************
  valid := false;
  mpeginfo.position := - 1;
  position := id3_size - 1;
  while NOT ((valid) or (position>length(buffer)+4)) do
  begin
    inc(position);
    if (ord(buffer[position]) = $FF) AND (ord(buffer[position + 1]) >= $E0)
    then
    begin
      valid := true;
      Versionindex := ((ord(buffer[position+1]) shr 3) and 3);
      case versionindex of
        0: mpeginfo.version := 3;
        1: mpeginfo.version := 0;
        2: mpeginfo.version := 2;
        3: mpeginfo.version := 1;
      end;
      mpeginfo.Layer := 4 - ((ord(buffer[position + 1]) shr 1) and 3);
      mpeginfo.protection := ((ord(buffer[position + 1]) AND 1) =0);

      bitrateindex := ((ord(buffer[position + 2]) shr 4) AND $F);
      mpeginfo.bitrate := MPEG_BIT_RATES[mpeginfo.version][mpeginfo.layer][bitrateindex];
      if bitrateindex = $F then
        valid := false;
      samplerateindex := ((ord(buffer[position + 2]) shr 2) AND 3);
      mpeginfo.samplerate := MPEG_SAMPLE_RATES[mpeginfo.version][samplerateindex];
      padding := ((ord(buffer[position + 2]) shr 1) AND 1);

      mpeginfo.channelmode :=((ord(buffer[position + 3]) shr 6) AND 3);
      mpeginfo.extension :=((ord(buffer[position + 3]) shr 4) AND 3);
      mpeginfo.copyright :=((ord(buffer[position + 3]) shr 3) AND 1) = 1;
      mpeginfo.original :=((ord(buffer[position + 3]) shr 2) AND 1) = 1;
      mpeginfo.emphasis :=(ord(buffer[position + 3]) AND 3);

      if mpeginfo.layer = 2 then
      begin
        if (mpeginfo.bitrate = 32) AND (mpeginfo.channelmode <> 3) then valid := false;
        if (mpeginfo.bitrate = 48) AND (mpeginfo.channelmode <> 3) then valid := false;
        if (mpeginfo.bitrate = 56) AND (mpeginfo.channelmode <> 3) then valid := false;
        if (mpeginfo.bitrate = 80) AND (mpeginfo.channelmode <> 3) then valid := false;
        if (mpeginfo.bitrate = 224) AND (mpeginfo.channelmode = 3) then valid := false;
        if (mpeginfo.bitrate = 256) AND (mpeginfo.channelmode = 3) then valid := false;
        if (mpeginfo.bitrate = 320) AND (mpeginfo.channelmode = 3) then valid := false;
        if (mpeginfo.bitrate = 384) AND (mpeginfo.channelmode = 3) then valid := false;
      end;
//***************damit ist der MPEG Header komplett eingelesen***********************************

//**************Einlesen des XING-Headers***************************
      if mpeginfo.version = 1 then
        if mpeginfo.channelmode <> 3 then
          xing_offset := 32 + 4
        else
          xing_offset := 17 + 4
      else
        if mpeginfo.channelmode <> 3 then
          xing_offset := 17 + 4
        else
          xing_offset := 9 + 4;
      if (buffer[position + xing_offset] = 'X')
      AND (buffer[position + xing_offset + 1] = 'i')
      AND (buffer[position + xing_offset + 2] = 'n')
      AND (buffer[position + xing_offset + 3] = 'g') then
      begin
        Xing_flags := ord(buffer[position + xing_offset + 7]);
        if (Xing_flags AND 1) = 1 then
        begin
          mpeginfo.frames := 16777216 * ord(buffer[position + xing_offset + 8])
              + 65536 * ord(buffer[position + xing_offset + 9])
              + 256 * ord(buffer[position + xing_offset + 10])
              + ord(buffer[position + xing_offset + 11]);
        end;
        if mpeginfo.layer = 1 then
          xing_header_size := trunc(((12 * MPEG_BIT_RATES[mpeginfo.version][mpeginfo.layer][bitrateindex] * 1000 / mpeginfo.samplerate) + padding) * 4)
        else
          xing_header_size := trunc(144 * (MPEG_BIT_RATES[mpeginfo.version][mpeginfo.layer][bitrateindex]* 1000 / mpeginfo.samplerate) + padding);
        framelength := xing_header_size;
        mpeginfo.bitrate := trunc((mpeginfo.samplerate / 1000 * (fsize - id3_size - id3v1_size - xing_header_size)) / (mpeginfo.frames * 144));
        mpeginfo.vbr := true;
      end
      else
      begin
        if mpeginfo.layer = 1 then
          framelength := trunc(((12 * MPEG_BIT_RATES[mpeginfo.version][mpeginfo.layer][bitrateindex] * 1000 / mpeginfo.samplerate) + padding) * 4)
        else
          framelength := trunc(144 * (MPEG_BIT_RATES[mpeginfo.version][mpeginfo.layer][bitrateindex] * 1000 / mpeginfo.samplerate) + padding);
        mpeginfo.frames := trunc((fsize - id3_size - id3v1_size - xing_header_size) / framelength);
        mpeginfo.vbr := false;
        xing_header_size := 0;
      end;
//**************XING-Header Ende***************************

      if (position + framelength > length(buffer) - 2) AND (position + framelength + 4 < fsize) then
      begin
        Reset(F,1);
        Seek(F,position + framelength);
        blockread(f,mp3_header,4);
        CloseFile(F);
      end
      else
      begin
        try
          mp3_header[1] := buffer[position + framelength];
          mp3_header[2] := buffer[position + framelength + 1];
        except
          mp3_header[1] := '0';
          mp3_header[2] := '0';
        end;
      end;
      if (ord(mp3_header[1]) <> $FF) or (ord(mp3_header[2]) < $E0) then valid := false;

      if valid then begin
          mpeginfo.dauer := ((fsize - id3_size - id3v1_size - xing_header_size) * 8) div ((mpeginfo.bitrate) * 1000);
          mpeginfo.position := position;
      end;
    end;
  end;
end;
{ ---------------------------------------------------------------------------- }
end.
Beispielprogramm zum laden und speichern füge ich an.
Grüße an alle.

MFG Alex
Angehängte Dateien
Dateityp: zip bsp-prog_887.zip (18,1 KB, 256x aufgerufen)
Let's fetz sprach der Frosch und sprang in den Mixer
  Mit Zitat antworten Zitat