Registriert seit: 22. Sep 2003
1.115 Beiträge
Delphi 12 Athens
|
ID3v1-Tag, ID3v2-Tag und MPEG-Header einer MP3 auslesen
27. Sep 2005, 12: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
Let's fetz sprach der Frosch und sprang in den Mixer
|