|
Antwort |
Registriert seit: 22. Sep 2003 1.115 Beiträge Delphi 12 Athens |
#1
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:
ID3v2.pas
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.
Delphi-Quellcode:
MPEG-Header.pas
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.
Delphi-Quellcode:
Beispielprogramm zum laden und speichern füge ich an.
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. Grüße an alle. MFG Alex
Let's fetz sprach der Frosch und sprang in den Mixer
|
Zitat |
(Co-Admin)
Registriert seit: 7. Jul 2003 Ort: Schwabenländle 14.929 Beiträge Turbo Delphi für Win32 |
#2
Moin!
Könntest du uns auch sagen, woher du diese Units hast? |
Zitat |
Registriert seit: 13. Okt 2003 Ort: Künzelsau 1.613 Beiträge Delphi 2006 Professional |
#3
Warum hast du aus dem Array MusicGenre eine Variable gemacht? Hätte da ne Konstante nich besser gepasst?
Außerdem wäre es so viel platzsparender gegangen:
Delphi-Quellcode:
Außerdem wäre es evtl komfortabel, dieses Array auch im ID3v2 zur Verfügung zu stellen, so daß man da auch darauf zugreifen kann, wenn man es braucht.
const MusicGenre : Array[1..MAX_MUSIC_GENRES - 1] =
('Blues','Classic Rock','Country','Dance',... ... ... 'Trash Metal','Anime','JPop','Synthpop');
Axel Sefranek
A programmer started to cuss, cause getting to sleep was a fuss.
As he lay there in bed, looping round in his head was: while(!asleep()) ++sheep; |
Zitat |
Registriert seit: 29. Mai 2002 Ort: Frankfurt 8.252 Beiträge Delphi 2006 Professional |
#4
Zitat von Matze:
... Könntest du uns auch sagen, woher du diese Units hast?
Stephan B.
"Lasst den Gänsen ihre Füßchen" |
Zitat |
Registriert seit: 22. Sep 2003 1.115 Beiträge Delphi 12 Athens |
#5
@Sharky: Sorry tut mir leid um dein Mausrad und dein warmen Finger
Ich wusste nicht, wie ich es am besten mache und habe daher alles gepostet. @Matze: Ich habe diese Units schon seit bestimmt 1,5Jahren und habe sie zwischendurch immer mal in ein paar Sachen geändert bzw. verfeinert. Ich kann dir ehrlich nicht sagen, wo ich damals das Grundgerüst gefunden habe. Das hat nichts damit zu tun, dass ich keine Namen nennen will sondern das ich es ganz einfach nicht mehr weiß.Sorry @leddl: Ja hätte ich so machen können aber es ist nunmal so gewachsen Kannst es gerne ändern... MFG Alex
Let's fetz sprach der Frosch und sprang in den Mixer
|
Zitat |
(Co-Admin)
Registriert seit: 7. Jul 2003 Ort: Schwabenländle 14.929 Beiträge Turbo Delphi für Win32 |
#6
Zitat von Alex_ITA01:
@Matze: Ich habe diese Units schon seit bestimmt 1,5Jahren und habe sie zwischendurch immer mal in ein paar Sachen geändert bzw. verfeinert. Ich kann dir ehrlich nicht sagen, wo ich damals das Grundgerüst gefunden habe. Das hat nichts damit zu tun, dass ich keine Namen nennen will sondern das ich es ganz einfach nicht mehr weiß.Sorry
ID3v1.pas: - hier im Anhang - hier ungefähr auf der Mitte der Seite - oder hier (mit Copyright im Header):
Delphi-Quellcode:
D3v2.pas:
{ *************************************************************************** }
{ } { Audio Tools Library (Freeware) } { Class TID3v1 - for manipulating with ID3v1 tags } { } { Copyright (c) 2001,2002 by Jurgen Faul } { E-mail: [email]jfaul@gmx.de[/email] } { [url]http://jfaul.de/atl[/url] } { } { Version 1.0 (25 July 2001) } { - Full support for ID3v1.0 & ID3v1.1 tags (read/write) } { } { *************************************************************************** } - Hier im SDC MPEG-Header.pas: - hier im DF Wir müssen das helt wissen, wegen den Copyright-Bestimmungen. |
Zitat |
Registriert seit: 22. Sep 2003 1.115 Beiträge Delphi 12 Athens |
#7
Ja, ich verstehe das mit den Copyright-Bestimmungen aber ich kann dir sagen, dass die ID3v1 und ID3v2 nicht von den obrigen Seiten sind. Ich will ja damit keinen ärgern oder seinen Namen nicht sagen aber wie gesagt, ich habe diese Units schon ziemlich lange und habe kein Copyright entfernt (falls eins vorhanden war).
Bei der MPEG.pas bin ich mir nicht ganz sicher ob sie aus dem DF ist. Falls es so ist, sage ich einfach mal Gausi aus dem DF danke schön! (Copyright ist aber nicht auf dem Source,oder?) Aber wie oben schon gesagt, ich weiß es nicht. MFG Alex
Let's fetz sprach der Frosch und sprang in den Mixer
|
Zitat |
Registriert seit: 9. Jun 2005 Ort: Unna 1.172 Beiträge Delphi 10.2 Tokyo Professional |
#8
Also die ID3v1.pas sieht schon sehr ähnlich aus, allerdings scheint die andere Variante (die 2. von Matze) späteren Datums zu sein und einige Verbesserungen/Korrekturen zu haben (Kommentare, Eigenschaft "Changed", Korrekte Versionen von "RemoveTag" und "SaveTag", etc.).
Beide Units haben auf jeden Fall denselben Ursprung, die vielen Gemeinsamkeiten können nicht zufällig sein. |
Zitat |
Registriert seit: 29. Mai 2002 37.621 Beiträge Delphi 2006 Professional |
#9
Warum eigentlich zwei Klasse für die Tag-Versionen? Wie soll man denn das jetzt anwenden? Erst mit der einen versuchen die Tags auszulesen und wenn das nicht klappt mit der anderen? Warum nicht eine Klasse die die Tag-Version bestimmt und dann entsprechend die Tags ausliest?
Michael
Ein Teil meines Codes würde euch verunsichern. |
Zitat |
Registriert seit: 22. Sep 2003 1.115 Beiträge Delphi 12 Athens |
#10
@Luckie: weil vielleicht nicht jeder alle Tag's braucht und nur z.B. die von ID3v1 haben will...
@Flocke: Ich habe auch nirgendwo gesagt, dass alles selber programmiert ist.Ich weiß nur ehrlich nicht mehr, woher ich sie habe.Aber ich weiß garantiert, dass ich kein Copyright verletzt habe bzw. gelöscht habe. MFG Alex
Let's fetz sprach der Frosch und sprang in den Mixer
|
Zitat |
Ansicht |
Linear-Darstellung |
Zur Hybrid-Darstellung wechseln |
Zur Baum-Darstellung wechseln |
ForumregelnEs ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.
BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus. Trackbacks are an
Pingbacks are an
Refbacks are aus
|
|
Nützliche Links |
Heutige Beiträge |
Sitemap |
Suchen |
Code-Library |
Wer ist online |
Alle Foren als gelesen markieren |
Gehe zu... |
LinkBack |
LinkBack URL |
About LinkBacks |