每一个MP3文件包含有一个ID3-Tag头, 它用来提供艺术家、标题、专辑、出版年和歌曲流派等信息。这个头总是128字节长并位于MP3文件末尾。ID3-Tag 结构是这样的: type TID3Tag = packed record // 128 字节 TAGID: array[0..2] of char; // 3 字节: 必须是TAG Title: array[0..29] of char; // 30 字节: 歌曲标题 Artist: array[0..29] of char; // 30 字节: 歌曲的艺术家 Album: array[0..29] of char; // 30 字节: 歌曲专辑 Year: array[0..3] of char; // 4 字节: 出版年 Comment: array[0..29] of char; // 30 字节: 评论 Genre: byte; // 1 字节: 种类标识 end; 为读取ID3-Tag信息并在一个对话框中显示,试试这个函数: procedure TForm1.Button1Click(Sender: TObject); const _mp3file='G:\Mp3\Miscellaneous\ATC - Around The World.mp3'; var id3tag: Tid3tag; mp3file: Tfilestream; begin mp3file:=Tfilestream.create(_mp3file,fmOpenRead); try mp3file.position:=mp3file.size-128; // 跳到id3-tag mp3file.Read(id3tag,SizeOf(id3tag)); showmessage(' Title: '+id3tag.title+#13+ ' Artist: '+id3tag.artist+#13+ ' Album: '+id3tag.album+#13+ ' Year: '+id3tag.year+#13+ ' Comment: '+id3tag.comment+#13+ ' Genre-ID: '+inttostr(id3tag.genre) ); finally mp3file.free; end; end; 这个函数读取_mp3file描述的文件,跳到倒数第128个字节,读取并在对话框中显示信息。
mp3file:=Tfilestream.create(_mp3file,fmOpenRead);
这句出错?没道理啊?
现在好了,可能是文件被占用可是我写进去的东西,用MEDIAPLAYER读不出来啊,播放倒是没有问题
使用这个类,被我修改了的:当不存在ID3标签的时候,将抛出异常。 unit untMP3Tags;interface uses Classes,SysUtils; Const erCancel = 0; erSave = 1; erRemove = 2; MAXGENRES = 114; GENRES: array [0..MAXGENRES] of String = ( 'Blues','Classic Rock','Country','Dance','Disco','Funk','Grunge','Hip-Hop','Jazz','Metal','New Age','Oldies', 'Other','Pop','R&B','Rap','Reggae','Rock','Techno','Industrial','Alternative','Ska','Death Metal','Pranks', 'Soundtrack','Euro-Techno','Ambient','Trip-Hop','Vocal','Jazz+Funk','Fusion','Trance','Classical','Instrumental', 'Acid','House','Game','Sound Clip','Gospel','Noise','AlternRock','Bass','Soul','Punk','Space','Meditative', 'Instrumental Pop','Instrumental Rock','Ethnic','Gothic','Darkwave','Techno-Industrial','Electronic','Pop-Folk', 'Eurodance','Dream','Southern Rock','Comedy','Cult','Gangsta','Top 40','Christian Rap','Pop/Funk','Jungle', 'Native American','Cabaret','New Wave','Psychadelic','Rave','Showtunes','Trailer','Lo-Fi','Tribal','Acid Punk', 'Acid Jazz','Polka','Retro','Musical','Rock & Roll','Hard Rock','Folk','Folk/Rock','National Folk','Swing','Bebob', 'Latin','Revival','Celtic','Bluegrass','Avantgarde','Gothic Rock','Progressive Rock','Psychedelic Rock','Symphonic Rock', 'Slow Rock','Big Band','Chorus','Easy Listening','Acoustic','Humour','Speech','Chanson','Opera','Chamber Music','Sonata', 'Symphony','Booty Bass','Primus','Porn Groove','Satire','Slow Jam','Club','Tango','Samba','Folklore' ); type String03 = String[3]; String04 = String[4]; String30 = String[30]; TFileName = Type String; TTag = packed record Tag : String03; Title : String30; Artist : String30; Album : String30; Year : String04; Comment : String30; GenreID : Byte; end; Type TReadWriteTag = packed record TAG : 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; TErrorEvent = procedure(Sender: TObject; Error : String) of Object; TMP3Tag = class(TComponent) private FFileName : TFileName; FValid : Boolean; FTag : TTag; FGenre : String; FError : String; FAutoSave : Boolean; //If you press "Save" in the edit box it saves automatically FOnFileChange : TNotifyEvent; FOnChange : TNotifyEvent; FOnError : TErrorEvent; procedure SetFileName(Value: TFileName); procedure SetTitle(Value: String30); procedure SetArtist(Value: String30); procedure SetAlbum(Value: String30); procedure SetYear(Value: String04); procedure SetComment(Value: String30); procedure SetGenreID(Value: Byte); procedure SetTag(Value: TTag); protected public procedure Open; procedure Remove; procedure Save; function GenreToByte(const strGenre:string):BYTE; function ByteToGenre(const ID:BYTE):string; // Function Edit:Byte; published constructor Create(AOwner: TComponent); override; destructor Destroy; override; property Filename :TFilename read FFilename write SetFilename; property MP3Tag : TTag read FTag write SetTag; property Title : String30 read FTag.Title write SetTitle; property Artist : String30 read FTag.Artist write SetArtist; property Album : String30 read FTag.Album write SetAlbum; property Year : String04 read FTag.Year write SetYear; property Comment : String30 read FTag.Comment write SetComment; property GenreID : Byte read FTag.GenreID write SetGenreID; property Genre : String read FGenre; property EditAutoSave : Boolean read FAutoSave write FAutoSave; property Valid: Boolean read FValid; property Error: String read FError; property onFileNameChange : TNotifyEvent read FOnFileChange write FOnFileChange; property onChange : TNotifyEvent read FOnChange write FOnChange; property onError : TErrorEvent read FOnError write FOnError; end;implementation procedure TMP3Tag.SetFileName(Value: TFileName); begin FFileName:=Value; Open; if Assigned(onFileNameChange) then onFileNameChange(Self);end; procedure TMP3Tag.SetTitle(Value: String30); begin FTag.Title:=Value; if Assigned(onChange) then onChange(Self); end;procedure TMP3Tag.SetArtist(Value: String30); begin FTag.Artist:=Value; if Assigned(onChange) then onChange(Self); end;procedure TMP3Tag.SetAlbum(Value: String30); begin FTag.Album:=Value; if Assigned(onChange) then onChange(Self); end;procedure TMP3Tag.SetYear(Value: String04); begin FTag.Year:=Value; if Assigned(onChange) then onChange(Self); end;procedure TMP3Tag.SetComment(Value: String30); begin FTag.Comment:=Value; if Assigned(onChange) then onChange(Self); end;procedure TMP3Tag.SetGenreID(Value: Byte); begin FTag.GenreID:=Value; if Assigned(onChange) then onChange(Self); end;{------------------------------------}procedure TMP3Tag.SetTag(Value: TTag); begin FTag:=Value; if Assigned(onChange) then onChange(Self); end;{-------------------------------------}procedure ClearTag(Var ATag : TTag); Begin With aTag do Begin Tag:=''; Title:=''; Artist:=''; Album:=''; Year:=''; Comment:=''; GenreID:=12; end; End;
procedure TMP3Tag.Open;Function RemoveSpaces(St : String):String; Var T : Byte; Begin T:=Length(ST); While (T>0) and (St[T]=' ') do Dec(T); Result:=Copy(St,1,T); End; procedure FillTag(STag : TReadWriteTag; Var DTag : TTag); Var T : Byte; Begin ClearTag(DTag); For T:=1 to 3 do DTag.Tag:=DTag.Tag+STag.Tag[T]; For T:=1 to 4 do DTag.Year:=DTag.Year+STag.Year[T]; For T:=1 to 30 do Begin DTag.Artist:=DTag.Artist+STag.Artist[T]; DTag.Title:=DTag.Title+STag.Title[T]; DTag.Album:=DTag.Album+STag.Album[T]; DTag.Comment:=DTag.Comment+STag.Comment[T]; end; DTag.GenreID:=STag.Genre; DTag.Artist:=RemoveSpaces(DTag.Artist); DTag.Title:=RemoveSpaces(DTag.Title); DTag.Album:=RemoveSpaces(DTag.Album); DTag.Comment:=RemoveSpaces(DTag.Comment); DTag.Year:=RemoveSpaces(DTag.Year); end;Var aStream : TFileStream; ATag : TReadWriteTag; begin FValid:=False; ClearTag(FTag); //asm int 3;end; IF FileExists(FFileName) then Begin aStream := TFileStream.Create(FFileName, fmOpenRead or fmShareDenyWrite); IF AStream.Size>128 then Begin aStream.Seek(-128,soFromEnd); aStream.ReadBuffer(aTag, 128); FillTag(aTag,FTag); IF FTag.Tag = 'TAG' then Begin FValid:=True; end Else Begin FError:='MP3 does not contain a mp3 tag or wrong file format!'; ClearTag(FTag); FTag.Tag:='TAG'; FTag.Title:=Copy(ExtractFileName(FFileName),1,30); end; end else FError:='File too small to be a MP3 file!'; aStream.Free; end else FError:='File does not exist!'; IF Not FValid then IF Assigned(OnError) then raise Exception.Create(FError); end;procedure TMP3Tag.Remove; var aFile : file; aTag : TReadWriteTag;begin If FileExists (FFileName) then begin AssignFile (aFile, FFileName); {$I-} Reset (aFile,1); {$I+} if (IOResult=0) and (FileSize(aFile) > 128) then begin Seek(AFile,FileSize(aFile)-128); BlockRead(aFile, ATag, 128); if aTag.TAG='TAG' then begin Seek (aFile,FileSize(aFile)-128); Truncate(aFile); end; Close(aFile); end; ClearTag(FTag); Open; end; end;procedure TMP3Tag.Save;procedure FillReadWriteTag(STag : TTag; Var DTag : TReadWriteTag); Begin FillChar (DTag, SizeOf(DTag), 0); DTag.Tag:='TAG'; Move (STag.Year[1],DTag.Year, Length(STag.Year)); Move (STag.Artist[1],DTag.Artist, Length(STag.Artist)); Move (STag.Title[1],DTag.Title, Length(STag.Title)); Move (STag.Album[1],DTag.Album, Length(STag.Album)); Move (STag.Comment[1],DTag.Comment, Length(STag.Comment)); DTag.Genre:=STag.GenreID; end; Var aStream : TFileStream; aTag : TReadWriteTag; begin FError:=''; IF FileExists(FFileName) then Begin aStream := TFileStream.Create(FFileName, fmOpenReadWrite or fmShareDenyWrite); IF AStream.Size>128 then Begin aStream.Seek(-128,soFromEnd); aStream.ReadBuffer(aTag, 128); IF aTag.Tag = 'TAG' then aStream.Seek(-128,soFromEnd) Else aStream.Seek(0,soFromEnd); end else aStream.Seek(0,soFromEnd); FillReadWriteTag(FTag,ATag); aStream.WriteBuffer(aTag, 128); aStream.Free; end else FError:='File does not exist!'; IF FError<>'' then IF Assigned(OnError) then OnError(Self,FError); end; function TMP3Tag.GenreToByte(const strGenre:string):byte; var i:integer; begin GenreToByte:=$ff; for i:=0 to MAXGENRES do if strGenre= GENRES[i] then GenreToByte:=i; end; function TMP3Tag.ByteToGenre(const ID:BYTE):string; begin if ((ID>= 0) and (ID<=MAXGENRES)) then ByteToGenre:=GENRES[ID] else ByteToGenre:=''; end;{-------------------------------------}constructor TMP3Tag.Create(AOwner: TComponent); begin inherited Create(AOwner); FError:=''; FAutoSave:=True; FFileName:=''; FValid:=false; FGenre:=''; FError:=''; FAutoSave :=false; end;destructor TMP3Tag.Destroy; begin inherited Destroy; end;{---------------------------} end.
在Open方法中,把最后一句改为: IF Not FValid then IF Assigned(OnError) then OnError(FError) else raise Exception.Create(FError);
TID3Tag = packed record // 128 字节
TAGID: array[0..2] of char; // 3 字节: 必须是TAG
Title: array[0..29] of char; // 30 字节: 歌曲标题
Artist: array[0..29] of char; // 30 字节: 歌曲的艺术家
Album: array[0..29] of char; // 30 字节: 歌曲专辑
Year: array[0..3] of char; // 4 字节: 出版年
Comment: array[0..29] of char; // 30 字节: 评论
Genre: byte; // 1 字节: 种类标识
end; 为读取ID3-Tag信息并在一个对话框中显示,试试这个函数: procedure TForm1.Button1Click(Sender: TObject);
const
_mp3file='G:\Mp3\Miscellaneous\ATC - Around The World.mp3';
var
id3tag: Tid3tag;
mp3file: Tfilestream;
begin
mp3file:=Tfilestream.create(_mp3file,fmOpenRead);
try
mp3file.position:=mp3file.size-128; // 跳到id3-tag
mp3file.Read(id3tag,SizeOf(id3tag));
showmessage(' Title: '+id3tag.title+#13+
' Artist: '+id3tag.artist+#13+
' Album: '+id3tag.album+#13+
' Year: '+id3tag.year+#13+
' Comment: '+id3tag.comment+#13+
' Genre-ID: '+inttostr(id3tag.genre)
);
finally
mp3file.free;
end;
end; 这个函数读取_mp3file描述的文件,跳到倒数第128个字节,读取并在对话框中显示信息。
这句出错?没道理啊?
unit untMP3Tags;interface
uses Classes,SysUtils;
Const
erCancel = 0;
erSave = 1;
erRemove = 2; MAXGENRES = 114;
GENRES: array [0..MAXGENRES] of String = (
'Blues','Classic Rock','Country','Dance','Disco','Funk','Grunge','Hip-Hop','Jazz','Metal','New Age','Oldies',
'Other','Pop','R&B','Rap','Reggae','Rock','Techno','Industrial','Alternative','Ska','Death Metal','Pranks',
'Soundtrack','Euro-Techno','Ambient','Trip-Hop','Vocal','Jazz+Funk','Fusion','Trance','Classical','Instrumental',
'Acid','House','Game','Sound Clip','Gospel','Noise','AlternRock','Bass','Soul','Punk','Space','Meditative',
'Instrumental Pop','Instrumental Rock','Ethnic','Gothic','Darkwave','Techno-Industrial','Electronic','Pop-Folk',
'Eurodance','Dream','Southern Rock','Comedy','Cult','Gangsta','Top 40','Christian Rap','Pop/Funk','Jungle',
'Native American','Cabaret','New Wave','Psychadelic','Rave','Showtunes','Trailer','Lo-Fi','Tribal','Acid Punk',
'Acid Jazz','Polka','Retro','Musical','Rock & Roll','Hard Rock','Folk','Folk/Rock','National Folk','Swing','Bebob',
'Latin','Revival','Celtic','Bluegrass','Avantgarde','Gothic Rock','Progressive Rock','Psychedelic Rock','Symphonic Rock',
'Slow Rock','Big Band','Chorus','Easy Listening','Acoustic','Humour','Speech','Chanson','Opera','Chamber Music','Sonata',
'Symphony','Booty Bass','Primus','Porn Groove','Satire','Slow Jam','Club','Tango','Samba','Folklore'
);
type
String03 = String[3];
String04 = String[4];
String30 = String[30]; TFileName = Type String; TTag = packed record
Tag : String03;
Title : String30;
Artist : String30;
Album : String30;
Year : String04;
Comment : String30;
GenreID : Byte;
end;
Type
TReadWriteTag = packed record
TAG : 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;
TErrorEvent = procedure(Sender: TObject; Error : String) of Object; TMP3Tag = class(TComponent)
private
FFileName : TFileName;
FValid : Boolean;
FTag : TTag;
FGenre : String;
FError : String; FAutoSave : Boolean;
//If you press "Save" in the edit box it saves automatically FOnFileChange : TNotifyEvent;
FOnChange : TNotifyEvent;
FOnError : TErrorEvent; procedure SetFileName(Value: TFileName);
procedure SetTitle(Value: String30);
procedure SetArtist(Value: String30);
procedure SetAlbum(Value: String30);
procedure SetYear(Value: String04);
procedure SetComment(Value: String30);
procedure SetGenreID(Value: Byte);
procedure SetTag(Value: TTag); protected public
procedure Open;
procedure Remove;
procedure Save;
function GenreToByte(const strGenre:string):BYTE;
function ByteToGenre(const ID:BYTE):string;
// Function Edit:Byte;
published
constructor Create(AOwner: TComponent); override;
destructor Destroy; override; property Filename :TFilename read FFilename write SetFilename;
property MP3Tag : TTag read FTag write SetTag; property Title : String30 read FTag.Title write SetTitle;
property Artist : String30 read FTag.Artist write SetArtist;
property Album : String30 read FTag.Album write SetAlbum;
property Year : String04 read FTag.Year write SetYear;
property Comment : String30 read FTag.Comment write SetComment;
property GenreID : Byte read FTag.GenreID write SetGenreID;
property Genre : String read FGenre; property EditAutoSave : Boolean read FAutoSave write FAutoSave; property Valid: Boolean read FValid;
property Error: String read FError; property onFileNameChange : TNotifyEvent read FOnFileChange write FOnFileChange;
property onChange : TNotifyEvent read FOnChange write FOnChange;
property onError : TErrorEvent read FOnError write FOnError;
end;implementation
procedure TMP3Tag.SetFileName(Value: TFileName);
begin
FFileName:=Value; Open;
if Assigned(onFileNameChange) then onFileNameChange(Self);end;
procedure TMP3Tag.SetTitle(Value: String30);
begin
FTag.Title:=Value;
if Assigned(onChange) then onChange(Self);
end;procedure TMP3Tag.SetArtist(Value: String30);
begin
FTag.Artist:=Value;
if Assigned(onChange) then onChange(Self);
end;procedure TMP3Tag.SetAlbum(Value: String30);
begin
FTag.Album:=Value;
if Assigned(onChange) then onChange(Self);
end;procedure TMP3Tag.SetYear(Value: String04);
begin
FTag.Year:=Value;
if Assigned(onChange) then onChange(Self);
end;procedure TMP3Tag.SetComment(Value: String30);
begin
FTag.Comment:=Value;
if Assigned(onChange) then onChange(Self);
end;procedure TMP3Tag.SetGenreID(Value: Byte);
begin
FTag.GenreID:=Value;
if Assigned(onChange) then onChange(Self);
end;{------------------------------------}procedure TMP3Tag.SetTag(Value: TTag);
begin
FTag:=Value;
if Assigned(onChange) then onChange(Self);
end;{-------------------------------------}procedure ClearTag(Var ATag : TTag);
Begin
With aTag do
Begin
Tag:='';
Title:='';
Artist:='';
Album:='';
Year:='';
Comment:='';
GenreID:=12;
end;
End;
procedure TMP3Tag.Open;Function RemoveSpaces(St : String):String;
Var
T : Byte;
Begin
T:=Length(ST);
While (T>0) and (St[T]=' ') do
Dec(T);
Result:=Copy(St,1,T);
End;
procedure FillTag(STag : TReadWriteTag; Var DTag : TTag);
Var
T : Byte;
Begin ClearTag(DTag); For T:=1 to 3 do
DTag.Tag:=DTag.Tag+STag.Tag[T]; For T:=1 to 4 do
DTag.Year:=DTag.Year+STag.Year[T]; For T:=1 to 30 do
Begin
DTag.Artist:=DTag.Artist+STag.Artist[T];
DTag.Title:=DTag.Title+STag.Title[T];
DTag.Album:=DTag.Album+STag.Album[T];
DTag.Comment:=DTag.Comment+STag.Comment[T];
end; DTag.GenreID:=STag.Genre; DTag.Artist:=RemoveSpaces(DTag.Artist);
DTag.Title:=RemoveSpaces(DTag.Title);
DTag.Album:=RemoveSpaces(DTag.Album);
DTag.Comment:=RemoveSpaces(DTag.Comment);
DTag.Year:=RemoveSpaces(DTag.Year);
end;Var
aStream : TFileStream;
ATag : TReadWriteTag;
begin
FValid:=False;
ClearTag(FTag);
//asm int 3;end;
IF FileExists(FFileName) then
Begin
aStream := TFileStream.Create(FFileName, fmOpenRead or fmShareDenyWrite);
IF AStream.Size>128 then
Begin
aStream.Seek(-128,soFromEnd);
aStream.ReadBuffer(aTag, 128);
FillTag(aTag,FTag);
IF FTag.Tag = 'TAG' then
Begin
FValid:=True;
end Else
Begin
FError:='MP3 does not contain a mp3 tag or wrong file format!';
ClearTag(FTag);
FTag.Tag:='TAG';
FTag.Title:=Copy(ExtractFileName(FFileName),1,30);
end; end else FError:='File too small to be a MP3 file!';
aStream.Free;
end else FError:='File does not exist!';
IF Not FValid then
IF Assigned(OnError) then raise Exception.Create(FError);
end;procedure TMP3Tag.Remove;
var
aFile : file;
aTag : TReadWriteTag;begin
If FileExists (FFileName) then begin
AssignFile (aFile, FFileName);
{$I-}
Reset (aFile,1);
{$I+}
if (IOResult=0) and (FileSize(aFile) > 128) then begin
Seek(AFile,FileSize(aFile)-128);
BlockRead(aFile, ATag, 128);
if aTag.TAG='TAG' then begin
Seek (aFile,FileSize(aFile)-128);
Truncate(aFile);
end;
Close(aFile);
end; ClearTag(FTag);
Open;
end;
end;procedure TMP3Tag.Save;procedure FillReadWriteTag(STag : TTag; Var DTag : TReadWriteTag);
Begin FillChar (DTag, SizeOf(DTag), 0); DTag.Tag:='TAG'; Move (STag.Year[1],DTag.Year, Length(STag.Year));
Move (STag.Artist[1],DTag.Artist, Length(STag.Artist));
Move (STag.Title[1],DTag.Title, Length(STag.Title));
Move (STag.Album[1],DTag.Album, Length(STag.Album));
Move (STag.Comment[1],DTag.Comment, Length(STag.Comment));
DTag.Genre:=STag.GenreID;
end;
Var
aStream : TFileStream;
aTag : TReadWriteTag;
begin
FError:='';
IF FileExists(FFileName) then
Begin
aStream := TFileStream.Create(FFileName, fmOpenReadWrite or fmShareDenyWrite);
IF AStream.Size>128 then
Begin
aStream.Seek(-128,soFromEnd);
aStream.ReadBuffer(aTag, 128);
IF aTag.Tag = 'TAG' then aStream.Seek(-128,soFromEnd)
Else aStream.Seek(0,soFromEnd);
end else aStream.Seek(0,soFromEnd);
FillReadWriteTag(FTag,ATag);
aStream.WriteBuffer(aTag, 128);
aStream.Free;
end else FError:='File does not exist!'; IF FError<>'' then
IF Assigned(OnError) then OnError(Self,FError);
end; function TMP3Tag.GenreToByte(const strGenre:string):byte;
var i:integer;
begin
GenreToByte:=$ff;
for i:=0 to MAXGENRES do
if strGenre= GENRES[i] then GenreToByte:=i;
end;
function TMP3Tag.ByteToGenre(const ID:BYTE):string;
begin
if ((ID>= 0) and (ID<=MAXGENRES)) then ByteToGenre:=GENRES[ID] else ByteToGenre:='';
end;{-------------------------------------}constructor TMP3Tag.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
FError:='';
FAutoSave:=True; FFileName:='';
FValid:=false; FGenre:='';
FError:=''; FAutoSave :=false;
end;destructor TMP3Tag.Destroy;
begin
inherited Destroy;
end;{---------------------------}
end.
IF Not FValid then
IF Assigned(OnError) then
OnError(FError)
else
raise Exception.Create(FError);