WMA:<br><br>------------------------------<br>unit WMAfile;<br><br>interface<br><br>uses<br> Classes, SysUtils;<br><br>const<br> { Channel modes }<br> WMA_CM_UNKNOWN = 0; { Unknown }<br> WMA_CM_MONO = 1; { Mono }<br> WMA_CM_STEREO = 2; { Stereo }<br><br> { Channel mode names }<br> WMA_MODE: array [0..2] of string = ('Unknown', 'Mono', 'Stereo');<br><br>type<br> { Class TWMAfile }<br> TWMAfile = class(TObject)<br> private<br> { Private declarations }<br> FValid: Boolean;<br> FFileSize: Integer;<br> FChannelModeID: Byte;<br> FSampleRate: Integer;<br> FDuration: Double;<br> FBitRate: Integer;<br> FTitle: WideString;<br> FArtist: WideString;<br> FAlbum: WideString;<br> FTrack: Integer;<br> FYear: WideString;<br> FGenre: WideString;<br> FComment: WideString;<br> procedure FResetData;<br> function FGetChannelMode: string;<br> public<br> { Public declarations }<br> constructor Create; { Create object }<br> function ReadFromFile(const FileName: string): Boolean; { Load data }<br> property Valid: Boolean read FValid; { True if valid data }<br> property FileSize: Integer read FFileSize; { File size (bytes) }<br> property ChannelModeID: Byte read FChannelModeID; { Channel mode code }<br> property ChannelMode: string read FGetChannelMode; { Channel mode name }<br> property SampleRate: Integer read FSampleRate; { Sample rate (hz) }<br> property Duration: Double read FDuration; { Duration (seconds) }<br> property BitRate: Integer read FBitRate; { Bit rate (kbit) }<br> property Title: WideString read FTitle; { Song title }<br> property Artist: WideString read FArtist; { Artist name }<br> property Album: WideString read FAlbum; { Album name }<br> property Track: Integer read FTrack; { Track number }<br> property Year: WideString read FYear; { Year }<br> property Genre: WideString read FGenre; { Genre name }<br> property Comment: WideString read FComment; { Comment }<br> end;<br><br>implementation<br><br>const<br> { Object IDs }<br> WMA_HEADER_ID =<br> #48#38#178#117#142#102#207#17#166#217#0#170#0#98#206#108;<br> WMA_FILE_PROPERTIES_ID =<br> #161#220#171#140#71#169#207#17#142#228#0#192#12#32#83#101;<br> WMA_STREAM_PROPERTIES_ID =<br> #145#7#220#183#183#169#207#17#142#230#0#192#12#32#83#101;<br> WMA_CONTENT_DESCRIPTION_ID =<br> #51#38#178#117#142#102#207#17#166#217#0#170#0#98#206#108;<br> WMA_EXTENDED_CONTENT_DESCRIPTION_ID =<br> #64#164#208#210#7#227#210#17#151#240#0#160#201#94#168#80;<br><br> { Max. number of supported comment fields }<br> WMA_FIELD_COUNT = 7;<br><br> { Names of supported comment fields }<br> WMA_FIELD_NAME: array [1..WMA_FIELD_COUNT] of WideString =<br> ('WM/TITLE', 'WM/AUTHOR', 'WM/ALBUMTITLE', 'WM/TRACK', 'WM/YEAR',<br> 'WM/GENRE', 'WM/DESCRIPTION');<br><br> { Max. number of characters in tag field }<br> WMA_MAX_STRING_SIZE = 250;<br><br>type<br> { Object ID }<br> ObjectID = array [1..16] of Char;<br><br> { Tag data }<br> TagData = array [1..WMA_FIELD_COUNT] of WideString;<br><br> { File data - for internal use }<br> FileData = record<br> FileSize: Integer; { File size (bytes) }<br> MaxBitRate: Integer; { Max. bit rate (bps) }<br> Channels: Word; { Number of channels }<br> SampleRate: Integer; { Sample rate (hz) }<br> ByteRate: Integer; { Byte rate }<br> Tag: TagData; { WMA tag information }<br> end;<br><br>{ ********************* Auxiliary functions & procedures ******************** }<br><br>function ReadFieldString(const Source: TStream; DataSize: Word): WideString;<br>var<br> Iterator, StringSize: Integer;<br> FieldData: array [1..WMA_MAX_STRING_SIZE * 2] of Byte;<br>begin<br> { Read field data and convert to Unicode string }<br> Result := '';<br> StringSize := DataSize div 2;<br> if StringSize > WMA_MAX_STRING_SIZE then StringSize := WMA_MAX_STRING_SIZE;<br> Source.ReadBuffer(FieldData, StringSize * 2);<br> Source.Seek(DataSize - StringSize * 2, soFromCurrent);<br> for Iterator := 1 to StringSize do<br> Result := Result +<br> WideChar(FieldData[Iterator * 2 - 1] + (FieldData[Iterator * 2] shl 8));<br>end;<br><br>{ --------------------------------------------------------------------------- }<br><br>procedure ReadTagStandard(const Source: TStream; var Tag: TagData);<br>var<br> Iterator: Integer;<br> FieldSize: array [1..5] of Word;<br> FieldValue: WideString;<br>begin<br> { Read standard tag data }<br> Source.ReadBuffer(FieldSize, SizeOf(FieldSize));<br> for Iterator := 1 to 5 do<br> if FieldSize[Iterator] > 0 then<br> begin<br> { Read field value }<br> FieldValue := ReadFieldString(Source, FieldSize[Iterator]);<br> { Set corresponding tag field if supported }<br> case Iterator of<br> 1: Tag[1] := FieldValue;<br> 2: Tag[2] := FieldValue;<br> 4: Tag[7] := FieldValue;<br> end;<br> end;<br>end;<br><br>{ --------------------------------------------------------------------------- }<br><br>procedure ReadTagExtended(const Source: TStream; var Tag: TagData);<br>var<br> Iterator1, Iterator2, FieldCount, DataSize, DataType: Word;<br> FieldName, FieldValue: WideString;<br>begin<br> { Read extended tag data }<br> Source.ReadBuffer(FieldCount, SizeOf(FieldCount));<br> for Iterator1 := 1 to FieldCount do<br> begin<br> { Read field name }<br> Source.ReadBuffer(DataSize, SizeOf(DataSize));<br> FieldName := ReadFieldString(Source, DataSize);<br> { Read value data type }<br> Source.ReadBuffer(DataType, SizeOf(DataType));<br> { Read field value only if string }<br> if DataType = 0 then<br> begin<br> Source.ReadBuffer(DataSize, SizeOf(DataSize));<br> FieldValue := ReadFieldString(Source, DataSize);<br> end<br> else<br> Source.Seek(DataSize, soFromCurrent);<br> { Set corresponding tag field if supported }<br> for Iterator2 := 1 to WMA_FIELD_COUNT do<br> if UpperCase(Trim(FieldName)) = WMA_FIELD_NAME[Iterator2] then<br> Tag[Iterator2] := FieldValue;<br> end;<br>end;<br><br>{ --------------------------------------------------------------------------- }<br><br>procedure ReadObject(const ID: ObjectID; Source: TStream; var Data: FileData);<br>begin<br> { Read data from header object if supported }<br> if ID = WMA_FILE_PROPERTIES_ID then<br> begin<br> { Read file properties }<br> Source.Seek(80, soFromCurrent);<br> Source.ReadBuffer(Data.MaxBitRate, SizeOf(Data.MaxBitRate));<br> end;<br> if ID = WMA_STREAM_PROPERTIES_ID then<br> begin<br> { Read stream properties }<br> Source.Seek(60, soFromCurrent);<br> Source.ReadBuffer(Data.Channels, SizeOf(Data.Channels));<br> Source.ReadBuffer(Data.SampleRate, SizeOf(Data.SampleRate));<br> Source.ReadBuffer(Data.ByteRate, SizeOf(Data.ByteRate));<br> end;<br> if ID = WMA_CONTENT_DESCRIPTION_ID then<br> begin<br> { Read standard tag data }<br> Source.Seek(4, soFromCurrent);<br> ReadTagStandard(Source, Data.Tag);<br> end;<br> if ID = WMA_EXTENDED_CONTENT_DESCRIPTION_ID then<br> begin<br> { Read extended tag data }<br> Source.Seek(4, soFromCurrent);<br> ReadTagExtended(Source, Data.Tag);<br> end;<br>end;<br><br>{ --------------------------------------------------------------------------- }<br><br>function ReadData(const FileName: string; var Data: FileData): Boolean;<br>var<br> Source: TFileStream;<br> ID: ObjectID;<br> Iterator, ObjectCount, ObjectSize, Position: Integer;<br>begin<br> { Read file data }<br> try<br> Source := TFileStream.Create(FileName, fmOpenRead or fmShareDenyWrite);<br> Data.FileSize := Source.Size;<br> { Check for existing header }<br> Source.ReadBuffer(ID, SizeOf(ID));<br> if ID = WMA_HEADER_ID then<br> begin<br> Source.Seek(8, soFromCurrent);<br> Source.ReadBuffer(ObjectCount, SizeOf(ObjectCount));<br> Source.Seek(2, soFromCurrent);<br> { Read all objects in header and get needed data }<br> for Iterator := 1 to ObjectCount do<br> begin<br> Position := Source.Position;<br> Source.ReadBuffer(ID, SizeOf(ID));<br> Source.ReadBuffer(ObjectSize, SizeOf(ObjectSize));<br> ReadObject(ID, Source, Data);<br> Source.Seek(Position + ObjectSize, soFromBeginning);<br> end;<br> end;<br> Source.Free;<br> Result := true;<br> except<br> Result := false;<br> end;<br>end;<br><br>{ --------------------------------------------------------------------------- }<br><br>function IsValid(const Data: FileData): Boolean;<br>begin<br> { Check for data validity }<br> Result :=<br> (Data.MaxBitRate > 0) and (Data.MaxBitRate < 320000) and<br> ((Data.Channels = WMA_CM_MONO) or (Data.Channels = WMA_CM_STEREO)) and<br> (Data.SampleRate >= 8000) and (Data.SampleRate <= 96000) and<br> (Data.ByteRate > 0) and (Data.ByteRate < 40000);<br>end;<br><br>{ --------------------------------------------------------------------------- }<br><br>function ExtractTrack(const TrackString: WideString): Integer;<br>var<br> Value, Code: Integer;<br>begin<br> { Extract track from string }<br> Result := 0;<br> Val(TrackString, Value, Code);<br> if Code = 0 then Result := Value;<br>end;<br><br>{ ********************** Private functions & procedures ********************* }<br><br>procedure TWMAfile.FResetData;<br>begin<br> { Reset variables }<br> FValid := false;<br> FFileSize := 0;<br> FChannelModeID := WMA_CM_UNKNOWN;<br> FSampleRate := 0;<br> FDuration := 0;<br> FBitRate := 0;<br> FTitle := '';<br> FArtist := '';<br> FAlbum := '';<br> FTrack := 0;<br> FYear := '';<br> FGenre := '';<br> FComment := '';<br>end;<br><br>{ --------------------------------------------------------------------------- }<br><br>function TWMAfile.FGetChannelMode: string;<br>begin<br> { Get channel mode name }<br> Result := WMA_MODE[FChannelModeID];<br>end;<br><br>{ ********************** Public functions & procedures ********************** }<br><br>constructor TWMAfile.Create;<br>begin<br> { Create object }<br> inherited;<br> FResetData;<br>end;<br><br>{ --------------------------------------------------------------------------- }<br><br>function TWMAfile.ReadFromFile(const FileName: string): Boolean;<br>var<br> Data: FileData;<br>begin<br> { Reset variables and load file data }<br> FResetData;<br> FillChar(Data, SizeOf(Data), 0);<br> Result := ReadData(FileName, Data);<br> { Process data if loaded and valid }<br> if Result and IsValid(Data) then<br> begin<br> FValid := true;<br> { Fill properties with loaded data }<br> FFileSize := Data.FileSize;<br> FChannelModeID := Data.Channels;<br> FSampleRate := Data.SampleRate;<br> FDuration := Data.FileSize * 8 / Data.MaxBitRate;<br> FBitRate := Data.ByteRate * 8 div 1000;<br> FTitle := Trim(Data.Tag[1]);<br> FArtist := Trim(Data.Tag[2]);<br> FAlbum := Trim(Data.Tag[3]);<br> FTrack := ExtractTrack(Trim(Data.Tag[4]));<br> FYear := Trim(Data.Tag[5]);<br> FGenre := Trim(Data.Tag[6]);<br> FComment := Trim(Data.Tag[7]);<br> end;<br>end;<br><br>end.<br><br>------------------------------<br><br>VQF<br><br>-------------------------------<br>unit TwinVQ;<br><br>interface<br><br>uses<br> Classes, SysUtils;<br><br>const<br> { Used with ChannelModeID property }<br> TWIN_CM_MONO = 1; { Index for mono mode }<br> TWIN_CM_STEREO = 2; { Index for stereo mode }<br><br> { Channel mode names }<br> TWIN_MODE: array [0..2] of string = ('Unknown', 'Mono', 'Stereo');<br><br>type<br> { Class TTwinVQ }<br> TTwinVQ = class(TObject)<br> private<br> { Private declarations }<br> FValid: Boolean;<br> FChannelModeID: Byte;<br> FBitRate: Byte;<br> FSampleRate: Word;<br> FFileSize: Cardinal;<br> FDuration: Double;<br> FTitle: string;<br> FComment: string;<br> FAuthor: string;<br> FCopyright: string;<br> FOriginalFile: string;<br> FAlbum: string;<br> procedure FResetData;<br> function FGetChannelMode: string;<br> function FIsCorrupted: Boolean;<br> public<br> { Public declarations }<br> constructor Create; { Create object }<br> function ReadFromFile(const FileName: string): Boolean; { Load header }<br> property Valid: Boolean read FValid; { True if header valid }<br> property ChannelModeID: Byte read FChannelModeID; { Channel mode code }<br> property ChannelMode: string read FGetChannelMode; { Channel mode name }<br> property BitRate: Byte read FBitRate; { Total bit rate }<br> property SampleRate: Word read FSampleRate; { Sample rate (hz) }<br> property FileSize: Cardinal read FFileSize; { File size (bytes) }<br> property Duration: Double read FDuration; { Duration (seconds) }<br> property Title: string read FTitle; { Title name }<br> property Comment: string read FComment; { Comment }<br> property Author: string read FAuthor; { Author name }<br> property Copyright: string read FCopyright; { Copyright }<br> property OriginalFile: string read FOriginalFile; { Original file name }<br> property Album: string read FAlbum; { Album title }<br> property Corrupted: Boolean read FIsCorrupted; { True if file corrupted }<br> end;<br><br>implementation<br><br>const<br> { Twin VQ header ID }<br> TWIN_ID = 'TWIN';<br> <br> { Max. number of supported tag-chunks }<br> TWIN_CHUNK_COUNT = 6;<br><br> { Names of supported tag-chunks }<br> TWIN_CHUNK: array [1..TWIN_CHUNK_COUNT] of string =<br> ('NAME', 'COMT', 'AUTH', '(c) ', 'FILE', 'ALBM');<br><br>type<br> { TwinVQ chunk header }<br> ChunkHeader = record<br> ID: array [1..4] of Char; { Chunk ID }<br> Size: Cardinal; { Chunk size }<br> end;<br><br> { File header data - for internal use }<br> HeaderInfo = record<br> { Real structure of TwinVQ file header }<br> ID: array [1..4] of Char; { Always "TWIN" }<br> Version: array [1..8] of Char; { Version ID }<br> Size: Cardinal; { Header size }<br> Common: ChunkHeader; { Common chunk header }<br> ChannelMode: Cardinal; { Channel mode: 0 - mono, 1 - stereo }<br> BitRate: Cardinal; { Total bit rate }<br> SampleRate: Cardinal; { Sample rate (khz) }<br> SecurityLevel: Cardinal; { Always 0 }<br> { Extended data }<br> FileSize: Cardinal; { File size (bytes) }<br> Tag: array [1..TWIN_CHUNK_COUNT] of string; { Tag information }<br> end;<br><br>{ ********************* Auxiliary functions & procedures ******************** }<br><br>function ReadHeader(const FileName: string; var Header: HeaderInfo): Boolean;<br>var<br> SourceFile: file;<br> Transferred: Integer;<br>begin<br> try<br> Result := true;<br> { Set read-access and open file }<br> AssignFile(SourceFile, FileName);<br> FileMode := 0;<br> Reset(SourceFile, 1);<br> { Read header and get file size }<br> BlockRead(SourceFile, Header, 40, Transferred);<br> Header.FileSize := FileSize(SourceFile);<br> CloseFile(SourceFile);<br> { if transfer is not complete }<br> if Transferred < 40 then Result := false;<br> except<br> { Error }<br> Result := false;<br> end;<br>end;<br><br>{ --------------------------------------------------------------------------- }<br><br>function GetChannelModeID(const Header: HeaderInfo): Byte;<br>begin<br> { Get channel mode from header }<br> case Swap(Header.ChannelMode shr 16) of<br> 0: Result := TWIN_CM_MONO;<br> 1: Result := TWIN_CM_STEREO<br> else Result := 0;<br> end;<br>end;<br><br>{ --------------------------------------------------------------------------- }<br><br>function GetBitRate(const Header: HeaderInfo): Byte;<br>begin<br> { Get bit rate from header }<br> Result := Swap(Header.BitRate shr 16);<br>end;<br><br>{ --------------------------------------------------------------------------- }<br><br>function GetSampleRate(const Header: HeaderInfo): Word;<br>begin<br> { Get real sample rate from header }<br> Result := Swap(Header.SampleRate shr 16);<br> case Result of<br> 11: Result := 11025;<br> 22: Result := 22050;<br> 44: Result := 44100;<br> else Result := Result * 1000;<br> end;<br>end;<br><br>{ --------------------------------------------------------------------------- }<br><br>function GetDuration(const Header: HeaderInfo): Double;<br>begin<br> { Get duration from header }<br> Result := Abs((Header.FileSize - Swap(Header.Size shr 16) - 20)) / 125 /<br> Swap(Header.BitRate shr 16);<br>end;<br><br>{ --------------------------------------------------------------------------- }<br><br>function HeaderEndReached(const Chunk: ChunkHeader): Boolean;<br>begin<br> { Check for header end }<br> Result := (Ord(Chunk.ID[1]) < 32) or<br> (Ord(Chunk.ID[2]) < 32) or<br> (Ord(Chunk.ID[3]) < 32) or<br> (Ord(Chunk.ID[4]) < 32) or<br> (Chunk.ID = 'DATA');<br>end;<br><br>{ --------------------------------------------------------------------------- }<br><br>procedure SetTagItem(const ID, Data: string; var Header: HeaderInfo);<br>var<br> Iterator: Byte;<br>begin<br> { Set tag item if supported tag-chunk found }<br> for Iterator := 1 to TWIN_CHUNK_COUNT do<br> if TWIN_CHUNK[Iterator] = ID then Header.Tag[Iterator] := Data;<br>end;<br><br>{ --------------------------------------------------------------------------- }<br><br>procedure ReadTag(const FileName: string; var Header: HeaderInfo);<br>var<br> SourceFile: file;<br> Chunk: ChunkHeader;<br> Data: array [1..250] of Char;<br>begin<br> try<br> { Set read-access, open file }<br> AssignFile(SourceFile, FileName);<br> FileMode := 0;<br> Reset(SourceFile, 1);<br> Seek(SourceFile, 16);<br> repeat<br> begin<br> FillChar(Data, SizeOf(Data), 0);<br> { Read chunk header }<br> BlockRead(SourceFile, Chunk, 8);<br> { Read chunk data and set tag item if chunk header valid }<br> if HeaderEndReached(Chunk) then break;<br> BlockRead(SourceFile, Data, Swap(Chunk.Size shr 16) mod SizeOf(Data));<br> SetTagItem(Chunk.ID, Data, Header);<br> end;<br> until EOF(SourceFile);<br> CloseFile(SourceFile);<br> except<br> end;<br>end;<br><br>{ ********************** Private functions & procedures ********************* }<br><br>procedure TTwinVQ.FResetData;<br>begin<br> FValid := false;<br> FChannelModeID := 0;<br> FBitRate := 0;<br> FSampleRate := 0;<br> FFileSize := 0;<br> FDuration := 0;<br> FTitle := '';<br> FComment := '';<br> FAuthor := '';<br> FCopyright := '';<br> FOriginalFile := '';<br> FAlbum := '';<br>end;<br><br>{ --------------------------------------------------------------------------- }<br><br>function TTwinVQ.FGetChannelMode: string;<br>begin<br> Result := TWIN_MODE[FChannelModeID];<br>end;<br><br>{ --------------------------------------------------------------------------- }<br><br>function TTwinVQ.FIsCorrupted: Boolean;<br>begin<br> { Check for file corruption }<br> Result := (FValid) and<br> ((FChannelModeID = 0) or<br> (FBitRate < 8) or (FBitRate > 192) or<br> (FSampleRate < 8000) or (FSampleRate > 44100) or<br> (FDuration < 0.1) or (FDuration > 10000));<br>end;<br><br>{ ********************** Public functions & procedures ********************** }<br><br>constructor TTwinVQ.Create;<br>begin<br> inherited;<br> FResetData;<br>end;<br><br>{ --------------------------------------------------------------------------- }<br><br>function TTwinVQ.ReadFromFile(const FileName: string): Boolean;<br>var<br> Header: HeaderInfo;<br>begin<br> { Reset data and load header from file to variable }<br> FResetData;<br> Result := ReadHeader(FileName, Header);<br> { Process data if loaded and header valid }<br> if (Result) and (Header.ID = TWIN_ID) then<br> begin<br> FValid := true;<br> { Fill properties with header data }<br> FChannelModeID := GetChannelModeID(Header);<br> FBitRate := GetBitRate(Header);<br> FSampleRate := GetSampleRate(Header);<br> FFileSize := Header.FileSize;<br> FDuration := GetDuration(Header);<br> { Get tag information and fill properties }<br> ReadTag(FileName, Header);<br> FTitle := Trim(Header.Tag[1]);<br> FComment := Trim(Header.Tag[2]);<br> FAuthor := Trim(Header.Tag[3]);<br> FCopyright := Trim(Header.Tag[4]);<br> FOriginalFile := Trim(Header.Tag[5]);<br> FAlbum := Trim(Header.Tag[6]);<br> end;<br>end;<br><br>end.<br>-------------------------------