W
wukw
Unregistered / Unconfirmed
GUEST, unregistred user!
我就知道TID3v2这个类很好用,但是我想看看它内部是如何读取Mp3文件信息的。
帮忙,谢谢!
或者,您有ID3V2的源码或者资料,也请贴出来,谢谢~~~
帮忙,谢谢!
或者,您有ID3V2的源码或者资料,也请贴出来,谢谢~~~
{ $RCSfile: ID3v2.pas,v $ $Revision: 1.8 $ $Date: 2002/11/12 15:44:29 $ $Author: vlads $ }
{ *************************************************************************** }
{ }
{ Audio Tools Library (Freeware) }
{ Class TID3v2 - for manipulating with ID3v2 tags }
{ }
{ Copyright (c) 2001,2002 by Jurgen Faul }
{ E-mail: jfaul@gmx.de }
{ http://jfaul.de/atl }
{ }
{ +vlads fix for "Track number as string": }
{ +vlads fix for "Read": }
{ }
{ Version 1.7 (2 October 2002) }
{ - Added property TrackString }
{ }
{ Version 1.6 (29 July 2002) }
{ - Reading support for Unicode }
{ - Removed limitation for the track number }
{ }
{ Version 1.5 (23 May 2002) }
{ - Support for padding }
{ }
{ Version 1.4 (24 March 2002) }
{ - Reading support for ID3v2.2.x &
ID3v2.4.x tags }
{ }
{ Version 1.3 (16 February 2002) }
{ - Fixed bug with property Comment }
{ - Added info: composer, encoder, copyright, language, link }
{ }
{ Version 1.2 (17 October 2001) }
{ - Writing support for ID3v2.3.x tags }
{ - Fixed bug with track number detection }
{ - Fixed bug with tag reading }
{ }
{ Version 1.1 (31 August 2001) }
{ - Added public procedure ResetData }
{ }
{ Version 1.0 (14 August 2001) }
{ - Reading support for ID3v2.3.x tags }
{ - Tag info: title, artist, album, track, year, genre, comment }
{ }
{ *************************************************************************** }
unit ID3v2;
interface
uses
Classes, SysUtils;
const
TAG_VERSION_2_2 = 2;
{ Code for ID3v2.2.x tag }
TAG_VERSION_2_3 = 3;
{ Code for ID3v2.3.x tag }
TAG_VERSION_2_4 = 4;
{ Code for ID3v2.4.x tag }
type
{ Class TID3v2 }
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);
{ vlads fix: }
procedure FSetTrackString(const NewTrack: string);
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);
function ProcessRead(var SourceFile: file): Boolean;
public
{ Public declarations }
constructor Create;
{ Create object }
procedure ResetData;
{ Reset all data }
{ vlads fix: }
function ReadFromFile(const FileName: string): Boolean;
overload;
{ Load tag }
function ReadFromFile(var SourceFile: file): Boolean;
overload;
{ Load tag }
function SaveToFile(const FileName: string): Boolean;
{ Save tag }
function RemoveFromFile(const FileName: string): Boolean;
{ Delete tag }
property Exists: Boolean read FExists;
{ True if tag found }
property VersionID: Byte read FVersionID;
{ Version code }
property Size: Integer read FSize;
{ Total tag size }
property Title: string read FTitle write FSetTitle;
{ Song title }
property Artist: string read FArtist write FSetArtist;
{ Artist name }
property Album: string read FAlbum write FSetAlbum;
{ Album title }
property Track: Word read FTrack write FSetTrack;
{ Track number }
{ vlads fix: }
property TrackString: string read FTrackString write FSetTrackString;
{ Track number as string }
property Year: string read FYear write FSetYear;
{ Release year }
property Genre: string read FGenre write FSetGenre;
{ Genre name }
property Comment: string read FComment write FSetComment;
{ Comment }
property Composer: string read FComposer write FSetComposer;
{ Composer }
property Encoder: string read FEncoder write FSetEncoder;
{ Encoder }
property Copyright: string read FCopyright write FSetCopyright;
{ (c) }
property Language: string read FLanguage write FSetLanguage;
{ Language }
property Link: string read Flink write FSetLink;
{ URL link }
end;
implementation
uses ATLcommon;
{$I+}
const
{ ID3v2 tag ID }
ID3V2_ID = 'ID3';
{ Max. number of supported tag frames }
ID3V2_FRAME_COUNT = 16;
{ Names of supported tag frames (ID3v2.3.x &
ID3v2.4.x) }
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');
{ Names of supported tag frames (ID3v2.2.x) }
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');
{ Max. tag size for saving }
ID3V2_MAX_SIZE = 4096;
{ Unicode ID }
UNICODE_ID = #1;
type
{ Frame header (ID3v2.3.x &
ID3v2.4.x) }
FrameHeaderNew = record
ID: array[1..4] of Char;
{ Frame ID }
Size: Integer;
{ Size excluding header }
Flags: Word;
{ Flags }
end;
{ Frame header (ID3v2.2.x) }
FrameHeaderOld = record
ID: array[1..3] of Char;
{ Frame ID }
Size: array[1..3] of Byte;
{ Size excluding header }
end;
{ ID3v2 header data - for internal use }
TagInfo = record
{ Real structure of ID3v2 header }
ID: array[1..3] of Char;
{ Always "ID3" }
Version: Byte;
{ Version number }
Revision: Byte;
{ Revision number }
Flags: Byte;
{ Flags of tag }
Size: array[1..4] of Byte;
{ Tag size excluding header }
{ Extended data }
Filesize: Integer;
{ File size (bytes) }
Frame: array[1..ID3V2_FRAME_COUNT] of string;
{ Information from frames }
NeedRewrite: Boolean;
{ Tag should be rewritten }
PaddingSize: Integer;
{ Padding size (bytes) }
end;
{ vlads fix: }
PTagInfo = ^TagInfo;
Size32 = array[1..4] of Byte;
{ ********************* Auxiliary functions &
procedures ******************** }
function ReadHeader(var SourceFile: file;
var Tag: TagInfo): Boolean;
overload;
var
Transferred: Integer;
begin
try
Result := True;
Seek(SourceFile, 0);
{ Read header and get file size }
BlockRead(SourceFile, Tag, 10, Transferred);
Tag.Filesize := Filesize(SourceFile);
{ if transfer is not complete }
if Transferred < 10 then
Result := False;
except
{ Error }
Result := False;
end;
end;
function ReadHeader(const FileName: string;
var Tag: TagInfo): Boolean;
overload;
var
SourceFile: file;
begin
try
{ Set read-access and open file }
AssignFile(SourceFile, FileName);
FileMode := 0;
Reset(SourceFile, 1);
{ Read header and get file size }
Result := ReadHeader(SourceFile, Tag);
CloseFile(SourceFile);
except
{ Error }
Result := False;
end;
end;
{ --------------------------------------------------------------------------- }
function GetTagSize(const Tag: TagInfo): Integer;
begin
{ Get total tag size }
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
{ Set tag item if supported frame found }
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;
overload;
var
ByteArray: array[1..4] of Byte absolute Figure;
begin
{ Swap 4 bytes }
Result :=
ByteArray[1] * $1000000 +
ByteArray[2] * $10000 +
ByteArray[3] * $100 +
ByteArray[4];
end;
function Swap32(const ByteArray: Size32): Integer;
overload;
begin
{ Swap 4 bytes }
Result :=
ByteArray[1] * $1000000 +
ByteArray[2] * $10000 +
ByteArray[3] * $100 +
ByteArray[4];
end;
{ --------------------------------------------------------------------------- }
procedure ReadFramesNewSlow(var SourceFile: file;
var Tag: TagInfo);
var
Frame: FrameHeaderNew;
Data: array[1..500] of Char;
DataPosition, DataSize: Integer;
begin
{ Get information from frames (ID3v2.3.x &
ID3v2.4.x) }
try
Seek(SourceFile, 10);
while (FilePos(SourceFile) < GetTagSize(Tag)) and (not EOF(SourceFile)) do
begin
FillChar(Data, SizeOf(Data), 0);
{ Read frame header and check frame ID }
BlockRead(SourceFile, Frame, 10);
if not (Frame.ID[1] in ['A'..'Z']) then
Break;
{ Note data position and determine significant data size }
DataPosition := FilePos(SourceFile);
if Swap32(Frame.Size) > SizeOf(Data) then
DataSize := SizeOf(Data)
else
DataSize := Swap32(Frame.Size);
{ Read frame data and set tag item if frame supported }
BlockRead(SourceFile, Data, DataSize);
SetTagItem(Frame.ID, Data, Tag);
Seek(SourceFile, DataPosition + Swap32(Frame.Size));
end;
except
end;
end;
{orig Read ID3v2 2600 files, 50 folders, 1.302 sec, 0.0005008 sec/per file}
{buff Read ID3v2 2600 files, 50 folders, 1.252 sec, 0.0004815 sec/per file}
procedure ReadFramesNew(var SourceFile: file;
var Tag: TagInfo);
var
Frame: FrameHeaderNew;
Data: array[1..500] of Char;
DataSize: Integer;
tagSIZE, AmtTransferred: Integer;
Buffer: PChar;
BufferPos: Integer;
Bptr: PChar;
begin
tagSIZE := GetTagSize(Tag);
Buffer := AllocMem(tagSIZE);
{ Get information from frames (ID3v2.3.x &
ID3v2.4.x) }
try
Seek(SourceFile, 10);
BlockRead(SourceFile, Buffer^, tagSIZE, AmtTransferred);
if (tagSIZE <> AmtTransferred) then
ReadFramesNewSlow(SourceFile, Tag)
else
begin
BufferPos := 0;
while (BufferPos < tagSIZE) and (BufferPos < AmtTransferred) do
begin
FillChar(Data, SizeOf(Data), 0);
Bptr := Buffer + BufferPos;
{ Read frame header and check frame ID }
Move(Bptr^, Frame, 10);
Bptr := Buffer + BufferPos + 10;
if not (Frame.ID[1] in ['A'..'Z']) then
Break;
{ Note data position and determine significant data size }
if Swap32(Frame.Size) > SizeOf(Data) then
DataSize := SizeOf(Data)
else
DataSize := Swap32(Frame.Size);
{ Read frame data and set tag item if frame supported }
Move(Bptr^, Data, DataSize);
SetTagItem(Frame.ID, Data, Tag);
Inc(BufferPos, 10 + Swap32(Frame.Size));
end;
end;
except
end;
FreeMem(Buffer);
end;
{ --------------------------------------------------------------------------- }
procedure ReadFramesOld(var SourceFile: file;
var Tag: TagInfo);
var
Frame: FrameHeaderOld;
Data: array[1..500] of Char;
DataPosition, FrameSize, DataSize: Integer;
begin
{ Get information from frames (ID3v2.2.x) }
try
{ Set read-access, open file }
Seek(SourceFile, 10);
while (FilePos(SourceFile) < GetTagSize(Tag)) and (not EOF(SourceFile)) do
begin
FillChar(Data, SizeOf(Data), 0);
{ Read frame header and check frame ID }
BlockRead(SourceFile, Frame, 6);
if not (Frame.ID[1] in ['A'..'Z']) then
Break;
{ Note data position and determine significant data size }
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;
{ Read frame data and set tag item if frame supported }
BlockRead(SourceFile, Data, DataSize);
SetTagItem(Frame.ID, Data, Tag);
Seek(SourceFile, DataPosition + FrameSize);
end;
except
end;
end;
{ --------------------------------------------------------------------------- }
function GetANSI(const Source: string): string;
var
Index: Integer;
FirstByte, SecondByte: Byte;
UnicodeChar: WideChar;
begin
{ Convert string from unicode if needed and trim spaces }
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
{ Get content preferring the first content }
Result := GetANSI(Content1);
if Result = '' then
Result := GetANSI(Content2);
end;
{ --------------------------------------------------------------------------- }
function ExtractTrack(const TrackString: string): Word;
var
Track: string;
Index, Value, Code: Integer;
begin
{ Extract track from string }
Track := GetANSI(TrackString);
{ Extract track from string like [02/20] }
Index := Pos('/', Track);
{ vlads fix: Extract track from string like [02 of 20] }
if Index = 0 then
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
{ Extract year from strings }
Result := GetANSI(YearString);
if Result = '' then
Result := Copy(GetANSI(DateString), 1, 4);
end;
{ --------------------------------------------------------------------------- }
function ExtractGenre(const GenreString: string): string;
begin
{ Extract genre from string }
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
{ Extract significant text data from a complex field }
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
{ Calculate new tag size (without padding) }
tagSIZE := 10;
for Iterator := 1 to ID3V2_FRAME_COUNT do
if Tag.Frame[Iterator] <> '' then
Inc(tagSIZE, Length(Tag.Frame[Iterator]) + 11);
{ Check for ability to change existing tag }
Tag.NeedRewrite :=
(Tag.ID <> ID3V2_ID) or
(GetTagSize(Tag) < tagSIZE) or
(GetTagSize(Tag) > ID3V2_MAX_SIZE);
{ Calculate padding size and set padded tag size }
if Tag.NeedRewrite then
Tag.PaddingSize := ID3V2_MAX_SIZE - tagSIZE
else
Tag.PaddingSize := GetTagSize(Tag) - tagSIZE;
Inc(tagSIZE, Tag.PaddingSize);
{ Build tag header }
Tag.ID := ID3V2_ID;
Tag.Version := TAG_VERSION_2_3;
Tag.Revision := 0;
Tag.Flags := 0;
{ Convert tag size }
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
{ Replace old tag with new tag data }
Result := False;
if (not FileExistsUnsetReadOnly(FileName)) then
Exit;
try
TagData.Position := 0;
Destination := TFileStream.Create(FileName, fmOpenReadWrite);
Destination.CopyFrom(TagData, TagData.Size);
Destination.Free;
Result := True;
except
{ Access error }
end;
end;
{ --------------------------------------------------------------------------- }
function RebuildFile(const FileName: string;
TagData: TStream): Boolean;
var
Tag: TagInfo;
Source, Destination: TFileStream;
BufferName: string;
begin
{ Rebuild file with old file data and new tag data (optional) }
Result := False;
if (not FileExistsUnsetReadOnly(FileName)) then
Exit;
if not ReadHeader(FileName, Tag) then
Exit;
if (TagData = nil) and (Tag.ID <> ID3V2_ID) then
Exit;
try
{ Create file streams }
BufferName := FileName + '~';
Source := TFileStream.Create(FileName, fmOpenRead or fmShareExclusive);
Destination := TFileStream.Create(BufferName, fmCreate);
{ Copy data blocks }
if Tag.ID = ID3V2_ID then
Source.Seek(GetTagSize(Tag), soFrombegin
ning);
if TagData <> nil then
Destination.CopyFrom(TagData, 0);
Destination.CopyFrom(Source, Source.Size - Source.Position);
{ Free resources }
Source.Free;
Destination.Free;
{ Replace old file and delete temporary file }
if (DeleteFile(FileName)) and (RenameFile(BufferName, FileName)) then
Result := True
else
raise Exception.Create('');
except
{ Access error }
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
{ Build and write tag header and frames to stream }
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;
{ Add padding }
FillChar(Padding, SizeOf(Padding), 0);
if Tag.PaddingSize > 0 then
TagData.Write(Padding, Tag.PaddingSize);
{ Rebuild file or replace tag with new tag data }
if Tag.NeedRewrite then
Result := RebuildFile(FileName, TagData)
else
Result := ReplaceTag(FileName, TagData);
TagData.Free;
end;
{ ********************** Private functions &
procedures ********************* }
procedure TID3v2.FSetTitle(const NewTitle: string);
begin
{ Set song title }
FTitle := Trim(NewTitle);
end;
{ --------------------------------------------------------------------------- }
procedure TID3v2.FSetArtist(const NewArtist: string);
begin
{ Set artist name }
FArtist := Trim(NewArtist);
end;
{ --------------------------------------------------------------------------- }
procedure TID3v2.FSetAlbum(const NewAlbum: string);
begin
{ Set album title }
FAlbum := Trim(NewAlbum);
end;
{ --------------------------------------------------------------------------- }
{ vlads fix: }
procedure TID3v2.FSetTrack(const NewTrack: Word);
begin
{ Set track number }
{ This will set a number value but Sting would be used when stored }
FTrack := NewTrack;
FTrackString := IntToStr(FTrack);
end;
{ --------------------------------------------------------------------------- }
{ vlads fix: }
procedure TID3v2.FSetTrackString(const NewTrack: string);
begin
{ Set track number }
FTrackString := NewTrack;
{ This will set a number value but Sting would be used when stored }
FTrack := ExtractTrack(FTrackString);
end;
{ --------------------------------------------------------------------------- }
procedure TID3v2.FSetYear(const NewYear: string);
begin
{ Set release year }
FYear := Trim(NewYear);
end;
{ --------------------------------------------------------------------------- }
procedure TID3v2.FSetGenre(const NewGenre: string);
begin
{ Set genre name }
FGenre := Trim(NewGenre);
end;
{ --------------------------------------------------------------------------- }
procedure TID3v2.FSetComment(const NewComment: string);
begin
{ Set comment }
FComment := Trim(NewComment);
end;
{ --------------------------------------------------------------------------- }
procedure TID3v2.FSetComposer(const NewComposer: string);
begin
{ Set composer name }
FComposer := Trim(NewComposer);
end;
{ --------------------------------------------------------------------------- }
procedure TID3v2.FSetEncoder(const NewEncoder: string);
begin
{ Set encoder name }
FEncoder := Trim(NewEncoder);
end;
{ --------------------------------------------------------------------------- }
procedure TID3v2.FSetCopyright(const NewCopyright: string);
begin
{ Set copyright information }
FCopyright := Trim(NewCopyright);
end;
{ --------------------------------------------------------------------------- }
procedure TID3v2.FSetLanguage(const NewLanguage: string);
begin
{ Set language }
FLanguage := Trim(NewLanguage);
end;
{ --------------------------------------------------------------------------- }
procedure TID3v2.FSetLink(const NewLink: string);
begin
{ Set URL link }
Flink := Trim(NewLink);
end;
{ ********************** Public functions &
procedures ********************** }
constructor TID3v2.Create;
begin
{ Create object }
inherited;
ResetData;
end;
{ --------------------------------------------------------------------------- }
procedure TID3v2.ResetData;
begin
{ Reset all variables }
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
SourceFile: file;
begin
{ Reset data and load header from file to variable }
ResetData;
try
AssignFile(SourceFile, FileName);
FileMode := 0;
Reset(SourceFile, 1);
except
Result := False;
Exit;
end;
Result := ProcessRead(SourceFile);
CloseFile(SourceFile);
end;
function TID3v2.ReadFromFile(var SourceFile: file): Boolean;
begin
{ Reset data and load header from file to variable }
ResetData;
Result := ProcessRead(SourceFile);
end;
function TID3v2.ProcessRead(var SourceFile: file): Boolean;
var
Tag: TagInfo;
begin
Result := ReadHeader(SourceFile, Tag);
{ Process data if loaded and header valid }
if (Result) and (Tag.ID = ID3V2_ID) then
begin
FExists := True;
{ Fill properties with header data }
FVersionID := Tag.Version;
FSize := GetTagSize(Tag);
{ Get information from frames if version supported }
if (FVersionID in [TAG_VERSION_2_2..TAG_VERSION_2_4]) and (FSize > 0) then
begin
if FVersionID > TAG_VERSION_2_2 then
ReadFramesNew(SourceFile, Tag)
else
ReadFramesOld(SourceFile, Tag);
FTitle := TruncateToZerro(GetContent(Tag.Frame[1], Tag.Frame[15]));
FArtist := TruncateToZerro(GetContent(Tag.Frame[2], Tag.Frame[14]));
FAlbum := TruncateToZerro(GetContent(Tag.Frame[3], Tag.Frame[16]));
FTrack := ExtractTrack(Tag.Frame[4]);
FTrackString := TruncateToZerro(GetANSI(Tag.Frame[4]));
FYear := TruncateToZerro(ExtractYear(Tag.Frame[5], Tag.Frame[13]));
FGenre := TruncateToZerro(ExtractGenre(Tag.Frame[6]));
FComment := TruncateToZerro(ExtractText(Tag.Frame[7], True));
FComposer := TruncateToZerro(GetANSI(Tag.Frame[8]));
FEncoder := TruncateToZerro(GetANSI(Tag.Frame[9]));
FCopyright := TruncateToZerro(GetANSI(Tag.Frame[10]));
FLanguage := TruncateToZerro(GetANSI(Tag.Frame[11]));
Flink := TruncateToZerro(ExtractText(Tag.Frame[12], False));
end;
end;
end;
{ --------------------------------------------------------------------------- }
function TID3v2.SaveToFile(const FileName: string): Boolean;
var
Tag: TagInfo;
begin
{ Check for existing tag }
FillChar(Tag, SizeOf(Tag), 0);
ReadHeader(FileName, Tag);
{ Prepare tag data and save to file }
Tag.Frame[1] := FTitle;
Tag.Frame[2] := FArtist;
Tag.Frame[3] := FAlbum;
{vlads fix: Save Track as Sting not as Number}
// if FTrack > 0 then
Tag.Frame[4] := IntToStr(FTrack);
Tag.Frame[4] := FTrackString;
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
{ Remove tag from file }
Result := RebuildFile(FileName, nil);
end;
end.