如何获取mp3的信息。 ( 积分: 200 )

  • 主题发起人 wyj19802004
  • 开始时间
W

wyj19802004

Unregistered / Unconfirmed
GUEST, unregistred user!
我这几天正在尝试做一个MP3播放器,它的一些基本信息如:歌名、演唱者、唱片名、
发行年、备注等,已经能获取。
主要是:MP3持续时间,不知道如何取。
(它的播放速率有64kbps,128kbps,192kbps,291kbps等,搞的我焦头烂额,
谁有现成的控件、源码、DLL最好)。

200分相赠!
 
我这几天正在尝试做一个MP3播放器,它的一些基本信息如:歌名、演唱者、唱片名、
发行年、备注等,已经能获取。
主要是:MP3持续时间,不知道如何取。
(它的播放速率有64kbps,128kbps,192kbps,291kbps等,搞的我焦头烂额,
谁有现成的控件、源码、DLL最好)。

200分相赠!
 
TMediaPlayer1组件的TrackLength函数可获得mp3得毫秒数如:MediaPlayer1.TrackLength[1]
另你知道如何录音成mp3吗知道请告诉我好吗?
 
用TrackLength函数获取的是位速是128kbps,帧数为24的时间。如果mp3是其它位速,
时间就会有偏差。
用Tmediaplayer播放,并且用TrackLength获取时间。当两个不同位速的mp3交替播放时,
有时会发生自动关闭或者时间不对的情况。(我是参考金山播放器获取的时间。)
 
搜索Mp3Info,就可以找到很多你要的东西了
 
如果想要获取64kbps,128kbps,192kbps,291kbps等,使用MPEGaudio.pas单元。
 
大家能不能说的再详细些,但回答就有分,1号不管怎样把分给大家分了。
 
俺也想知道!
 
在 Active X中添加 一个mp3play.dll(具体的名字不太记得)
 
ID3v1.pas 单元
==============================================================================
unit ID3v1;

interface

uses
Classes, SysUtils;

const
MAX_MUSIC_GENRES = 148;
{ Max. number of music genres }
DEFAULT_GENRE = 255;
{ Index for default genre }

{ Used with VersionID property }
TAG_VERSION_1_0 = 1;
{ Index for ID3v1.0 tag }
TAG_VERSION_1_1 = 2;
{ Index for ID3v1.1 tag }

var
MusicGenre: array [0..MAX_MUSIC_GENRES - 1] of string;
{ Genre names }

type
{ Used in TID3v1 class }
String04 = string[4];
{ String with max. 4 symbols }
String30 = string[30];
{ String with max. 30 symbols }

{ Class TID3v1 }
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;
{ Create object }
procedure ResetData;
{ Reset all data }
function ReadFromFile(const FileName: string): Boolean;
{ Load tag }
function RemoveFromFile(const FileName: string): Boolean;
{ Delete tag }
function SaveToFile(const FileName: string): Boolean;
{ Save tag }
property Exists: Boolean read FExists;
{ True if tag found }
property VersionID: Byte read FVersionID;
{ Version code }
property Title: String30 read FTitle write FSetTitle;
{ Song title }
property Artist: String30 read FArtist write FSetArtist;
{ Artist name }
property Album: String30 read FAlbum write FSetAlbum;
{ Album name }
property Year: String04 read FYear write FSetYear;
{ Year }
property Comment: String30 read FComment write FSetComment;
{ Comment }
property Track: Byte read FTrack write FSetTrack;
{ Track number }
property GenreID: Byte read FGenreID write FSetGenreID;
{ Genre code }
property Genre: string read FGetGenre;
{ Genre name }
end;


implementation

type
{ Real structure of ID3v1 tag }
TagRecord = record
Header: array [1..3] of Char;
{ Tag header - must be "TAG"
}
Title: array [1..30] of Char;
{ Title data }
Artist: array [1..30] of Char;
{ Artist data }
Album: array [1..30] of Char;
{ Album data }
Year: array [1..4] of Char;
{ Year data }
Comment: array [1..30] of Char;
{ Comment data }
Genre: Byte;
{ Genre data }
end;


{ ********************* Auxiliary functions &
procedures ******************** }

function ReadTag(const FileName: string;
var TagData: TagRecord): Boolean;
var
SourceFile: file;
begin

try
Result := true;
{ Set read-access and open file }
AssignFile(SourceFile, FileName);
FileMode := 0;

Reset(SourceFile, 1);
{ Read tag }
Seek(SourceFile, FileSize(SourceFile) - 128);
BlockRead(SourceFile, TagData, 128);
CloseFile(SourceFile);
except
{ Error }
Result := false;
end;

end;


{ --------------------------------------------------------------------------- }

function RemoveTag(const FileName: string): Boolean;
var
SourceFile: file;
begin

try
Result := true;
{ Allow write-access and open file }
FileSetAttr(FileName, 0);
AssignFile(SourceFile, FileName);
FileMode := 2;
Reset(SourceFile, 1);
{ Delete tag }
Seek(SourceFile, FileSize(SourceFile) - 128);
Truncate(SourceFile);
CloseFile(SourceFile);
except
{ Error }
Result := false;
end;

end;


{ --------------------------------------------------------------------------- }

function SaveTag(const FileName: string;
TagData: TagRecord): Boolean;
var
SourceFile: file;
begin

try
Result := true;
{ Allow write-access and open file }
FileSetAttr(FileName, 0);
AssignFile(SourceFile, FileName);
FileMode := 2;
Reset(SourceFile, 1);
{ Write tag }
Seek(SourceFile, FileSize(SourceFile));
BlockWrite(SourceFile, TagData, SizeOf(TagData));
CloseFile(SourceFile);
except
{ Error }
Result := false;
end;

end;


{ --------------------------------------------------------------------------- }

function GetTagVersion(const TagData: TagRecord): Byte;
begin

Result := TAG_VERSION_1_0;
{ Terms for ID3v1.1 }
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;


{ ********************** Private functions &
procedures ********************* }

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 := '';
{ Return an empty string if the current GenreID is not valid }
if FGenreID in [0..MAX_MUSIC_GENRES - 1] then
Result := MusicGenre[FGenreID];
end;


{ ********************** Public functions &
procedures ********************** }

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

{ Reset and load tag data from file to variable }
ResetData;
Result := ReadTag(FileName, TagData);
{ Process data if loaded and tag header OK }
if (Result) and (TagData.Header = 'TAG') then

begin

FExists := true;
FVersionID := GetTagVersion(TagData);
{ Fill properties with tag data }
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

{ Find tag }
Result := ReadTag(FileName, TagData);
{ Delete tag if loaded and tag header OK }
if (Result) and (TagData.Header = 'TAG') then
Result := RemoveTag(FileName);
end;


{ --------------------------------------------------------------------------- }

function TID3v1.SaveToFile(const FileName: string): Boolean;
var
TagData: TagRecord;
begin

{ Prepare tag record }
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;
{ Delete old tag and write new tag }
Result := (RemoveFromFile(FileName)) and (SaveTag(FileName, TagData));
end;


{ ************************** Initialize music genres ************************ }

initialization
begin

{ Standard genres }
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';
{ Extended genres }
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 单元

unit ID3v2;

interface

uses
Classes, SysUtils;

const
TAG_VERSION_2_3 = 3;
{ Code for ID3v2.3.x tag }

type
{ Class TID3v2 }
TID3v2 = class(TObject)
private
{ Private declarations }
FExists: Boolean;
FVersionID: Byte;
FSize: Integer;
FTitle: string;
FArtist: string;
FAlbum: string;
FTrack: Byte;
FYear: string;
FGenre: string;
FComment: string;
procedure FSetTitle(const NewTitle: string);
procedure FSetArtist(const NewArtist: string);
procedure FSetAlbum(const NewAlbum: string);
procedure FSetTrack(const NewTrack: Byte);
procedure FSetYear(const NewYear: string);
procedure FSetGenre(const NewGenre: string);
procedure FSetComment(const NewComment: string);
public
{ Public declarations }
constructor Create;
{ Create object }
procedure ResetData;
{ Reset all data }
function ReadFromFile(const FileName: string): Boolean;
{ Load tag }
function RemoveFromFile(const FileName: string): Boolean;
{ Delete tag }
function SaveToFile(const FileName: string): Boolean;
{ Save 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: Byte read FTrack write FSetTrack;
{ Track number }
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 }
end;


implementation

const
{ ID3v2 tag ID }
ID3V2_ID = 'ID3';

{ Max. number of supported tag frames }
ID3V2_FRAME_COUNT = 7;

{ Names of supported tag frames }
ID3V2_FRAME: array [1..ID3V2_FRAME_COUNT] of string =
('TIT2', 'TPE1', 'TALB', 'TRCK', 'TYER', 'TCON', 'COMM');

type
{ ID3v2 frame header }
FrameHeader = record
ID: array [1..4] of Char;
{ Frame ID }
Size: Integer;
{ Size excluding header }
Flags: Word;
{ Flags }
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 }
end;


{ ********************* Auxiliary functions &
procedures ******************** }

function ReadHeader(const FileName: string;
var Tag: TagInfo): Boolean;
var
SourceFile: file;
Transferred: Integer;
begin

try
Result := true;
{ Set read-access and open file }
AssignFile(SourceFile, FileName);
FileMode := 0;
Reset(SourceFile, 1);
{ Read header and get file size }
BlockRead(SourceFile, Tag, 10, Transferred);
Tag.FileSize := FileSize(SourceFile);
CloseFile(SourceFile);
{ if transfer is not complete }
if Transferred <
10 then
Result := false;
except
{ Error }
Result := false;
end;

end;


{ --------------------------------------------------------------------------- }

function GetVersionID(const Tag: TagInfo): Byte;
begin

{ Get tag version from header }
Result := Tag.Version;
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 Result >
Tag.FileSize then
Result := 0;
end;


{ --------------------------------------------------------------------------- }

procedure SetTagItem(const ID, Data: string;
var Tag: TagInfo);
var
Iterator: Byte;
begin

{ Set tag item if supported frame found }
for Iterator := 1 to ID3V2_FRAME_COUNTdo

if ID3V2_FRAME[Iterator] = ID then
Tag.Frame[Iterator] := Data;
end;


{ --------------------------------------------------------------------------- }

function Swap32(const Figure: Integer): Integer;
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;


{ --------------------------------------------------------------------------- }

procedure ReadFrames(const FileName: string;
var Tag: TagInfo);
var
SourceFile: file;
Frame: FrameHeader;
Data: array [1..250] of Char;
DataPosition: Integer;
begin

try
{ Set read-access, open file }
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);
{ Read frame header and check frame ID }
BlockRead(SourceFile, Frame, 10);
if not (Frame.ID[1] in ['A'..'Z']) then
break;
DataPosition := FilePos(SourceFile);
{ Read frame data and set tag item if frame supported }
BlockRead(SourceFile, Data, Swap32(Frame.Size) mod SizeOf(Data));
SetTagItem(Frame.ID, Data, Tag);
Seek(SourceFile, DataPosition + Swap32(Frame.Size));
end;

CloseFile(SourceFile);
except
end;

end;


{ --------------------------------------------------------------------------- }

function ExtractTrack(const TrackString: string): Byte;
var
Index, Value, Code: Integer;
begin

{ Extract track from string }
Index := Pos('/', Trim(TrackString));
if Index = 0 then
Val(Trim(TrackString), Value, Code)
else
Val(Copy(Trim(TrackString), 1, Index - 1), Value, Code);
if Code = 0 then
Result := Value
else
Result := 0;
end;


{ --------------------------------------------------------------------------- }

function ExtractGenre(const GenreString: string): string;
begin

{ Extract genre from string }
Result := Trim(GenreString);
if Pos(')', Result) >
0 then
Delete(Result, 1, LastDelimiter(')', Result));
end;


{ --------------------------------------------------------------------------- }

function RebuildFile(const FileName: string;
NewTagData: TStream): Boolean;
var
Tag: TagInfo;
Source, Destination: TFileStream;
BufferName: string;
begin

{ Rebuild file with old file data and new tag (optional) }
Result := false;
if (not FileExists(FileName)) or (FileSetAttr(FileName, 0) <>
0) then
exit;
if not ReadHeader(FileName, Tag) then
exit;
if (NewTagData = 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 NewTagData <>
nil then
Destination.CopyFrom(NewTagData, 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;


{ --------------------------------------------------------------------------- }

procedure BuildHeader(var Tag: TagInfo);
var
Iterator, TagSize: Integer;
begin

{ Build tag header }
Tag.ID := ID3V2_ID;
Tag.Version := TAG_VERSION_2_3;
Tag.Revision := 0;
Tag.Flags := 0;
TagSize := 0;
for Iterator := 1 to ID3V2_FRAME_COUNTdo

if Tag.Frame[Iterator] <>
'' then

Inc(TagSize, Length(Tag.Frame[Iterator]) + 11);
{ Convert tag size }
Tag.Size[1] := TagSize div $200000;
Tag.Size[2] := TagSize div $4000;
Tag.Size[3] := TagSize div $80;
Tag.Size[4] := TagSize mod $80;
end;


{ ********************** Private functions &
procedures ********************* }

procedure TID3v2.FSetTitle(const NewTitle: string);
begin

{ Set song title }
FTitle := TrimRight(NewTitle);
end;


{ --------------------------------------------------------------------------- }

procedure TID3v2.FSetArtist(const NewArtist: string);
begin

{ Set artist name }
FArtist := TrimRight(NewArtist);
end;


{ --------------------------------------------------------------------------- }

procedure TID3v2.FSetAlbum(const NewAlbum: string);
begin

{ Set album title }
FAlbum := TrimRight(NewAlbum);
end;


{ --------------------------------------------------------------------------- }

procedure TID3v2.FSetTrack(const NewTrack: Byte);
begin

{ Set track number }
FTrack := NewTrack;
end;


{ --------------------------------------------------------------------------- }

procedure TID3v2.FSetYear(const NewYear: string);
begin

{ Set release year }
FYear := TrimRight(NewYear);
end;


{ --------------------------------------------------------------------------- }

procedure TID3v2.FSetGenre(const NewGenre: string);
begin

{ Set genre name }
FGenre := TrimRight(NewGenre);
end;


{ --------------------------------------------------------------------------- }

procedure TID3v2.FSetComment(const NewComment: string);
begin

{ Set comment }
FComment := TrimRight(NewComment);
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;
FYear := '';
FGenre := '';
FComment := '';
end;


{ --------------------------------------------------------------------------- }

function TID3v2.ReadFromFile(const FileName: string): Boolean;
var
Tag: TagInfo;
begin

{ Reset data and load header from file to variable }
ResetData;
Result := ReadHeader(FileName, 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 := GetVersionID(Tag);
FSize := GetTagSize(Tag);
{ Get information from frames if version supported }
if (FVersionID = TAG_VERSION_2_3) and (FSize >
0) then

begin

ReadFrames(FileName, Tag);
{ Fill properties with data from frames }
FTitle := Trim(Tag.Frame[1]);
FArtist := Trim(Tag.Frame[2]);
FAlbum := Trim(Tag.Frame[3]);
FTrack := ExtractTrack(Tag.Frame[4]);
FYear := Trim(Tag.Frame[5]);
FGenre := ExtractGenre(Tag.Frame[6]);
FComment := Trim(Copy(Tag.Frame[7], 5, Length(Tag.Frame[7]) - 4));
end;

end;

end;


{ --------------------------------------------------------------------------- }

function TID3v2.RemoveFromFile(const FileName: string): Boolean;
begin

{ Remove tag from file }
Result := RebuildFile(FileName, nil);
end;


{ --------------------------------------------------------------------------- }

function TID3v2.SaveToFile(const FileName: string): Boolean;
var
NewTagData: TStringStream;
Tag: TagInfo;
Iterator, FrameSize: Integer;
begin

{ Build tag and save to file }
NewTagData := TStringStream.Create('');
{ Prepare tag data }
FillChar(Tag, SizeOf(Tag), 0);
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;
{ Build and write tag header }
BuildHeader(Tag);
NewTagData.Write(Tag, 10);
{ Build and write tag frames }
for Iterator := 1 to ID3V2_FRAME_COUNTdo

if Tag.Frame[Iterator] <>
'' then

begin

NewTagData.WriteString(ID3V2_FRAME[Iterator]);
FrameSize := Swap32(Length(Tag.Frame[Iterator]) + 1);
NewTagData.Write(FrameSize, SizeOf(FrameSize));
NewTagData.WriteString(#0#0#0 + Tag.Frame[Iterator]);
end;

{ Rebuild file with new tag }
Result := RebuildFile(FileName, NewTagData);
NewTagData.Free;
end;


end.

===============================================================================
MPEGaudio.pas单元

{ *************************************************************************** }
{ }
{ Audio Tools Library (Freeware) }
{ Class TMPEGaudio - for manipulating with MPEG audio file information }
{ }
{ Uses: }
{ - Class TID3v1 }
{ - Class TID3v2 }
{ }
{ Copyright (c) 2001,2002 by Jurgen Faul }
{ E-mail: jfaul@gmx.de }
{ http://jfaul.de/atl }
{ }
{ Version 1.7 (4 November 2002) }
{ - Ability to recognize QDesign MPEG audio encoder }
{ - Fixed bug with MPEG Layer II }
{ - Fixed bug with very big files }
{ }
{ Version 1.6 (23 May 2002) }
{ - Improved reading performance (up to 50% faster) }
{ }
{ Version 1.1 (11 September 2001) }
{ - Improved encoder guessing for CBR files }
{ }
{ Version 1.0 (31 August 2001) }
{ - Support for MPEG audio (versions 1, 2, 2.5, layers I, II, III) }
{ - Support for Xing &
FhG VBR }
{ - Ability to guess audio encoder (Xing, FhG, LAME, Blade, GoGo, Shine) }
{ - Class TID3v1: reading &
writing support for ID3v1 tags }
{ - Class TID3v2: reading &
writing support for ID3v2 tags }
{ }
{ *************************************************************************** }

unit MPEGaudio;

interface

uses
Classes, SysUtils, ID3v1, ID3v2;

const
{ Table for bit rates }
MPEG_BIT_RATE: array [0..3, 0..3, 0..15] of Word =
(
{ For MPEG 2.5 }
((0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0),
(0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 0),
(0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 0),
(0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256, 0)),
{ Reserved }
((0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0),
(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0),
(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0),
(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)),
{ For MPEG 2 }
((0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0),
(0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 0),
(0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 0),
(0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256, 0)),
{ For MPEG 1 }
((0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0),
(0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 0),
(0, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384, 0),
(0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448, 0))
);

{ Sample rate codes }
MPEG_SAMPLE_RATE_LEVEL_3 = 0;
{ Level 3 }
MPEG_SAMPLE_RATE_LEVEL_2 = 1;
{ Level 2 }
MPEG_SAMPLE_RATE_LEVEL_1 = 2;
{ Level 1 }
MPEG_SAMPLE_RATE_UNKNOWN = 3;
{ Unknown value }

{ Table for sample rates }
MPEG_SAMPLE_RATE: array [0..3, 0..3] of Word =
(
(11025, 12000, 8000, 0), { For MPEG 2.5 }
(0, 0, 0, 0), { Reserved }
(22050, 24000, 16000, 0), { For MPEG 2 }
(44100, 48000, 32000, 0) { For MPEG 1 }
);

{ VBR header ID for Xing/FhG }
VBR_ID_XING = 'Xing';
{ Xing VBR ID }
VBR_ID_FHG = 'VBRI';
{ FhG VBR ID }

{ MPEG version codes }
MPEG_VERSION_2_5 = 0;
{ MPEG 2.5 }
MPEG_VERSION_UNKNOWN = 1;
{ Unknown version }
MPEG_VERSION_2 = 2;
{ MPEG 2 }
MPEG_VERSION_1 = 3;
{ MPEG 1 }

{ MPEG version names }
MPEG_VERSION: array [0..3] of string =
('MPEG 2.5', 'MPEG ?', 'MPEG 2', 'MPEG 1');

{ MPEG layer codes }
MPEG_LAYER_UNKNOWN = 0;
{ Unknown layer }
MPEG_LAYER_III = 1;
{ Layer III }
MPEG_LAYER_II = 2;
{ Layer II }
MPEG_LAYER_I = 3;
{ Layer I }

{ MPEG layer names }
MPEG_LAYER: array [0..3] of string =
('Layer ?', 'Layer III', 'Layer II', 'Layer I');

{ Channel mode codes }
MPEG_CM_STEREO = 0;
{ Stereo }
MPEG_CM_JOINT_STEREO = 1;
{ Joint Stereo }
MPEG_CM_DUAL_CHANNEL = 2;
{ Dual Channel }
MPEG_CM_MONO = 3;
{ Mono }
MPEG_CM_UNKNOWN = 4;
{ Unknown mode }

{ Channel mode names }
MPEG_CM_MODE: array [0..4] of string =
('Stereo', 'Joint Stereo', 'Dual Channel', 'Mono', 'Unknown');

{ Extension mode codes (for Joint Stereo) }
MPEG_CM_EXTENSION_OFF = 0;
{ IS and MS modes set off }
MPEG_CM_EXTENSION_IS = 1;
{ Only IS mode set on }
MPEG_CM_EXTENSION_MS = 2;
{ Only MS mode set on }
MPEG_CM_EXTENSION_ON = 3;
{ IS and MS modes set on }
MPEG_CM_EXTENSION_UNKNOWN = 4;
{ Unknown extension mode }

{ Emphasis mode codes }
MPEG_EMPHASIS_NONE = 0;
{ None }
MPEG_EMPHASIS_5015 = 1;
{ 50/15 ms }
MPEG_EMPHASIS_UNKNOWN = 2;
{ Unknown emphasis }
MPEG_EMPHASIS_CCIT = 3;
{ CCIT J.17 }

{ Emphasis names }
MPEG_EMPHASIS: array [0..3] of string =
('None', '50/15 ms', 'Unknown', 'CCIT J.17');

{ Encoder codes }
MPEG_ENCODER_UNKNOWN = 0;
{ Unknown encoder }
MPEG_ENCODER_XING = 1;
{ Xing }
MPEG_ENCODER_FHG = 2;
{ FhG }
MPEG_ENCODER_LAME = 3;
{ LAME }
MPEG_ENCODER_BLADE = 4;
{ Blade }
MPEG_ENCODER_GOGO = 5;
{ GoGo }
MPEG_ENCODER_SHINE = 6;
{ Shine }
MPEG_ENCODER_QDESIGN = 7;
{ QDesign }

{ Encoder names }
MPEG_ENCODER: array [0..7] of string =
('Unknown', 'Xing', 'FhG', 'LAME', 'Blade', 'GoGo', 'Shine', 'QDesign');

type
{ Xing/FhG VBR header data }
VBRData = record
Found: Boolean;
{ True if VBR header found }
ID: array [1..4] of Char;
{ Header ID: "Xing"
or "VBRI"
}
Frames: Integer;
{ Total number of frames }
Bytes: Integer;
{ Total number of bytes }
Scale: Byte;
{ VBR scale (1..100) }
VendorID: string;
{ Vendor ID (if present) }
end;


{ MPEG frame header data}
FrameData = record
Found: Boolean;
{ True if frame found }
Position: Integer;
{ Frame position in the file }
Size: Word;
{ Frame size (bytes) }
Xing: Boolean;
{ True if Xing encoder }
Data: array [1..4] of Byte;
{ The whole frame header data }
VersionID: Byte;
{ MPEG version ID }
LayerID: Byte;
{ MPEG layer ID }
ProtectionBit: Boolean;
{ True if protected by CRC }
BitRateID: Word;
{ Bit rate ID }
SampleRateID: Word;
{ Sample rate ID }
PaddingBit: Boolean;
{ True if frame padded }
PrivateBit: Boolean;
{ Extra information }
ModeID: Byte;
{ Channel mode ID }
ModeExtensionID: Byte;
{ Mode extension ID (for Joint Stereo) }
CopyrightBit: Boolean;
{ True if audio copyrighted }
OriginalBit: Boolean;
{ True if original media }
EmphasisID: Byte;
{ Emphasis ID }
end;


{ Class TMPEGaudio }
TMPEGaudio = class(TObject)
private
{ Private declarations }
FFileLength: Integer;
FVendorID: string;
FVBR: VBRData;
FFrame: FrameData;
FID3v1: TID3v1;
FID3v2: TID3v2;
procedure FResetData;
function FGetVersion: string;
function FGetLayer: string;
function FGetBitRate: Word;
function FGetSampleRate: Word;
function FGetChannelMode: string;
function FGetEmphasis: string;
function FGetFrames: Integer;
function FGetDuration:do
uble;
function FGetVBREncoderID: Byte;
function FGetCBREncoderID: Byte;
function FGetEncoderID: Byte;
function FGetEncoder: string;
function FGetValid: Boolean;
public
{ Public declarations }
constructor Create;
{ Create object }
destructor Destroy;
override;
{ Destroy object }
function ReadFromFile(const FileName: string): Boolean;
{ Load data }
property FileLength: Integer read FFileLength;
{ File length (bytes) }
property VBR: VBRData read FVBR;
{ VBR header data }
property Frame: FrameData read FFrame;
{ Frame header data }
property ID3v1: TID3v1 read FID3v1;
{ ID3v1 tag data }
property ID3v2: TID3v2 read FID3v2;
{ ID3v2 tag data }
property Version: string read FGetVersion;
{ MPEG version name }
property Layer: string read FGetLayer;
{ MPEG layer name }
property BitRate: Word read FGetBitRate;
{ Bit rate (kbit/s) }
property SampleRate: Word read FGetSampleRate;
{ Sample rate (hz) }
property ChannelMode: string read FGetChannelMode;
{ Channel mode name }
property Emphasis: string read FGetEmphasis;
{ Emphasis name }
property Frames: Integer read FGetFrames;
{ Total number of frames }
property Duration:do
uble read FGetDuration;
{ Song duration (sec) }
property EncoderID: Byte read FGetEncoderID;
{ Guessed encoder ID }
property Encoder: string read FGetEncoder;
{ Guessed encoder name }
property Valid: Boolean read FGetValid;
{ True if MPEG file valid }
end;


implementation

const
{ Limitation constants }
MAX_MPEG_FRAME_LENGTH = 1729;
{ Max. MPEG frame length }
MIN_MPEG_BIT_RATE = 8;
{ Min. bit rate value }
MAX_MPEG_BIT_RATE = 448;
{ Max. bit rate value }
MIN_ALLOWED_DURATION = 0.1;
{ Min. song duration value }

{ VBR Vendor ID strings }
VENDOR_ID_LAME = 'LAME';
{ For LAME }
VENDOR_ID_GOGO_NEW = 'GOGO';
{ For GoGo (New) }
VENDOR_ID_GOGO_OLD = 'MPGE';
{ For GoGo (Old) }

{ ********************* Auxiliary functions &
procedures ******************** }

function IsFrameHeader(const HeaderData: array of Byte): Boolean;
begin

{ Check for valid frame header }
if ((HeaderData[0] and $FF) <>
$FF) or
((HeaderData[1] and $E0) <>
$E0) or
(((HeaderData[1] shr 3) and 3) = 1) or
(((HeaderData[1] shr 1) and 3) = 0) or
((HeaderData[2] and $F0) = $F0) or
((HeaderData[2] and $F0) = 0) or
(((HeaderData[2] shr 2) and 3) = 3) or
((HeaderData[3] and 3) = 2) then

Result := false
else

Result := true;
end;


{ --------------------------------------------------------------------------- }

procedure DecodeHeader(const HeaderData: array of Byte;
var Frame: FrameData);
begin

{ Decode frame header data }
Move(HeaderData, Frame.Data, SizeOf(Frame.Data));
Frame.VersionID := (HeaderData[1] shr 3) and 3;
Frame.LayerID := (HeaderData[1] shr 1) and 3;
Frame.ProtectionBit := (HeaderData[1] and 1) <>
1;
Frame.BitRateID := HeaderData[2] shr 4;
Frame.SampleRateID := (HeaderData[2] shr 2) and 3;
Frame.PaddingBit := ((HeaderData[2] shr 1) and 1) = 1;
Frame.PrivateBit := (HeaderData[2] and 1) = 1;
Frame.ModeID := (HeaderData[3] shr 6) and 3;
Frame.ModeExtensionID := (HeaderData[3] shr 4) and 3;
Frame.CopyrightBit := ((HeaderData[3] shr 3) and 1) = 1;
Frame.OriginalBit := ((HeaderData[3] shr 2) and 1) = 1;
Frame.EmphasisID := HeaderData[3] and 3;
end;


{ --------------------------------------------------------------------------- }

function ValidFrameAt(const Index: Word;
Data: array of Byte): Boolean;
var
HeaderData: array [1..4] of Byte;
begin

{ Check for frame at given position }
HeaderData[1] := Data[Index];
HeaderData[2] := Data[Index + 1];
HeaderData[3] := Data[Index + 2];
HeaderData[4] := Data[Index + 3];
if IsFrameHeader(HeaderData) then
Result := true
else
Result := false;
end;


{ --------------------------------------------------------------------------- }

function GetCoefficient(const Frame: FrameData): Byte;
begin

{ Get frame size coefficient }
if Frame.VersionID = MPEG_VERSION_1 then

if Frame.LayerID = MPEG_LAYER_I then
Result := 48
else
Result := 144
else

if Frame.LayerID = MPEG_LAYER_I then
Result := 24
else
if Frame.LayerID = MPEG_LAYER_II then
Result := 144
else
Result := 72;
end;


{ --------------------------------------------------------------------------- }

function GetBitRate(const Frame: FrameData): Word;
begin

{ Get bit rate }
Result := MPEG_BIT_RATE[Frame.VersionID, Frame.LayerID, Frame.BitRateID];
end;


{ --------------------------------------------------------------------------- }

function GetSampleRate(const Frame: FrameData): Word;
begin

{ Get sample rate }
Result := MPEG_SAMPLE_RATE[Frame.VersionID, Frame.SampleRateID];
end;


{ --------------------------------------------------------------------------- }

function GetPadding(const Frame: FrameData): Byte;
begin

{ Get frame padding }
if Frame.PaddingBit then

if Frame.LayerID = MPEG_LAYER_I then
Result := 4
else
Result := 1
else
Result := 0;
end;


{ --------------------------------------------------------------------------- }

function GetFrameLength(const Frame: FrameData): Word;
var
Coefficient, BitRate, SampleRate, Padding: Word;
begin

{ Calculate MPEG frame length }
Coefficient := GetCoefficient(Frame);
BitRate := GetBitRate(Frame);
SampleRate := GetSampleRate(Frame);
Padding := GetPadding(Frame);
Result := Trunc(Coefficient * BitRate * 1000 / SampleRate) + Padding;
end;


{ --------------------------------------------------------------------------- }

function IsXing(const Index: Word;
Data: array of Byte): Boolean;
begin

{ Get true if Xing encoder }
Result :=
(Data[Index] = 0) and
(Data[Index + 1] = 0) and
(Data[Index + 2] = 0) and
(Data[Index + 3] = 0) and
(Data[Index + 4] = 0) and
(Data[Index + 5] = 0);
end;


{ --------------------------------------------------------------------------- }

function GetXingInfo(const Index: Word;
Data: array of Byte): VBRData;
begin

{ Extract Xing VBR info at given position }
FillChar(Result, SizeOf(Result), 0);
Result.Found := true;
Result.ID := VBR_ID_XING;
Result.Frames :=
Data[Index + 8] * $1000000 +
Data[Index + 9] * $10000 +
Data[Index + 10] * $100 +
Data[Index + 11];
Result.Bytes :=
Data[Index + 12] * $1000000 +
Data[Index + 13] * $10000 +
Data[Index + 14] * $100 +
Data[Index + 15];
Result.Scale := Data[Index + 119];
{ Vendor ID can be not present }
Result.VendorID :=
Chr(Data[Index + 120]) +
Chr(Data[Index + 121]) +
Chr(Data[Index + 122]) +
Chr(Data[Index + 123]) +
Chr(Data[Index + 124]) +
Chr(Data[Index + 125]) +
Chr(Data[Index + 126]) +
Chr(Data[Index + 127]);
end;


{ --------------------------------------------------------------------------- }

function GetFhGInfo(const Index: Word;
Data: array of Byte): VBRData;
begin

{ Extract FhG VBR info at given position }
FillChar(Result, SizeOf(Result), 0);
Result.Found := true;
Result.ID := VBR_ID_FHG;
Result.Scale := Data[Index + 9];
Result.Bytes :=
Data[Index + 10] * $1000000 +
Data[Index + 11] * $10000 +
Data[Index + 12] * $100 +
Data[Index + 13];
Result.Frames :=
Data[Index + 14] * $1000000 +
Data[Index + 15] * $10000 +
Data[Index + 16] * $100 +
Data[Index + 17];
end;


{ --------------------------------------------------------------------------- }

function FindVBR(const Index: Word;
Data: array of Byte): VBRData;
begin

{ Check for VBR header at given position }
FillChar(Result, SizeOf(Result), 0);
if Chr(Data[Index]) +
Chr(Data[Index + 1]) +
Chr(Data[Index + 2]) +
Chr(Data[Index + 3]) = VBR_ID_XING then
Result := GetXingInfo(Index, Data);
if Chr(Data[Index]) +
Chr(Data[Index + 1]) +
Chr(Data[Index + 2]) +
Chr(Data[Index + 3]) = VBR_ID_FHG then
Result := GetFhGInfo(Index, Data);
end;


{ --------------------------------------------------------------------------- }

function GetVBRDeviation(const Frame: FrameData): Byte;
begin

{ Calculate VBR deviation }
if Frame.VersionID = MPEG_VERSION_1 then

if Frame.ModeID <>
MPEG_CM_MONO then
Result := 36
else
Result := 21
else

if Frame.ModeID <>
MPEG_CM_MONO then
Result := 21
else
Result := 13;
end;


{ --------------------------------------------------------------------------- }

function FindFrame(const Data: array of Byte;
var VBR: VBRData): FrameData;
var
HeaderData: array [1..4] of Byte;
Iterator: Integer;
begin

{ Search for valid frame }
FillChar(Result, SizeOf(Result), 0);
Move(Data, HeaderData, SizeOf(HeaderData));
for Iterator := 0 to SizeOf(Data) - MAX_MPEG_FRAME_LENGTHdo

begin

{ Decode data if frame header found }
if IsFrameHeader(HeaderData) then

begin

DecodeHeader(HeaderData, Result);
{ Check for next frame and try to find VBR header }
if ValidFrameAt(Iterator + GetFrameLength(Result), Data) then

begin

Result.Found := true;
Result.Position := Iterator;
Result.Size := GetFrameLength(Result);
Result.Xing := IsXing(Iterator + SizeOf(HeaderData), Data);
VBR := FindVBR(Iterator + GetVBRDeviation(Result), Data);
break;
end;

end;

{ Prepare next data block }
HeaderData[1] := HeaderData[2];
HeaderData[2] := HeaderData[3];
HeaderData[3] := HeaderData[4];
HeaderData[4] := Data[Iterator + SizeOf(HeaderData)];
end;

end;


{ --------------------------------------------------------------------------- }

function FindVendorID(const Data: array of Byte;
Size: Word): string;
var
Iterator: Integer;
VendorID: string;
begin

{ Search for vendor ID }
Result := '';
if (SizeOf(Data) - Size - 8) <
0 then
Size := SizeOf(Data) - 8;
for Iterator := 0 to Sizedo

begin

VendorID :=
Chr(Data[SizeOf(Data) - Iterator - 8]) +
Chr(Data[SizeOf(Data) - Iterator - 7]) +
Chr(Data[SizeOf(Data) - Iterator - 6]) +
Chr(Data[SizeOf(Data) - Iterator - 5]);
if VendorID = VENDOR_ID_LAME then

begin

Result := VendorID +
Chr(Data[SizeOf(Data) - Iterator - 4]) +
Chr(Data[SizeOf(Data) - Iterator - 3]) +
Chr(Data[SizeOf(Data) - Iterator - 2]) +
Chr(Data[SizeOf(Data) - Iterator - 1]);
break;
end;

if VendorID = VENDOR_ID_GOGO_NEW then

begin

Result := VendorID;
break;
end;

end;

end;


{ ********************** Private functions &
procedures ********************* }

procedure TMPEGaudio.FResetData;
begin

{ Reset all variables }
FFileLength := 0;
FVendorID := '';
FillChar(FVBR, SizeOf(FVBR), 0);
FillChar(FFrame, SizeOf(FFrame), 0);
FFrame.VersionID := MPEG_VERSION_UNKNOWN;
FFrame.SampleRateID := MPEG_SAMPLE_RATE_UNKNOWN;
FFrame.ModeID := MPEG_CM_UNKNOWN;
FFrame.ModeExtensionID := MPEG_CM_EXTENSION_UNKNOWN;
FFrame.EmphasisID := MPEG_EMPHASIS_UNKNOWN;
FID3v1.ResetData;
FID3v2.ResetData;
end;


{ --------------------------------------------------------------------------- }

function TMPEGaudio.FGetVersion: string;
begin

{ Get MPEG version name }
Result := MPEG_VERSION[FFrame.VersionID];
end;


{ --------------------------------------------------------------------------- }

function TMPEGaudio.FGetLayer: string;
begin

{ Get MPEG layer name }
Result := MPEG_LAYER[FFrame.LayerID];
end;


{ --------------------------------------------------------------------------- }

function TMPEGaudio.FGetBitRate: Word;
begin

{ Get bit rate, calculate average bit rate if VBR header found }
if (FVBR.Found) and (FVBR.Frames >
0) then

Result := Round((FVBR.Bytes / FVBR.Frames - GetPadding(FFrame)) *
GetSampleRate(FFrame) / GetCoefficient(FFrame) / 1000)
else

Result := GetBitRate(FFrame);
end;


{ --------------------------------------------------------------------------- }

function TMPEGaudio.FGetSampleRate: Word;
begin

{ Get sample rate }
Result := GetSampleRate(FFrame);
end;


{ --------------------------------------------------------------------------- }

function TMPEGaudio.FGetChannelMode: string;
begin

{ Get channel mode name }
Result := MPEG_CM_MODE[FFrame.ModeID];
end;


{ --------------------------------------------------------------------------- }

function TMPEGaudio.FGetEmphasis: string;
begin

{ Get emphasis name }
Result := MPEG_EMPHASIS[FFrame.EmphasisID];
end;


{ --------------------------------------------------------------------------- }

function TMPEGaudio.FGetFrames: Integer;
var
MPEGSize: Integer;
begin

{ Get total number of frames, calculate if VBR header not found }
if FVBR.Found then

Result := FVBR.Frames
else

begin

if FID3v1.Exists then
MPEGSize := FFileLength - FID3v2.Size - 128
else
MPEGSize := FFileLength - FID3v2.Size;
Result := (MPEGSize - FFrame.Position) div GetFrameLength(FFrame);
end;

end;


{ --------------------------------------------------------------------------- }

function TMPEGaudio.FGetDuration:do
uble;
var
MPEGSize: Integer;
begin

{ Calculate song duration }
if FFrame.Found then

if (FVBR.Found) and (FVBR.Frames >
0) then

Result := FVBR.Frames * GetCoefficient(FFrame) * 8 /
GetSampleRate(FFrame)
else

begin

if FID3v1.Exists then
MPEGSize := FFileLength - FID3v2.Size - 128
else
MPEGSize := FFileLength - FID3v2.Size;
Result := (MPEGSize - FFrame.Position) / GetBitRate(FFrame) / 1000 * 8;
end
else

Result := 0;
end;


{ --------------------------------------------------------------------------- }

function TMPEGaudio.FGetVBREncoderID: Byte;
begin

{ Guess VBR encoder and get ID }
Result := 0;
if Copy(FVBR.VendorID, 1, 4) = VENDOR_ID_LAME then

Result := MPEG_ENCODER_LAME;
if Copy(FVBR.VendorID, 1, 4) = VENDOR_ID_GOGO_NEW then

Result := MPEG_ENCODER_GOGO;
if Copy(FVBR.VendorID, 1, 4) = VENDOR_ID_GOGO_OLD then

Result := MPEG_ENCODER_GOGO;
if (FVBR.ID = VBR_ID_XING) and
(Copy(FVBR.VendorID, 1, 4) <>
VENDOR_ID_LAME) and
(Copy(FVBR.VendorID, 1, 4) <>
VENDOR_ID_GOGO_NEW) and
(Copy(FVBR.VendorID, 1, 4) <>
VENDOR_ID_GOGO_OLD) then

Result := MPEG_ENCODER_XING;
if FVBR.ID = VBR_ID_FHG then

Result := MPEG_ENCODER_FHG;
end;


{ --------------------------------------------------------------------------- }

function TMPEGaudio.FGetCBREncoderID: Byte;
begin

{ Guess CBR encoder and get ID }
Result := MPEG_ENCODER_FHG;
if (FFrame.OriginalBit) and
(FFrame.ProtectionBit) then

Result := MPEG_ENCODER_LAME;
if (GetBitRate(FFrame) <= 160) and
(FFrame.ModeID = MPEG_CM_STEREO) then

Result := MPEG_ENCODER_BLADE;
if (FFrame.CopyrightBit) and
(FFrame.OriginalBit) and
(not FFrame.ProtectionBit) then

Result := MPEG_ENCODER_XING;
if (FFrame.Xing) and
(FFrame.OriginalBit) then

Result := MPEG_ENCODER_XING;
if FFrame.LayerID = MPEG_LAYER_II then

Result := MPEG_ENCODER_QDESIGN;
if (FFrame.ModeID = MPEG_CM_DUAL_CHANNEL) and
(FFrame.ProtectionBit) then

Result := MPEG_ENCODER_SHINE;
if Copy(FVendorID, 1, 4) = VENDOR_ID_LAME then

Result := MPEG_ENCODER_LAME;
if Copy(FVendorID, 1, 4) = VENDOR_ID_GOGO_NEW then

Result := MPEG_ENCODER_GOGO;
end;


{ --------------------------------------------------------------------------- }

function TMPEGaudio.FGetEncoderID: Byte;
begin

{ Get guessed encoder ID }
if FFrame.Found then

if FVBR.Found then
Result := FGetVBREncoderID
else
Result := FGetCBREncoderID
else

Result := 0;
end;


{ --------------------------------------------------------------------------- }

function TMPEGaudio.FGetEncoder: string;
var
VendorID: string;
begin

{ Get guessed encoder name and encoder version for LAME }
Result := MPEG_ENCODER[FGetEncoderID];
if FVBR.VendorID <>
'' then
VendorID := FVBR.VendorID;
if FVendorID <>
'' then
VendorID := FVendorID;
if (FGetEncoderID = MPEG_ENCODER_LAME) and
(Length(VendorID) >= 8) and
(VendorID[5] in ['0'..'9']) and
(VendorID[6] = '.') and
(VendorID[7] in ['0'..'9']) and
(VendorID[8] in ['0'..'9']) then

Result :=
Result + #32 +
VendorID[5] +
VendorID[6] +
VendorID[7] +
VendorID[8];
end;


{ --------------------------------------------------------------------------- }

function TMPEGaudio.FGetValid: Boolean;
begin

{ Check for right MPEG file data }
Result :=
(FFrame.Found) and
(FGetBitRate >= MIN_MPEG_BIT_RATE) and
(FGetBitRate <= MAX_MPEG_BIT_RATE) and
(FGetDuration >= MIN_ALLOWED_DURATION);
end;


{ ********************** Public functions &
procedures ********************** }

constructor TMPEGaudio.Create;
begin

{ Object constructor }
inherited;
FID3v1 := TID3v1.Create;
FID3v2 := TID3v2.Create;
FResetData;
end;


{ --------------------------------------------------------------------------- }

destructor TMPEGaudio.Destroy;
begin

{ Object destructor }
FID3v1.Free;
FID3v2.Free;
inherited;
end;


{ --------------------------------------------------------------------------- }

function TMPEGaudio.ReadFromFile(const FileName: string): Boolean;
var
SourceFile: file;
Data: array [1..MAX_MPEG_FRAME_LENGTH * 2] of Byte;
Transferred: Integer;
begin

Result := false;
FResetData;
{ At first search for tags, then
search for a MPEG frame and VBR data }
if (FID3v2.ReadFromFile(FileName)) and (FID3v1.ReadFromFile(FileName)) then

try
{ Open file, read first block of data and search for a frame }
AssignFile(SourceFile, FileName);
FileMode := 0;
Reset(SourceFile, 1);
FFileLength := FileSize(SourceFile);
Seek(SourceFile, FID3v2.Size);
BlockRead(SourceFile, Data, SizeOf(Data), Transferred);
FFrame := FindFrame(Data, FVBR);
{ Try to search in the middle if no frame at the begin
ning found }
if (not FFrame.Found) and (Transferred = SizeOf(Data)) then

begin

Seek(SourceFile, (FFileLength - FID3v2.Size) div 2);
BlockRead(SourceFile, Data, SizeOf(Data), Transferred);
FFrame := FindFrame(Data, FVBR);
end;

{ Search for vendor ID at the end if CBR encoded }
if (FFrame.Found) and (not FVBR.Found) then

begin

if not FID3v1.Exists then
Seek(SourceFile, FFileLength - SizeOf(Data))
else
Seek(SourceFile, FFileLength - SizeOf(Data) - 128);
BlockRead(SourceFile, Data, SizeOf(Data), Transferred);
FVendorID := FindVendorID(Data, FFrame.Size * 5);
end;

CloseFile(SourceFile);
Result := true;
except
end;

if not FFrame.Found then
FResetData;
end;


end.



 
上面是我用于获取MP3信息的ID3v1.pas , ID3v2.pas ,MPEGaudio.pas单元,希望对你有帮助

 
你要的在 mpeg 那个单元里
时间是对 一个4字节的头标记的分析
mp3对格式的说明很精练的
 
多人接受答案了。
 

Similar threads

S
回复
0
查看
3K
SUNSTONE的Delphi笔记
S
S
回复
0
查看
2K
SUNSTONE的Delphi笔记
S
顶部