首页  编辑  

WMV的歌曲信息

Tags: /超级猛料/Multi-Media.多媒体相关/   Date Created:

unit WMAfile;

interface

uses

 Classes, SysUtils;

const

 { Channel modes }

 WMA_CM_UNKNOWN = 0;                                               { Unknown }

 WMA_CM_MONO = 1;                                                     { Mono }

 WMA_CM_STEREO = 2;                                                 { Stereo }

 { Channel mode names }

 WMA_MODE: array [0..2] of string = ('Unknown', 'Mono', 'Stereo');

type

 { Class TWMAfile }

 TWMAfile = class(TObject)

   private

     { Private declarations }

     FValid: Boolean;

     FFileSize: Integer;

     FChannelModeID: Byte;

     FSampleRate: Integer;

     FDuration: Double;

     FBitRate: Integer;

     FTitle: WideString;

     FArtist: WideString;

     FAlbum: WideString;

     FTrack: Integer;

     FYear: WideString;

     FGenre: WideString;

     FComment: WideString;

     procedure FResetData;

     function FGetChannelMode: string;

   public

     { Public declarations }

     constructor Create;                                     { Create object }

     function ReadFromFile(const FileName: string): Boolean;     { Load data }

     property Valid: Boolean read FValid;               { True if valid data }

     property FileSize: Integer read FFileSize;          { File size (bytes) }

     property ChannelModeID: Byte read FChannelModeID;   { Channel mode code }

     property ChannelMode: string read FGetChannelMode;  { Channel mode name }

     property SampleRate: Integer read FSampleRate;       { Sample rate (hz) }

     property Duration: Double read FDuration;          { Duration (seconds) }

     property BitRate: Integer read FBitRate;              { Bit rate (kbit) }

     property Title: WideString read FTitle;                    { Song title }

     property Artist: WideString read FArtist;                 { Artist name }

     property Album: WideString read FAlbum;                    { Album name }

     property Track: Integer read FTrack;                     { Track number }

     property Year: WideString read FYear;                            { Year }

     property Genre: WideString read FGenre;                    { Genre name }

     property Comment: WideString read FComment;                   { Comment }

 end;

implementation

const

 { Object IDs }

 WMA_HEADER_ID =

   #48#38#178#117#142#102#207#17#166#217#0#170#0#98#206#108;

 WMA_FILE_PROPERTIES_ID =

   #161#220#171#140#71#169#207#17#142#228#0#192#12#32#83#101;

 WMA_STREAM_PROPERTIES_ID =

   #145#7#220#183#183#169#207#17#142#230#0#192#12#32#83#101;

 WMA_CONTENT_DESCRIPTION_ID =

   #51#38#178#117#142#102#207#17#166#217#0#170#0#98#206#108;

 WMA_EXTENDED_CONTENT_DESCRIPTION_ID =

   #64#164#208#210#7#227#210#17#151#240#0#160#201#94#168#80;

 { Max. number of supported comment fields }

 WMA_FIELD_COUNT = 7;

 { Names of supported comment fields }

 WMA_FIELD_NAME: array [1..WMA_FIELD_COUNT] of WideString =

   ('WM/TITLE', 'WM/AUTHOR', 'WM/ALBUMTITLE', 'WM/TRACK', 'WM/YEAR',

    'WM/GENRE', 'WM/DESCRIPTION');

 { Max. number of characters in tag field }

 WMA_MAX_STRING_SIZE = 250;

type

 { Object ID }

 ObjectID = array [1..16] of Char;

 { Tag data }

 TagData = array [1..WMA_FIELD_COUNT] of WideString;

 { File data - for internal use }

 FileData = record

   FileSize: Integer;                                    { File size (bytes) }

   MaxBitRate: Integer;                                { Max. bit rate (bps) }

   Channels: Word;                                      { Number of channels }

   SampleRate: Integer;                                   { Sample rate (hz) }

   ByteRate: Integer;                                            { Byte rate }

   Tag: TagData;                                       { WMA tag information }

 end;

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

function ReadFieldString(const Source: TStream; DataSize: Word): WideString;

var

 Iterator, StringSize: Integer;

 FieldData: array [1..WMA_MAX_STRING_SIZE * 2] of Byte;

begin

 { Read field data and convert to Unicode string }

 Result := '';

 StringSize := DataSize div 2;

 if StringSize > WMA_MAX_STRING_SIZE then StringSize := WMA_MAX_STRING_SIZE;

 Source.ReadBuffer(FieldData, StringSize * 2);

 Source.Seek(DataSize - StringSize * 2, soFromCurrent);

 for Iterator := 1 to StringSize do

   Result := Result +

     WideChar(FieldData[Iterator * 2 - 1] + (FieldData[Iterator * 2] shl 8));

end;

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

procedure ReadTagStandard(const Source: TStream; var Tag: TagData);

var

 Iterator: Integer;

 FieldSize: array [1..5] of Word;

 FieldValue: WideString;

begin

 { Read standard tag data }

 Source.ReadBuffer(FieldSize, SizeOf(FieldSize));

 for Iterator := 1 to 5 do

   if FieldSize[Iterator] > 0 then

   begin

     { Read field value }

     FieldValue := ReadFieldString(Source, FieldSize[Iterator]);

     { Set corresponding tag field if supported }

     case Iterator of

       1: Tag[1] := FieldValue;

       2: Tag[2] := FieldValue;

       4: Tag[7] := FieldValue;

     end;

   end;

end;

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

procedure ReadTagExtended(const Source: TStream; var Tag: TagData);

var

 Iterator1, Iterator2, FieldCount, DataSize, DataType: Word;

 FieldName, FieldValue: WideString;

begin

 { Read extended tag data }

 Source.ReadBuffer(FieldCount, SizeOf(FieldCount));

 for Iterator1 := 1 to FieldCount do

 begin

   { Read field name }

   Source.ReadBuffer(DataSize, SizeOf(DataSize));

   FieldName := ReadFieldString(Source, DataSize);

   { Read value data type }

   Source.ReadBuffer(DataType, SizeOf(DataType));

   { Read field value only if string }

   if DataType = 0 then

   begin

     Source.ReadBuffer(DataSize, SizeOf(DataSize));

     FieldValue := ReadFieldString(Source, DataSize);

   end

   else

     Source.Seek(DataSize, soFromCurrent);

   { Set corresponding tag field if supported }

   for Iterator2 := 1 to WMA_FIELD_COUNT do

     if UpperCase(Trim(FieldName)) = WMA_FIELD_NAME[Iterator2] then

       Tag[Iterator2] := FieldValue;

 end;

end;

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

procedure ReadObject(const ID: ObjectID; Source: TStream; var Data: FileData);

begin

 { Read data from header object if supported }

 if ID = WMA_FILE_PROPERTIES_ID then

 begin

   { Read file properties }

   Source.Seek(80, soFromCurrent);

   Source.ReadBuffer(Data.MaxBitRate, SizeOf(Data.MaxBitRate));

 end;

 if ID = WMA_STREAM_PROPERTIES_ID then

 begin

   { Read stream properties }

   Source.Seek(60, soFromCurrent);

   Source.ReadBuffer(Data.Channels, SizeOf(Data.Channels));

   Source.ReadBuffer(Data.SampleRate, SizeOf(Data.SampleRate));

   Source.ReadBuffer(Data.ByteRate, SizeOf(Data.ByteRate));

 end;

 if ID = WMA_CONTENT_DESCRIPTION_ID then

 begin

   { Read standard tag data }

   Source.Seek(4, soFromCurrent);

   ReadTagStandard(Source, Data.Tag);

 end;

 if ID = WMA_EXTENDED_CONTENT_DESCRIPTION_ID then

 begin

   { Read extended tag data }

   Source.Seek(4, soFromCurrent);

   ReadTagExtended(Source, Data.Tag);

 end;

end;

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

function ReadData(const FileName: string; var Data: FileData): Boolean;

var

 Source: TFileStream;

 ID: ObjectID;

 Iterator, ObjectCount, ObjectSize, Position: Integer;

begin

 { Read file data }

 try

   Source := TFileStream.Create(FileName, fmOpenRead or fmShareDenyWrite);

   Data.FileSize := Source.Size;

   { Check for existing header }

   Source.ReadBuffer(ID, SizeOf(ID));

   if ID = WMA_HEADER_ID then

   begin

     Source.Seek(8, soFromCurrent);

     Source.ReadBuffer(ObjectCount, SizeOf(ObjectCount));

     Source.Seek(2, soFromCurrent);

     { Read all objects in header and get needed data }

     for Iterator := 1 to ObjectCount do

     begin

       Position := Source.Position;

       Source.ReadBuffer(ID, SizeOf(ID));

       Source.ReadBuffer(ObjectSize, SizeOf(ObjectSize));

       ReadObject(ID, Source, Data);

       Source.Seek(Position + ObjectSize, soFromBeginning);

     end;

   end;

   Source.Free;

   Result := true;

 except

   Result := false;

 end;

end;

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

function IsValid(const Data: FileData): Boolean;

begin

 { Check for data validity }

 Result :=

   (Data.MaxBitRate > 0) and (Data.MaxBitRate < 320000) and

   ((Data.Channels = WMA_CM_MONO) or (Data.Channels = WMA_CM_STEREO)) and

   (Data.SampleRate >= 8000) and (Data.SampleRate <= 96000) and

   (Data.ByteRate > 0) and (Data.ByteRate < 40000);

end;

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

function ExtractTrack(const TrackString: WideString): Integer;

var

 Value, Code: Integer;

begin

 { Extract track from string }

 Result := 0;

 Val(TrackString, Value, Code);

 if Code = 0 then Result := Value;

end;

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

procedure TWMAfile.FResetData;

begin

 { Reset variables }

 FValid := false;

 FFileSize := 0;

 FChannelModeID := WMA_CM_UNKNOWN;

 FSampleRate := 0;

 FDuration := 0;

 FBitRate := 0;

 FTitle := '';

 FArtist := '';

 FAlbum := '';

 FTrack := 0;

 FYear := '';

 FGenre := '';

 FComment := '';

end;

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

function TWMAfile.FGetChannelMode: string;

begin

 { Get channel mode name }

 Result := WMA_MODE[FChannelModeID];

end;

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

constructor TWMAfile.Create;

begin

 { Create object }

 inherited;

 FResetData;

end;

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

function TWMAfile.ReadFromFile(const FileName: string): Boolean;

var

 Data: FileData;

begin

 { Reset variables and load file data }

 FResetData;

 FillChar(Data, SizeOf(Data), 0);

 Result := ReadData(FileName, Data);

 { Process data if loaded and valid }

 if Result and IsValid(Data) then

 begin

   FValid := true;

   { Fill properties with loaded data }

   FFileSize := Data.FileSize;

   FChannelModeID := Data.Channels;

   FSampleRate := Data.SampleRate;

   FDuration := Data.FileSize * 8 / Data.MaxBitRate;

   FBitRate := Data.ByteRate * 8 div 1000;

   FTitle := Trim(Data.Tag[1]);

   FArtist := Trim(Data.Tag[2]);

   FAlbum := Trim(Data.Tag[3]);

   FTrack := ExtractTrack(Trim(Data.Tag[4]));

   FYear := Trim(Data.Tag[5]);

   FGenre := Trim(Data.Tag[6]);

   FComment := Trim(Data.Tag[7]);

 end;

end;

end.