WMAFILE.PAS
{********************************************************** *************************} {} {Audio Tools Library (freeware)} {Class TwMafile - for Extracting Information from WMA file header} { } {CopyRight (C) 2001, 2002 by jurgen faul} {e-mail: jfaul@gmx.de} {http://jfaul.de/atl} {} {version 1.0 (29 April 2002)} {- File Info : File Size, Channel Mode, Sample Rate, Duration, Bit Rate} {- WMA Tag Info: Title, Artist, Album, Track, Year, Genre, Comment} } {********************************************************** ***************************
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; importation
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 = # 140 # 220 # 171 # 140 # 220 # 171 # 140 # 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_DESBRIPTION_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 commertes} wma_field_count = 7;
{Names of support_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)}}}} {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: = ''; = WMA_MAX_STRING_SIZE; Source.ReadBuffer (FieldData, StringSize * 2); Source.Seek (DataSize - StringSize * 2, soFromCurrent); for Iterator::;: StringSize if StringSize> WMA_MAX_STRING_SIZE then StringSize = DataSize 2 div = 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;
{------------------------------------- --------------------------}
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): 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.byterete> 0) and (data.byterate <40000);
{------------------------------------- --------------------------}
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; ford: =' '; fgenre: ='; fcomment: = ''; end;
{------------------------------------- --------------------------}
Function TWMAFILE.FGETCHANNELMODE: STRING; Begin {Get Channel Mode Name} Result: = WMA_MODE [FCHANNELMODEID];
{**********************************************}
Constructor twmafile.create; begin {create object} inherited; FRETDATA;
{------------------------------------- --------------------------}
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.bytereate * 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.