mpegaudio.pas
上传用户:raido2005
上传日期:2022-06-22
资源大小:5044k
文件大小:46k
源码类别:

Delphi控件源码

开发平台:

Delphi

  1. { *************************************************************************** }
  2. {                                                                             }
  3. { Audio Tools Library (Freeware)                                              }
  4. { Class TMPEGaudio - for manipulating with MPEG audio file information        }
  5. {                                                                             }
  6. { Uses:                                                                       }
  7. {   - Class TID3v1                                                            }
  8. {   - Class TID3v2                                                            }
  9. {                                                                             }
  10. { Copyright (c) 2001 by Jurgen Faul                                           }
  11. { E-mail: jfaul@gmx.de                                                        }
  12. { http://jfaul.de/atl                                                         }
  13. {                                                                             }
  14. { Version 1.0 (31 August 2001)                                                }
  15. {   - Support for MPEG audio (versions 1, 2, 2.5, layers I, II, III)          }
  16. {   - Support for Xing & FhG VBR                                              }
  17. {   - Ability to guess audio encoder (Xing, FhG, LAME, Blade, GoGo, Shine)    }
  18. {   - Class TID3v1: reading & writing support for ID3v1.x tags                }
  19. {   - Class TID3v2: reading support for ID3v2.3.x tags                        }
  20. {                                                                             }
  21. { 11 mar 2007  - Add ReadFromStream mathod by DelphiFlash.com                 }
  22. { 10 mar 2008  - some bugs fixed                                              }
  23. {                                                                             }
  24. { *************************************************************************** }
  25. unit MPEGaudio;
  26. interface
  27. uses
  28.   Windows, Classes, SysUtils, Contnrs;
  29. const
  30.   { Table for bit rates }
  31.   MPEG_BIT_RATE: array [0..3, 0..3, 0..15] of Word =
  32.     (
  33.     { For MPEG 2.5 }
  34.     ((0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0),
  35.     (0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 0),
  36.     (0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 0),
  37.     (0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256, 0)),
  38.     { Reserved }
  39.     ((0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0),
  40.     (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0),
  41.     (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0),
  42.     (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)),
  43.     { For MPEG 2 }
  44.     ((0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0),
  45.     (0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 0),
  46.     (0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 0),
  47.     (0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256, 0)),
  48.     { For MPEG 1 }
  49.     ((0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0),
  50.     (0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 0),
  51.     (0, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384, 0),
  52.     (0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448, 0))
  53.     );
  54.   { Sample rate codes }
  55.   MPEG_SAMPLE_RATE_LEVEL_3 = 0;                                     { Level 3 }
  56.   MPEG_SAMPLE_RATE_LEVEL_2 = 1;                                     { Level 2 }
  57.   MPEG_SAMPLE_RATE_LEVEL_1 = 2;                                     { Level 1 }
  58.   MPEG_SAMPLE_RATE_UNKNOWN = 3;                               { Unknown value }
  59.   { Table for sample rates }
  60.   MPEG_SAMPLE_RATE: array [0..3, 0..3] of Word =
  61.     (
  62.     (11025, 12000, 8000, 0),                                   { For MPEG 2.5 }
  63.     (0, 0, 0, 0),                                                  { Reserved }
  64.     (22050, 24000, 16000, 0),                                    { For MPEG 2 }
  65.     (44100, 48000, 32000, 0)                                     { For MPEG 1 }
  66.     );
  67.   { VBR header ID for Xing/FhG }
  68.   VBR_ID_XING = 'Xing';                                         { Xing VBR ID }
  69.   VBR_ID_FHG = 'VBRI';                                           { FhG VBR ID }
  70.   VBR_ID_INFO = 'Info';
  71.   { MPEG version codes }
  72.   MPEG_VERSION_2_5 = 0;                                            { MPEG 2.5 }
  73.   MPEG_VERSION_UNKNOWN = 1;                                 { Unknown version }
  74.   MPEG_VERSION_2 = 2;                                                { MPEG 2 }
  75.   MPEG_VERSION_1 = 3;                                                { MPEG 1 }
  76.   { MPEG version names }
  77.   MPEG_VERSION: array [0..3] of string =
  78.     ('MPEG 2.5', 'MPEG ?', 'MPEG 2', 'MPEG 1');
  79.   { MPEG layer codes }
  80.   MPEG_LAYER_UNKNOWN = 0;                                     { Unknown layer }
  81.   MPEG_LAYER_III = 1;                                             { Layer III }
  82.   MPEG_LAYER_II = 2;                                               { Layer II }
  83.   MPEG_LAYER_I = 3;                                                 { Layer I }
  84.   { MPEG layer names }
  85.   MPEG_LAYER: array [0..3] of string =
  86.     ('Layer ?', 'Layer III', 'Layer II', 'Layer I');
  87.   { Channel mode codes }
  88.   MPEG_CM_STEREO = 0;                                                { Stereo }
  89.   MPEG_CM_JOINT_STEREO = 1;                                    { Joint Stereo }
  90.   MPEG_CM_DUAL_CHANNEL = 2;                                    { Dual Channel }
  91.   MPEG_CM_MONO = 3;                                                    { Mono }
  92.   MPEG_CM_UNKNOWN = 4;                                         { Unknown mode }
  93.   { Channel mode names }
  94.   MPEG_CM_MODE: array [0..4] of string =
  95.     ('Stereo', 'Joint Stereo', 'Dual Channel', 'Mono', 'Unknown');
  96.   { Extension mode codes (for Joint Stereo) }
  97.   MPEG_CM_EXTENSION_OFF = 0;                        { IS and MS modes set off }
  98.   MPEG_CM_EXTENSION_IS = 1;                             { Only IS mode set on }
  99.   MPEG_CM_EXTENSION_MS = 2;                             { Only MS mode set on }
  100.   MPEG_CM_EXTENSION_ON = 3;                          { IS and MS modes set on }
  101.   MPEG_CM_EXTENSION_UNKNOWN = 4;                     { Unknown extension mode }
  102.   { Emphasis mode codes }
  103.   MPEG_EMPHASIS_NONE = 0;                                              { None }
  104.   MPEG_EMPHASIS_5015 = 1;                                          { 50/15 ms }
  105.   MPEG_EMPHASIS_UNKNOWN = 2;                               { Unknown emphasis }
  106.   MPEG_EMPHASIS_CCIT = 3;                                         { CCIT J.17 }
  107.   { Emphasis names }
  108.   MPEG_EMPHASIS: array [0..3] of string =
  109.     ('None', '50/15 ms', 'Unknown', 'CCIT J.17');
  110.   { Encoder codes }
  111.   MPEG_ENCODER_UNKNOWN = 0;                                 { Unknown encoder }
  112.   MPEG_ENCODER_XING = 1;                                               { Xing }
  113.   MPEG_ENCODER_FHG = 2;                                                 { FhG }
  114.   MPEG_ENCODER_LAME = 3;                                               { LAME }
  115.   MPEG_ENCODER_BLADE = 4;                                             { Blade }
  116.   MPEG_ENCODER_GOGO = 5;                                               { GoGo }
  117.   MPEG_ENCODER_SHINE = 6;                                             { Shine }
  118.   { Encoder names }
  119.   MPEG_ENCODER: array [0..6] of string =
  120.     ('Unknown', 'Xing', 'FhG', 'LAME', 'Blade', 'GoGo', 'Shine');
  121.   TAG_VERSION_1_0 = 1;                                { Index for ID3v1.0 tag }
  122.   TAG_VERSION_1_1 = 2;                                { Index for ID3v1.1 tag }
  123.   TAG_VERSION_2_3 = 3;                               { Code for ID3v2.3.0 tag }
  124.   { Max. number of supported tag frames }
  125.   ID3V2_FRAME_COUNT = 7;
  126.   { Names of supported tag frames }
  127.   ID3V2_FRAME: array [1..ID3V2_FRAME_COUNT] of string =
  128.     ('TIT2', 'TPE1', 'TALB', 'TRCK', 'TYER', 'TCON', 'COMM');
  129.   MAX_MUSIC_GENRES = 148;                       { Max. number of music genres }
  130.   DEFAULT_GENRE = 255;                              { Index for default genre }
  131. var
  132.   MusicGenre: array [0..MAX_MUSIC_GENRES - 1] of PChar =        { Genre names }
  133.     ({ Standard genres }
  134.      'Blues', 'Classic Rock', 'Country', 'Dance', 'Disco', 'Funk', 'Grunge', 'Hip-Hop', 'Jazz', 'Metal',
  135.      'New Age',  'Oldies', 'Other', 'Pop', 'R&B', 'Rap', 'Reggae', 'Rock', 'Techno', 'Industrial',
  136.      'Alternative', 'Ska', 'Death Metal', 'Pranks', 'Soundtrack', 'Euro-Techno', 'Ambient', 'Trip-Hop',
  137.      'Vocal', 'Jazz+Funk', 'Fusion', 'Trance', 'Classical', 'Instrumental', 'Acid', 'House', 'Game',
  138.      'Sound Clip', 'Gospel', 'Noise', 'AlternRock', 'Bass', 'Soul', 'Punk', 'Space', 'Meditative',
  139.      'Instrumental Pop', 'Instrumental Rock', 'Ethnic', 'Gothic', 'Darkwave', 'Techno-Industrial',
  140.      'Electronic', 'Pop-Folk', 'Eurodance', 'Dream', 'Southern Rock', 'Comedy', 'Cult', 'Gangsta',
  141.      'Top 40', 'Christian Rap', 'Pop/Funk', 'Jungle', 'Native American', 'Cabaret', 'New Wave',
  142.      'Psychadelic', 'Rave', 'Showtunes', 'Trailer', 'Lo-Fi', 'Tribal', 'Acid Punk', 'Acid Jazz',
  143.      'Polka', 'Retro', 'Musical', 'Rock & Roll', 'Hard Rock',
  144.      { Extended genres }
  145.      'Folk', 'Folk-Rock', 'National Folk', 'Swing', 'Fast Fusion', 'Bebob', 'Latin', 'Revival',
  146.      'Celtic', 'Bluegrass', 'Avantgarde', 'Gothic Rock', 'Progessive Rock', 'Psychedelic Rock',
  147.      'Symphonic Rock', 'Slow Rock', 'Big Band', 'Chorus', 'Easy Listening', 'Acoustic', 'Humour',
  148.      'Speech', 'Chanson', 'Opera', 'Chamber Music', 'Sonata', 'Symphony', 'Booty Bass', 'Primus',
  149.      'Porn Groove', 'Satire', 'Slow Jam', 'Club', 'Tango', 'Samba', 'Folklore', 'Ballad', 'Power Ballad',
  150.      'Rhythmic Soul', 'Freestyle', 'Duet', 'Punk Rock', 'Drum Solo', 'A capella', 'Euro-House',
  151.      'Dance Hall', 'Goa', 'Drum & Bass', 'Club-House', 'Hardcore', 'Terror', 'Indie', 'BritPop',
  152.      'Negerpunk', 'Polsk Punk', 'Beat', 'Christian Gangsta Rap', 'Heavy Metal', 'Black Metal',
  153.      'Crossover', 'Contemporary Christian', 'Christian Rock', 'Merengue', 'Salsa', 'Trash Metal',
  154.      'Anime', 'JPop', 'Synthpop');
  155. type
  156.   { Used in TID3v1 class }
  157.   String04 = string[4];                          { String with max. 4 symbols }
  158.   String30 = string[30];                        { String with max. 30 symbols }
  159.   { Xing/FhG VBR header data }
  160.   VBRData = record
  161.     Found: Boolean;                                { True if VBR header found }
  162.     ID: array [1..4] of Char;                   { Header ID: "Xing" or "VBRI" }
  163.     Frames: longint;                                 { Total number of frames }
  164.     Bytes: longint;                                   { Total number of bytes }
  165.     Scale: Byte;                                         { VBR scale (1..100) }
  166.     VendorID: array [1..8] of Char;                  { Vendor ID (if present) }
  167.   end;
  168.   { MPEG frame header data}
  169.   FrameData = record
  170.     Found: Boolean;                                     { True if frame found }
  171.     Position: Integer;                           { Frame position in the file }
  172.     Size: Word;                                          { Frame size (bytes) }
  173.     Empty: Boolean;                       { True if no significant frame data }
  174.     Data: array [1..4] of Byte;                 { The whole frame header data }
  175.     VersionID: Byte;                                        { MPEG version ID }
  176.     LayerID: Byte;                                            { MPEG layer ID }
  177.     ProtectionBit: Boolean;                        { True if protected by CRC }
  178.     BitRateID: Word;                                            { Bit rate ID }
  179.     SampleRateID: Word;                                      { Sample rate ID }
  180.     PaddingBit: Boolean;                               { True if frame padded }
  181.     PrivateBit: Boolean;                                  { Extra information }
  182.     ModeID: Byte;                                           { Channel mode ID }
  183.     ModeExtensionID: Byte;             { Mode extension ID (for Joint Stereo) }
  184.     CopyrightBit: Boolean;                        { True if audio copyrighted }
  185.     OriginalBit: Boolean;                            { True if original media }
  186.     EmphasisID: Byte;                                           { Emphasis ID }
  187.   end;
  188.   { Real structure of ID3v1 tag }
  189.   TagID3v1 = record
  190.     Header: array [1..3] of Char;                { Tag header - must be "TAG" }
  191.     Title: array [1..30] of Char;                                { Title data }
  192.     Artist: array [1..30] of Char;                              { Artist data }
  193.     Album: array [1..30] of Char;                                { Album data }
  194.     Year: array [1..4] of Char;                                   { Year data }
  195.     Comment: array [1..30] of Char;                            { Comment data }
  196.     Genre: Byte;                                                 { Genre data }
  197.   end;
  198.     { ID3v2 header data - for internal use }
  199.   TagID3v2 = record
  200.     { Real structure of ID3v2 header }
  201.     ID: array [1..3] of Char;                                  { Always "ID3" }
  202.     Version: Byte;                                           { Version number }
  203.     Revision: Byte;                                         { Revision number }
  204.     Flags: Byte;                                               { Flags of tag }
  205.     Size: array [1..4] of Byte;                   { Tag size excluding header }
  206.     { Extended data }
  207.     FileSize: Integer;                                    { File size (bytes) }
  208.     Frame: array [1..ID3V2_FRAME_COUNT] of string;  { Information from frames }
  209.   end;
  210.   TID3v1 = class(TObject)
  211.     private
  212.       { Private declarations }
  213.       FExists: Boolean;
  214.       FVersionID: Byte;
  215.       FTitle: String30;
  216.       FArtist: String30;
  217.       FAlbum: String30;
  218.       FYear: String04;
  219.       FComment: String30;
  220.       FTrack: Byte;
  221.       FGenreID: Byte;
  222.     FSize: longint;
  223.       procedure FSetTitle(const NewTitle: String30);
  224.       procedure FSetArtist(const NewArtist: String30);
  225.       procedure FSetAlbum(const NewAlbum: String30);
  226.       procedure FSetYear(const NewYear: String04);
  227.       procedure FSetComment(const NewComment: String30);
  228.       procedure FSetTrack(const NewTrack: Byte);
  229.       procedure FSetGenreID(const NewGenreID: Byte);
  230.       function FGetGenre: string;
  231.     public
  232.       { Public declarations }
  233.       constructor Create;                                     { Create object }
  234.       procedure ResetData;                                   { Reset all data }
  235.       function ReadFromFile(const FileName: string): Boolean;      { Load tag }
  236.       function ReadFromStream(Src: TStream): Boolean;
  237.       property Size: longint read FSize;
  238.       property Exists: Boolean read FExists;              { True if tag found }
  239.       property VersionID: Byte read FVersionID;                { Version code }
  240.       property Title: String30 read FTitle write FSetTitle;      { Song title }
  241.       property Artist: String30 read FArtist write FSetArtist;  { Artist name }
  242.       property Album: String30 read FAlbum write FSetAlbum;      { Album name }
  243.       property Year: String04 read FYear write FSetYear;               { Year }
  244.       property Comment: String30 read FComment write FSetComment;   { Comment }
  245.       property Track: Byte read FTrack write FSetTrack;        { Track number }
  246.       property GenreID: Byte read FGenreID write FSetGenreID;    { Genre code }
  247.       property Genre: string read FGetGenre;                     { Genre name }
  248.   end;
  249.   
  250.   TID3v2 = class(TObject)
  251.     private
  252.       { Private declarations }
  253.       FExists: Boolean;
  254.       FVersionID: Byte;
  255.       FSize: Integer;
  256.       FTitle: string;
  257.       FArtist: string;
  258.       FAlbum: string;
  259.       FTrack: Byte;
  260.       FYear: string;
  261.       FGenre: string;
  262.       FComment: string;
  263.     public
  264.       { Public declarations }
  265.       constructor Create;                                     { Create object }
  266.       procedure ResetData;                                   { Reset all data }
  267.       function ReadFromStream(Src: TStream): boolean;
  268.       function ReadFromFile(const FileName: string): Boolean;      { Load tag }
  269.       property Exists: Boolean read FExists;              { True if tag found }
  270.       property VersionID: Byte read FVersionID;                { Version code }
  271.       property Size: Integer read FSize;                     { Total tag size }
  272.       property Title: string read FTitle;                        { Song title }
  273.       property Artist: string read FArtist;                     { Artist name }
  274.       property Album: string read FAlbum;                        { Album name }
  275.       property Track: Byte read FTrack;                        { Track number }
  276.       property Year: string read FYear;                                { Year }
  277.       property Genre: string read FGenre;                        { Genre name }
  278.       property Comment: string read FComment;                       { Comment }
  279.   end;
  280.   TMP3FrameInfo = class (TObject)
  281.   private
  282.     FPosition: longint;
  283.     FSize: Word;
  284.     FisSound: boolean;
  285.     FHeader: DWord;
  286.   public
  287.     property Header: DWord read FHeader write FHeader;
  288.     property Position: longint read FPosition write FPosition;
  289.     property Size: Word read FSize write FSize;
  290.     property isSound: boolean read FisSound write FisSound;
  291.   end;
  292.   { Class TMPEGaudio }
  293.   TMPEGaudio = class(TObject)
  294.     private
  295.       { Private declarations }
  296.       FFramesPos: TObjectList;
  297.       FFileLength: Integer;
  298.       FWaveHeader: Boolean;
  299.       FVBR: VBRData;
  300.       FFrame: FrameData;
  301.       FID3v1: TID3v1;
  302.       FID3v2: TID3v2;
  303.     FSoundFrameCount: longint;
  304.       procedure FResetData;
  305.       function FGetVersion: string;
  306.       function FGetLayer: string;
  307.       function FGetBitRate: Word;
  308.       function FGetSampleRate: Word;
  309.       function FGetChannelMode: string;
  310.       function FGetEmphasis: string;
  311.       function FGetFrames: Integer;
  312.       function FGetDuration: Double;
  313.       function FGetVBREncoderID: Byte;
  314.       function FGetCBREncoderID: Byte;
  315.       function FGetEncoderID: Byte;
  316.       function FGetEncoder: string;
  317.       function FGetValid: Boolean;
  318.     function GetMP3FrameInfo(index: Integer): TMP3FrameInfo;
  319.     protected
  320.       procedure EnumFrames(Src: TStream);
  321.     public
  322.       SkipCount: integer;
  323.       { Public declarations }
  324.       constructor Create;                                     { Create object }
  325.       destructor Destroy; override;                          { Destroy object }
  326.       function ReadFromFile(const FileName: string): Boolean;     { Load data }
  327.       function ReadFromStream(Src: TStream): boolean;
  328.       function isMP3File(const FileName: string): Boolean;   {check MP3 format}
  329.       property FileLength: Integer read FFileLength;    { File length (bytes) }
  330.       property FrameInfo[index: longint]: TMP3FrameInfo read GetMP3FrameInfo;
  331.       property VBR: VBRData read FVBR;                      { VBR header data }
  332.       property FirstFrame: FrameData read FFrame;         { Frame header data }
  333.       property ID3v1: TID3v1 read FID3v1;                    { ID3v1 tag data }
  334.       property ID3v2: TID3v2 read FID3v2;                    { ID3v2 tag data }
  335.       property Version: string read FGetVersion;          { MPEG version name }
  336.       property Layer: string read FGetLayer;                { MPEG layer name }
  337.       property BitRate: Word read FGetBitRate;            { Bit rate (kbit/s) }
  338.       property SampleRate: Word read FGetSampleRate;       { Sample rate (hz) }
  339.       property ChannelMode: string read FGetChannelMode;  { Channel mode name }
  340.       property Emphasis: string read FGetEmphasis;            { Emphasis name }
  341.       property FrameCount: longint read FGetFrames;  { Total number of frames }
  342.       property SoundFrameCount: longint read FSoundFrameCount;
  343.       property Duration: Double read FGetDuration;      { Song duration (sec) }
  344.       property EncoderID: Byte read FGetEncoderID;       { Guessed encoder ID }
  345.       property Encoder: string read FGetEncoder;       { Guessed encoder name }
  346.       property Valid: Boolean read FGetValid;       { True if MPEG file valid }
  347.   end;
  348.   procedure DecodeHeader(const HeaderData: array of Byte; var Frame: FrameData);
  349.   function GetFrameLength(const Frame: FrameData): Word;
  350. implementation
  351. type
  352.   MP3FrameHeader = record
  353.     ID: array [1..4] of Char;                                      { Frame ID }
  354.     Size: Integer;                                    { Size excluding header }
  355.     Flags: Word;                                                      { Flags }
  356.   end;
  357. const
  358.   { Limitation constants }
  359.   MAX_MPEG_FRAME_LENGTH = 1729;                      { Max. MPEG frame length }
  360.   MIN_MPEG_BIT_RATE = 8;                                { Min. bit rate value }
  361.   MAX_MPEG_BIT_RATE = 448;                              { Max. bit rate value }
  362.   MIN_ALLOWED_DURATION = 0.1;                      { Min. song duration value }
  363.   { VBR Vendor ID strings }
  364.   VBR_VENDOR_ID_LAME = 'LAME';                                     { For LAME }
  365.   VBR_VENDOR_ID_GOGO_NEW = 'GOGO';                           { For GoGo (New) }
  366.   VBR_VENDOR_ID_GOGO_OLD = 'MPGE';                           { For GoGo (Old) }
  367.   VBR_Lame_Track = 'UUUU';
  368. { ********************* Auxiliary functions & procedures ******************** }
  369. function WaveHeaderPresent(const Index: Integer; Data: array of Byte): Boolean;
  370. begin
  371.   { Check for WAV header }
  372.   Result :=
  373.     (Chr(Data[Index + 8]) = 'W') and
  374.     (Chr(Data[Index + 9]) = 'A') and
  375.     (Chr(Data[Index + 10]) = 'V') and
  376.     (Chr(Data[Index + 11]) = 'E');
  377. end;
  378. { --------------------------------------------------------------------------- }
  379. function IsFrameHeader(const HeaderData: array of Byte): Boolean;
  380. begin
  381.   { Check for valid frame header }
  382.   if ((HeaderData[0] and $FF) <> $FF) or
  383.     ((HeaderData[1] and $E0) <> $E0) or
  384.     (((HeaderData[1] shr 3) and 3) = 1) or
  385.     (((HeaderData[1] shr 1) and 3) = 0) or
  386.     ((HeaderData[2] and $F0) = $F0) or
  387.     ((HeaderData[2] and $F0) = 0) or
  388.     (((HeaderData[2] shr 2) and 3) = 3) or
  389.     ((HeaderData[3] and 3) = 2) then
  390.     Result := false
  391.   else
  392.     Result := true;
  393. end;
  394. { --------------------------------------------------------------------------- }
  395. procedure DecodeHeader(const HeaderData: array of Byte; var Frame: FrameData);
  396. begin
  397.   { Decode frame header data }
  398.   Move(HeaderData, Frame.Data, SizeOf(Frame.Data));
  399.   Frame.VersionID := (HeaderData[1] shr 3) and 3;
  400.   Frame.LayerID := (HeaderData[1] shr 1) and 3;
  401.   Frame.ProtectionBit := (HeaderData[1] and 1) <> 1;
  402.   Frame.BitRateID := HeaderData[2] shr 4;
  403.   Frame.SampleRateID := (HeaderData[2] shr 2) and 3;
  404.   Frame.PaddingBit := ((HeaderData[2] shr 1) and 1) = 1;
  405.   Frame.PrivateBit := (HeaderData[2] and 1) = 1;
  406.   Frame.ModeID := (HeaderData[3] shr 6) and 3;
  407.   Frame.ModeExtensionID := (HeaderData[3] shr 4) and 3;
  408.   Frame.CopyrightBit := ((HeaderData[3] shr 3) and 1) = 1;
  409.   Frame.OriginalBit := ((HeaderData[3] shr 2) and 1) = 1;
  410.   Frame.EmphasisID := HeaderData[3] and 3;
  411. end;
  412. { --------------------------------------------------------------------------- }
  413. function ValidFrameAt(const Index: Word; Data: array of Byte): Boolean;
  414. var
  415.   HeaderData: array [1..4] of Byte;
  416. begin
  417.   { Check for frame at given position }
  418.   HeaderData[1] := Data[Index];
  419.   HeaderData[2] := Data[Index + 1];
  420.   HeaderData[3] := Data[Index + 2];
  421.   HeaderData[4] := Data[Index + 3];
  422.   if IsFrameHeader(HeaderData) then Result := true
  423.   else Result := false;
  424. end;
  425. { --------------------------------------------------------------------------- }
  426. function GetCoefficient(const Frame: FrameData): Byte;
  427. begin
  428.   { Get frame coefficient }
  429.   if Frame.VersionID = MPEG_VERSION_1 then
  430.     if Frame.LayerID = MPEG_LAYER_I then Result := 48
  431.     else Result := 144
  432.   else
  433.     if Frame.LayerID = MPEG_LAYER_I then Result := 24
  434.     else Result := 72;
  435. end;
  436. { --------------------------------------------------------------------------- }
  437. function GetBitRate(const Frame: FrameData): Word;
  438. begin
  439.   { Get bit rate }
  440.   Result := MPEG_BIT_RATE[Frame.VersionID, Frame.LayerID, Frame.BitRateID];
  441. end;
  442. { --------------------------------------------------------------------------- }
  443. function GetSampleRate(const Frame: FrameData): Word;
  444. begin
  445.   { Get sample rate }
  446.   Result := MPEG_SAMPLE_RATE[Frame.VersionID, Frame.SampleRateID];
  447. end;
  448. { --------------------------------------------------------------------------- }
  449. function GetPadding(const Frame: FrameData): Byte;
  450. begin
  451.   { Get frame padding }
  452.   if Frame.PaddingBit then
  453.     if Frame.LayerID = MPEG_LAYER_I then Result := 4
  454.     else Result := 1
  455.   else Result := 0;
  456. end;
  457. { --------------------------------------------------------------------------- }
  458. function GetFrameLength(const Frame: FrameData): Word;
  459. var
  460.   Coefficient, BitRate, SampleRate, Padding: Word;
  461. begin
  462.   { Calculate MPEG frame length }
  463.   Coefficient := GetCoefficient(Frame);
  464.   BitRate := GetBitRate(Frame);
  465.   SampleRate := GetSampleRate(Frame);
  466.   Padding := GetPadding(Frame);
  467.   Result := Trunc(Coefficient * BitRate * 1000 / SampleRate) + Padding;
  468.   
  469. {  if Frame.LayerID = MPEG_LAYER_I then
  470.     Result := Trunc((12 * BitRate * 1000 / SampleRate + Padding) * 4)
  471.     else
  472.     Result := trunc(144 * BitRate * 1000 / SampleRate) + Padding;
  473. }
  474. end;
  475. { --------------------------------------------------------------------------- }
  476. function FrameIsEmpty(const Index: Word; Data: array of Byte): Boolean;
  477. begin
  478.   { Get true if frame has no significant data }
  479.   Result :=
  480.     (Data[Index] = 0) and
  481.     (Data[Index + 1] = 0) and
  482.     (Data[Index + 2] = 0) and
  483.     (Data[Index + 3] = 0) and
  484.     (Data[Index + 4] = 0) and
  485.     (Data[Index + 5] = 0);
  486. end;
  487. { --------------------------------------------------------------------------- }
  488. function GetXingInfo(const Index: Word; Data: array of Byte): VBRData;
  489. begin
  490.   { Extract Xing VBR info at given position }
  491.   FillChar(Result, SizeOf(Result), 0);
  492.   Result.Found := true;
  493.   Result.ID := VBR_ID_XING;
  494.   Result.Frames := Data[Index + 8] shl 24 + Data[Index + 9] shl 16 +
  495.                    Data[Index + 10] shl 8 + Data[Index + 11];
  496.   Result.Bytes :=  Data[Index + 12] shl 24 + Data[Index + 13] shl 16 +
  497.                    Data[Index + 14]  shl 8 + Data[Index + 15];
  498.   Result.Scale := Data[Index + 119];
  499.   { Encoder ID can be not present }
  500.   Result.VendorID[1] := Chr(Data[Index + 120]);
  501.   Result.VendorID[2] := Chr(Data[Index + 121]);
  502.   Result.VendorID[3] := Chr(Data[Index + 122]);
  503.   Result.VendorID[4] := Chr(Data[Index + 123]);
  504.   Result.VendorID[5] := Chr(Data[Index + 124]);
  505.   Result.VendorID[6] := Chr(Data[Index + 125]);
  506.   Result.VendorID[7] := Chr(Data[Index + 126]);
  507.   Result.VendorID[8] := Chr(Data[Index + 127]);
  508. end;
  509. { --------------------------------------------------------------------------- }
  510. function GetFhGInfo(const Index: Word; Data: array of Byte): VBRData;
  511. begin
  512.   { Extract FhG VBR info at given position }
  513.   FillChar(Result, SizeOf(Result), 0);
  514.   Result.Found := true;
  515.   Result.ID := VBR_ID_FHG;
  516.   Result.Scale :=  Data[Index + 9];
  517.   Result.Bytes :=  Data[Index + 10] shl 24 + Data[Index + 11] shl 16 +
  518.                    Data[Index + 12] shl 8  + Data[Index + 13];
  519.   Result.Frames := Data[Index + 14] shl 24 + Data[Index + 15] shl 16 +
  520.                    Data[Index + 16] shl 8  + Data[Index + 17];
  521. end;
  522. { --------------------------------------------------------------------------- }
  523. function GetVbrInfo(const Index: Word; Data: array of Byte): VBRData;
  524. var
  525.   PSize: PLongint;
  526.   PPos: PByte absolute PSize;
  527.   Flags: byte;
  528. begin
  529.   FillChar(Result, SizeOf(Result), 0);
  530.   Result.Found := true;
  531.   Result.ID := VBR_ID_INFO;
  532.   Flags := Data[Index + 7];
  533.   if (Flags and 1) = 1 then
  534.     Result.Frames := Data[Index + 8] shl 24 + Data[Index + 9] shl 16 +
  535.                      Data[Index + 10] shl 8  + Data[Index + 11];
  536.   if (Flags and 2) = 2 then
  537.     Result.Bytes :=  Data[Index + 12] shl 24 + Data[Index + 13] shl 16 +
  538.                      Data[Index + 14] shl 8  + Data[Index + 15];
  539.   if (Flags and 8) = 8 then
  540.     Result.Scale := Data[Index + 119];
  541. end;
  542. { --------------------------------------------------------------------------- }
  543. function FindVBR(const Index: Word; Data: array of Byte): VBRData;
  544. begin
  545.   { Check for VBR header at given position }
  546.   FillChar(Result, SizeOf(Result), 0);
  547.   if Chr(Data[Index]) +
  548.     Chr(Data[Index + 1]) +
  549.     Chr(Data[Index + 2]) +
  550.     Chr(Data[Index + 3]) = VBR_ID_XING then Result := GetXingInfo(Index, Data);
  551.   if Chr(Data[Index]) +
  552.     Chr(Data[Index + 1]) +
  553.     Chr(Data[Index + 2]) +
  554.     Chr(Data[Index + 3]) = VBR_ID_FHG then Result := GetFhGInfo(Index, Data);
  555.   if Chr(Data[Index]) +
  556.     Chr(Data[Index + 1]) +
  557.     Chr(Data[Index + 2]) +
  558.     Chr(Data[Index + 3]) = VBR_ID_INFO then Result := GetVbrInfo(Index, Data);
  559. end;
  560. { --------------------------------------------------------------------------- }
  561. function GetVBRDeviation(const Frame: FrameData): Byte;
  562. begin
  563.   { Calculate VBR deviation }
  564.   if Frame.VersionID = MPEG_VERSION_1 then
  565.     if Frame.ModeID <> MPEG_CM_MONO then Result := 36
  566.     else Result := 21
  567.   else
  568.     if Frame.ModeID <> MPEG_CM_MONO then Result := 21
  569.     else Result := 13;
  570. end;
  571. { --------------------------------------------------------------------------- }
  572. function FindFrame(const Data: array of Byte; var VBR: VBRData): FrameData;
  573. var
  574.   HeaderData: array [1..4] of Byte;
  575.   Iterator: Integer;
  576. //  isDecode: boolean;
  577. begin
  578.   { Search for valid frame }
  579.   FillChar(Result, SizeOf(Result), 0);
  580.   Move(Data, HeaderData, SizeOf(HeaderData));
  581.   for Iterator := 0 to SizeOf(Data) - MAX_MPEG_FRAME_LENGTH do
  582.   begin
  583.     { Decode data if frame header found }
  584.     if IsFrameHeader(HeaderData) then
  585.     begin
  586.       DecodeHeader(HeaderData, Result);
  587. //      if not isDecode then
  588. //      begin
  589. //        DecodeHeader(HeaderData, Result);
  590. //        isDecode := true;
  591. //        Result.Found := true;
  592. //        Result.Size := GetFrameLength(Result);
  593. //        Result.Empty := FrameIsEmpty(Iterator + SizeOf(HeaderData), Data);
  594. //      end;
  595.       { Check for next frame and try to find VBR header }
  596.       if ValidFrameAt(Iterator + GetFrameLength(Result), Data) then
  597.       begin
  598.         Result.Found := true;
  599.         Result.Position := Iterator;
  600.         Result.Size := GetFrameLength(Result);
  601.         Result.Empty := FrameIsEmpty(Iterator + SizeOf(HeaderData), Data);
  602.         VBR := FindVBR(Iterator + GetVBRDeviation(Result), Data);
  603.         break;
  604.       end;
  605.     end;
  606.     { Prepare next data block }
  607.     HeaderData[1] := HeaderData[2];
  608.     HeaderData[2] := HeaderData[3];
  609.     HeaderData[3] := HeaderData[4];
  610.     HeaderData[4] := Data[Iterator + SizeOf(HeaderData)];
  611.   end;
  612. end;
  613. { --------------------------------------------------------------------------- }
  614. function GetTrack(const TrackString: string): Byte;
  615. var
  616.   Index, Value, Code: Integer;
  617. begin
  618.   { Extract track from string }
  619.   Index := Pos('/', TrackString);
  620.   if Index = 0 then Val(Trim(TrackString), Value, Code)
  621.   else Val(Copy(Trim(TrackString), 1, Index), Value, Code);
  622.   if Code = 0 then Result := Value
  623.   else Result := 0;
  624. end;
  625. { ********************** Private functions & procedures ********************* }
  626. procedure TMPEGaudio.FResetData;
  627. begin
  628.   { Reset all variables }
  629.   FFileLength := 0;
  630.   FillChar(FVBR, SizeOf(FVBR), 0);
  631.   FillChar(FFrame, SizeOf(FFrame), 0);
  632.   FFrame.VersionID := MPEG_VERSION_UNKNOWN;
  633.   FFrame.SampleRateID := MPEG_SAMPLE_RATE_UNKNOWN;
  634.   FFrame.ModeID := MPEG_CM_UNKNOWN;
  635.   FFrame.ModeExtensionID := MPEG_CM_EXTENSION_UNKNOWN;
  636.   FFrame.EmphasisID := MPEG_EMPHASIS_UNKNOWN;
  637.   FID3v1.ResetData;
  638.   FID3v2.ResetData;
  639. end;
  640. function TMPEGaudio.GetMP3FrameInfo(index: longint): TMP3FrameInfo;
  641. begin
  642.   result := TMP3FrameInfo(FFramesPos[index]);
  643. end;
  644. function TMPEGaudio.isMP3File(const FileName: string): Boolean;
  645. var
  646.   F: TFileStream;
  647.   Data: array [1..MAX_MPEG_FRAME_LENGTH * 2] of Byte;
  648. begin
  649.   F := TFileStream.Create(FileName, fmOpenRead + fmShareDenyWrite);
  650.   Result := false;
  651.   FResetData;
  652.   if (FID3v1.ReadFromStream(F)) and (FID3v2.ReadFromStream(F)) then
  653.   begin
  654.     F.Position := FID3v2.Size;
  655.     F.Read(Data, SizeOf(Data));
  656.     FWaveHeader := WaveHeaderPresent(FID3v2.Size, Data);
  657.     FFrame := FindFrame(Data, FVBR);
  658.     Result := FFrame.Found;
  659.   end;
  660.   F.Free;
  661. end;
  662. { --------------------------------------------------------------------------- }
  663. function TMPEGaudio.FGetVersion: string;
  664. begin
  665.   { Get MPEG version name }
  666.   Result := MPEG_VERSION[FFrame.VersionID];
  667. end;
  668. { --------------------------------------------------------------------------- }
  669. function TMPEGaudio.FGetLayer: string;
  670. begin
  671.   { Get MPEG layer name }
  672.   Result := MPEG_LAYER[FFrame.LayerID];
  673. end;
  674. { --------------------------------------------------------------------------- }
  675. function TMPEGaudio.FGetBitRate: Word;
  676. begin
  677.   { Get bit rate, calculate average bit rate if VBR header found }
  678.   if (FVBR.Found) and (FVBR.Frames > 0) then
  679.     Result := Round((FVBR.Bytes / FVBR.Frames - GetPadding(FFrame)) *
  680.       GetSampleRate(FFrame) / GetCoefficient(FFrame) / 1000)
  681.   else
  682.     Result := GetBitRate(FFrame);
  683. end;
  684. { --------------------------------------------------------------------------- }
  685. function TMPEGaudio.FGetSampleRate: Word;
  686. begin
  687.   { Get sample rate }
  688.   Result := GetSampleRate(FFrame);
  689. end;
  690. { --------------------------------------------------------------------------- }
  691. function TMPEGaudio.FGetChannelMode: string;
  692. begin
  693.   { Get channel mode name }
  694.   Result := MPEG_CM_MODE[FFrame.ModeID];
  695. end;
  696. { --------------------------------------------------------------------------- }
  697. function TMPEGaudio.FGetEmphasis: string;
  698. begin
  699.   { Get emphasis name }
  700.   Result := MPEG_EMPHASIS[FFrame.EmphasisID];
  701. end;
  702. { --------------------------------------------------------------------------- }
  703. function TMPEGaudio.FGetFrames: LongInt;
  704. begin
  705.   { Get total number of frames, calculate if VBR header not found }
  706.   if FFramesPos.Count > 0 then
  707.     Result := FFramesPos.Count else
  708.   if FVBR.Found then
  709.     Result := FVBR.Frames
  710.   else
  711.     Result := (FFileLength - FID3v2.Size - FFrame.Position) div
  712.       GetFrameLength(FFrame);
  713. end;
  714. { --------------------------------------------------------------------------- }
  715. function TMPEGaudio.FGetDuration: Double;
  716. begin
  717.   { Calculate song duration }
  718.   if FFrame.Found then
  719.     if (FVBR.Found) and (FVBR.Frames > 0) then
  720.       Result := FVBR.Frames * GetCoefficient(FFrame) * 8 /
  721.         GetSampleRate(FFrame)
  722.     else
  723.       Result := (FFileLength - FID3v2.Size - FFrame.Position) * 8 /
  724.         GetBitRate(FFrame) / 1000
  725.   else
  726.     Result := 0;
  727. end;
  728. { --------------------------------------------------------------------------- }
  729. function TMPEGaudio.FGetVBREncoderID: Byte;
  730. begin
  731.   { Guess VBR encoder and get ID }
  732.   Result := 0;
  733.   if Copy(FVBR.VendorID, 1, 4) = VBR_VENDOR_ID_LAME then
  734.     Result := MPEG_ENCODER_LAME;
  735.   if Copy(FVBR.VendorID, 1, 4) = VBR_VENDOR_ID_GOGO_NEW then
  736.     Result := MPEG_ENCODER_GOGO;
  737.   if Copy(FVBR.VendorID, 1, 4) = VBR_VENDOR_ID_GOGO_OLD then
  738.     Result := MPEG_ENCODER_GOGO;
  739.   if (FVBR.ID = VBR_ID_XING) and
  740.     (Copy(FVBR.VendorID, 1, 4) <> VBR_VENDOR_ID_LAME) and
  741.     (Copy(FVBR.VendorID, 1, 4) <> VBR_VENDOR_ID_GOGO_NEW) and
  742.     (Copy(FVBR.VendorID, 1, 4) <> VBR_VENDOR_ID_GOGO_OLD) then
  743.     Result := MPEG_ENCODER_XING;
  744.   if FVBR.ID = VBR_ID_FHG then
  745.     Result := MPEG_ENCODER_FHG;
  746. end;
  747. { --------------------------------------------------------------------------- }
  748. function TMPEGaudio.FGetCBREncoderID: Byte;
  749. begin
  750.   { Guess CBR encoder and get ID }
  751.   Result := 0;
  752.   if (FFrame.OriginalBit) and
  753.     (FFrame.ProtectionBit) then
  754.     Result := MPEG_ENCODER_LAME;
  755.   if (FFrame.ModeID = MPEG_CM_JOINT_STEREO) and
  756.     (not FFrame.CopyrightBit) and
  757.     (not FFrame.OriginalBit) then
  758.     Result := MPEG_ENCODER_FHG;
  759.   if (GetBitRate(FFrame) <= 112) and
  760.     (FFrame.ModeID = MPEG_CM_STEREO) then
  761.     Result := MPEG_ENCODER_BLADE;
  762.   if (FFrame.CopyrightBit) and
  763.     (FFrame.OriginalBit) and
  764.     (not FFrame.ProtectionBit) then
  765.     Result := MPEG_ENCODER_XING;
  766.   if (FFrame.Empty) and
  767.     (FFrame.OriginalBit) then
  768.     Result := MPEG_ENCODER_XING;
  769.   if (FWaveHeader) then
  770.     Result := MPEG_ENCODER_FHG;
  771.   if (FFrame.ModeID = MPEG_CM_DUAL_CHANNEL) and
  772.     (FFrame.ProtectionBit) then
  773.     Result := MPEG_ENCODER_SHINE;
  774. end;
  775. { --------------------------------------------------------------------------- }
  776. function TMPEGaudio.FGetEncoderID: Byte;
  777. begin
  778.   { Get guessed encoder ID }
  779.   if FFrame.Found then
  780.     if FVBR.Found then Result := FGetVBREncoderID
  781.     else Result := FGetCBREncoderID
  782.   else
  783.     Result := 0;
  784. end;
  785. { --------------------------------------------------------------------------- }
  786. function TMPEGaudio.FGetEncoder: string;
  787. begin
  788.   { Get guessed encoder name }
  789.   Result := MPEG_ENCODER[FGetEncoderID];
  790.   if (FVBR.Found) and
  791.     (FGetEncoderID = MPEG_ENCODER_LAME) and
  792.     (FVBR.VendorID[5] in ['0'..'9']) and
  793.     (FVBR.VendorID[6] = '.') and
  794.     (FVBR.VendorID[7] in ['0'..'9']) and
  795.     (FVBR.VendorID[8] in ['0'..'9']) then
  796.     Result :=
  797.       Result + #32 +
  798.       FVBR.VendorID[5] +
  799.       FVBR.VendorID[6] +
  800.       FVBR.VendorID[7] +
  801.       FVBR.VendorID[8];
  802. end;
  803. { --------------------------------------------------------------------------- }
  804. function TMPEGaudio.FGetValid: Boolean;
  805. begin
  806.   { Check for right MPEG file data }
  807.   Result :=
  808.     (FFrame.Found) and
  809.     (FGetBitRate >= MIN_MPEG_BIT_RATE) and
  810.     (FGetBitRate <= MAX_MPEG_BIT_RATE) and
  811.     (FGetDuration >= MIN_ALLOWED_DURATION);
  812. end;
  813. { ********************** Public functions & procedures ********************** }
  814. constructor TMPEGaudio.Create;
  815. begin
  816.   inherited;
  817.   FID3v1 := TID3v1.Create;
  818.   FID3v2 := TID3v2.Create;
  819.   FResetData;
  820.   FFramesPos := TObjectList.Create;
  821. end;
  822. { --------------------------------------------------------------------------- }
  823. destructor TMPEGaudio.Destroy;
  824. begin
  825.   FFramesPos.Free;
  826.   FID3v1.Free;
  827.   FID3v2.Free;
  828.   inherited;
  829. end;
  830. { --------------------------------------------------------------------------- }
  831. function TMPEGaudio.ReadFromFile(const FileName: string): Boolean;
  832. var
  833.   F: TFileStream;
  834. begin
  835.   F := TFileStream.Create(FileName, fmOpenRead + fmShareDenyWrite);
  836.   Result := ReadFromStream(F);
  837.   F.Free;
  838. end;
  839. function TMPEGaudio.ReadFromStream(Src: TStream): boolean;
  840. var
  841.   Data: array [1..MAX_MPEG_FRAME_LENGTH * 2] of Byte;
  842. begin
  843.   Result := false;
  844.   FResetData;
  845.   if (FID3v1.ReadFromStream(Src)) and (FID3v2.ReadFromStream(Src)) then
  846.     try
  847.       { Open file, read first block of data and search for a frame }
  848.       FFileLength := Src.Size;
  849.       Src.Position := FID3v2.Size;
  850.       Src.Read(Data, SizeOf(Data));
  851. //      BlockRead(SourceFile, Data, SizeOf(Data), Transferred);
  852.       FWaveHeader := WaveHeaderPresent(FID3v2.Size, Data);
  853.       FFrame := FindFrame(Data, FVBR);
  854.       { Try to search in the middle if no frame at the beginning found }
  855.       if (not FFrame.Found) {and (Transferred = SizeOf(Data))} then
  856.       begin
  857.         Src.Position := (FFileLength - FID3v2.Size) div 2;
  858.         Src.Read(Data, SizeOf(Data));
  859.         FFrame := FindFrame(Data, FVBR);
  860.       end;
  861.       Result := true;
  862.       if FFrame.Found then
  863.         EnumFrames(Src);
  864.     except
  865.     end;
  866.   if not FFrame.Found then FResetData;
  867. end;
  868. procedure TMPEGaudio.EnumFrames(Src: TStream);
  869.  var HeaderData: array [1..4] of Byte;
  870.      DW: dword absolute HeaderData;
  871.      CFrame: FrameData;
  872.      FI: TMP3FrameInfo;
  873.      vbrflag: array [0..3] of char;
  874. begin
  875.   CFrame := FirstFrame;
  876.   FSoundFrameCount := 0;
  877.   CFrame.Position := FirstFrame.Position + ID3v2.Size;
  878.   Src.Position := CFrame.Position;
  879.   while (Src.Position + ID3v1.Size) < Src.Size do
  880.   begin
  881.     Src.Read(HeaderData, 4);
  882.     if IsFrameHeader(HeaderData) then
  883.     begin
  884.       FI := TMP3FrameInfo.Create;
  885.       FI.Header := DW;
  886.       FI.Position := Src.Position - 4;
  887.       DecodeHeader(HeaderData, CFrame);
  888.       FI.Size := GetFrameLength(CFrame);
  889.       Src.Seek(32, 1);
  890.       Src.Read(vbrflag, 4);
  891.       FI.isSound := not((vbrflag = VBR_ID_INFO) or (vbrflag = VBR_Lame_Track) or
  892.                         (vbrflag = VBR_ID_XING) or (vbrflag = MPEG_ENCODER[3]));
  893.       if FI.isSound then inc(FSoundFrameCount);
  894.       FFramesPos.Add(FI);
  895.       Src.Position := FI.Position + Fi.Size;
  896.     end else
  897.       Src.Position := Src.Size;
  898.   end;
  899. end;
  900. function GetTagVersion(const TagData: TagID3v1): Byte;
  901. begin
  902.   Result := TAG_VERSION_1_0;
  903.   { Terms for ID3v1.1 }
  904.   if ((TagData.Comment[29] = #0) and (TagData.Comment[30] <> #0)) or
  905.     ((TagData.Comment[29] = #32) and (TagData.Comment[30] <> #32)) then
  906.     Result := TAG_VERSION_1_1;
  907. end;
  908. function Swap32(const Figure: Integer): Integer;
  909. var
  910.   ByteArray: array [1..4] of Byte absolute Figure;
  911. begin
  912.   { Swap 4 bytes }
  913.   Result :=
  914.     ByteArray[1] * $100000000 +
  915.     ByteArray[2] * $10000 +
  916.     ByteArray[3] * $100 +
  917.     ByteArray[4];
  918. end;
  919. { ********************** TID3v1  ********************* }
  920. constructor TID3v1.Create;
  921. begin
  922.   inherited;
  923.   ResetData;
  924. end;
  925. { --------------------------------------------------------------------------- }
  926. procedure TID3v1.ResetData;
  927. begin
  928.   FExists := false;
  929.   FVersionID := TAG_VERSION_1_0;
  930.   FTitle := '';
  931.   FArtist := '';
  932.   FAlbum := '';
  933.   FYear := '';
  934.   FComment := '';
  935.   FTrack := 0;
  936.   FGenreID := DEFAULT_GENRE;
  937. end;
  938. { --------------------------------------------------------------------------- }
  939. function TID3v1.ReadFromFile(const FileName: string): Boolean;
  940.  var
  941.    F: TFileStream;
  942. begin
  943.   F := TFileStream.Create(FileName, fmOpenRead + fmShareDenyWrite);
  944.   Result := ReadFromStream(F);
  945.   F.Free;
  946. end;
  947. { --------------------------------------------------------------------------- }
  948. function TID3v1.ReadFromStream(Src: TStream): Boolean;
  949.  var
  950.    TagData: TagID3v1;
  951. begin
  952.   Result := true;
  953.   Src.Position := Src.Size - 128;
  954.   Src.Read(TagData, 128);
  955.   FSize := 0;
  956.   if (TagData.Header = 'TAG') then
  957.   begin
  958.     FSize := 128;
  959.     FExists := true;
  960.     FVersionID := GetTagVersion(TagData);
  961.     { Fill properties with tag data }
  962.     FTitle := TrimRight(TagData.Title);
  963.     FArtist := TrimRight(TagData.Artist);
  964.     FAlbum := TrimRight(TagData.Album);
  965.     FYear := TrimRight(TagData.Year);
  966.     if FVersionID = TAG_VERSION_1_0 then
  967.       FComment := TrimRight(TagData.Comment)
  968.     else
  969.     begin
  970.       FComment := TrimRight(Copy(TagData.Comment, 1, 28));
  971.       FTrack := Ord(TagData.Comment[30]);
  972.     end;
  973.     FGenreID := TagData.Genre;
  974.     Result := true;
  975.   end;
  976. end;
  977. procedure TID3v1.FSetTitle(const NewTitle: String30);
  978. begin
  979.   FTitle := TrimRight(NewTitle);
  980. end;
  981. { --------------------------------------------------------------------------- }
  982. procedure TID3v1.FSetArtist(const NewArtist: String30);
  983. begin
  984.   FArtist := TrimRight(NewArtist);
  985. end;
  986. { --------------------------------------------------------------------------- }
  987. procedure TID3v1.FSetAlbum(const NewAlbum: String30);
  988. begin
  989.   FAlbum := TrimRight(NewAlbum);
  990. end;
  991. { --------------------------------------------------------------------------- }
  992. procedure TID3v1.FSetYear(const NewYear: String04);
  993. begin
  994.   FYear := TrimRight(NewYear);
  995. end;
  996. { --------------------------------------------------------------------------- }
  997. procedure TID3v1.FSetComment(const NewComment: String30);
  998. begin
  999.   FComment := TrimRight(NewComment);
  1000. end;
  1001. { --------------------------------------------------------------------------- }
  1002. procedure TID3v1.FSetTrack(const NewTrack: Byte);
  1003. begin
  1004.   FTrack := NewTrack;
  1005. end;
  1006. { --------------------------------------------------------------------------- }
  1007. procedure TID3v1.FSetGenreID(const NewGenreID: Byte);
  1008. begin
  1009.   if NewGenreID >= MAX_MUSIC_GENRES
  1010.     then FGenreID := MAX_MUSIC_GENRES - 1
  1011.     else FGenreID := NewGenreID;
  1012. end;
  1013. { --------------------------------------------------------------------------- }
  1014. function TID3v1.FGetGenre: string;
  1015. begin
  1016.   Result := '';
  1017.   { Return an empty string if the current GenreID is not valid }
  1018.   if FGenreID in [0..MAX_MUSIC_GENRES - 1] then Result := MusicGenre[FGenreID];
  1019. end;
  1020. { ********************************** TID3v2 ******************************** }
  1021. constructor TID3v2.Create;
  1022. begin
  1023.   inherited;
  1024.   ResetData;
  1025. end;
  1026. { --------------------------------------------------------------------------- }
  1027. procedure TID3v2.ResetData;
  1028. begin
  1029.   FExists := false;
  1030.   FVersionID := 0;
  1031.   FSize := 0;
  1032.   FTitle := '';
  1033.   FArtist := '';
  1034.   FAlbum := '';
  1035.   FTrack := 0;
  1036.   FYear := '';
  1037.   FGenre := '';
  1038.   FComment := '';
  1039. end;
  1040. { --------------------------------------------------------------------------- }
  1041. function TID3v2.ReadFromFile(const FileName: string): Boolean;
  1042. var
  1043.   F: TFileStream;
  1044. begin
  1045.   F := TFileStream.Create(FileName, fmOpenRead + fmShareDenyWrite);
  1046.   Result := ReadFromStream(F);
  1047.   F.Free;
  1048. end;
  1049. function TID3v2.ReadFromStream(Src: TStream): boolean;
  1050. var
  1051.   TagData: TagID3v2;
  1052.   Frame: MP3FrameHeader;
  1053.   DataPosition: longint;
  1054.   Data: array [1..250] of Char;
  1055.   Iterator: Byte;
  1056. begin
  1057.   ResetData;
  1058.   Result := true;
  1059.   Src.Position := 0;
  1060.   Src.Read(TagData, 10);
  1061.   if TagData.ID = 'ID3' then
  1062.   begin
  1063.     FExists := true;
  1064.     TagData.FileSize := Src.Size;
  1065.     { Fill properties with header data }
  1066.     FVersionID := TagData.Version;
  1067.     FSize := TagData.Size[1] * $200000 + TagData.Size[2] * $4000 + TagData.Size[3] * $80 + TagData.Size[4] + 10;
  1068.     if FSize > TagData.FileSize then FSize := 0;;
  1069.     { Get information from frames if version supported }
  1070.     if (FVersionID = TAG_VERSION_2_3) and (FSize > 0) then
  1071.     begin
  1072.       while (Src.Position < FSize) and (Src.Position < Src.Size) do
  1073.       begin
  1074.         FillChar(Data, SizeOf(Data), 0);
  1075.         Src.Read(Frame, 10);
  1076.         DataPosition := Src.Position;
  1077.         Src.Read(Data, Swap32(Frame.Size) mod SizeOf(Data));
  1078.         for Iterator := 1 to ID3V2_FRAME_COUNT do
  1079.           if ID3V2_FRAME[Iterator] = Frame.ID then TagData.Frame[Iterator] := Data;
  1080.         Src.Seek(DataPosition + Swap32(Frame.Size), 0);
  1081.       end;
  1082.       { Fill properties with data from frames }
  1083.       FTitle := Trim(TagData.Frame[1]);
  1084.       FArtist := Trim(TagData.Frame[2]);
  1085.       FAlbum := Trim(TagData.Frame[3]);
  1086.       FTrack := GetTrack(TagData.Frame[4]);
  1087.       FYear := Trim(TagData.Frame[5]);
  1088.       FGenre := Trim(TagData.Frame[6]);
  1089.       if Pos(')', FGenre) > 0 then Delete(FGenre, 1, LastDelimiter(')', FGenre));
  1090.       FComment := Trim(Copy(TagData.Frame[7], 5, Length(TagData.Frame[7]) - 4));
  1091.     end;
  1092.   end;
  1093. end;
  1094. { TMP3FrameInfo }
  1095. end.