unit _Old_AudioMPEG;

interface

uses Windows, KOL, ATLCommon, TagID3v1, TagID3v2, TagAPE;

function NewAudioMPEG: PAudioInfo;

implementation

uses ATLLib;

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

  { Xing/FhG VBR header data }
  TVBRData = record
    Found: Boolean;                                { True if VBR header found }
    ID: array [1..4] of Char;                   { Header ID: "Xing" or "VBRI" }
    Frames: Integer;                                 { Total number of frames }
    Bytes: Integer;                                   { Total number of bytes }
    Scale: Byte;                                         { VBR scale (1..100) }
    VendorID: string;                                { Vendor ID (if present) }
  end;
  
const
  { Limitation constants }
  MAX_MPEG_FRAME_LENGTH = 1729;                      { Max. MPEG frame length }
  MIN_MPEG_BIT_RATE = 8;                                { Min. bit rate value }
  MAX_MPEG_BIT_RATE = 448;                              { Max. bit rate value }
  MIN_ALLOWED_DURATION = 0.1;                      { Min. song duration value }

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

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

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

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

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

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

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

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

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

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

  MPEG_SAMPLE_RATE: array [0..3, 0..3] of Word =
    (
    (11025, 12000, 8000, 0),                                   { For MPEG 2.5 }
    (0, 0, 0, 0),                                                  { Reserved }
    (22050, 24000, 16000, 0),                                    { For MPEG 2 }
    (44100, 48000, 32000, 0)                                     { For MPEG 1 }
    );
  MPEG_CM_MODE: array [0..4] of string = ('Stereo', 'Joint Stereo', 'Dual Channel', 'Mono', 'Unknown');
  MPEG_VERSION: array [0..3] of string = ('MPEG 2.5', 'MPEG ?', 'MPEG 2', 'MPEG 1');
  MPEG_LAYER: array [0..3] of string = ('Layer ?', 'Layer III', 'Layer II', 'Layer I');
  MPEG_EMPHASIS: array [0..3] of string = ('None', '50/15 ms', 'Unknown', 'CCIT J.17');
  MPEG_ENCODER: array [0..7] of string = ('Unknown', 'Xing', 'FhG', 'LAME', 'Blade', 'GoGo', 'Shine', 'QDesign');

type
  TAudioMPEG = object(TAudioInfo)
  end;

function AudioMPEGReader(const AFileName: KOLstring; AudioInfo: PAudioInfo): Boolean; forward;

function NewAudioMPEG: PAudioInfo;
begin
    New(Result, Create);
    with TAudioMPEG(Result^) do begin
      TagAdd(tID3v1, NewTagID3v1);
      TagAdd(tID3v2, NewTagID3v2);
      TagAdd(tAPE, NewTagAPE);
      FAudioVendor:= aMPEG;
      FExistsProps:= [apSampleRate, apBitrate, apChannels, apBits, apSamples, apDuration, apRatio, apBitrateType];
      FReader:= AudioMPEGReader;
    end;
end;

function AudioMPEGReader(const AFileName: KOLstring; AudioInfo: PAudioInfo): Boolean;
  function GetPadding(const AFrame: TFrameData): Byte;
  begin
      if AFrame.PaddingBit then
        if AFrame.LayerID = MPEG_LAYER_I then Result:= 4 else Result:= 1
      else Result:= 0;
  end;
  function GetCoefficient(const AFrame: TFrameData): Byte;
  begin
      if AFrame.VersionID = MPEG_VERSION_1 then
        if AFrame.LayerID = MPEG_LAYER_I then Result:= 48 else Result:= 144
      else
        if AFrame.LayerID = MPEG_LAYER_I then Result:= 24
        else
          if AFrame.LayerID = MPEG_LAYER_II then Result:= 144 else Result:= 72;
  end;
  function GetFrameLength(const AFrame: TFrameData): Word;
  begin
      Result:= Trunc(GetCoefficient(AFrame) * MPEG_BIT_RATE[AFrame.VersionID, AFrame.LayerID, AFrame.BitRateID] * 1000 / MPEG_SAMPLE_RATE[AFrame.VersionID, AFrame.SampleRateID]) + GetPadding(AFrame);
  end;
  procedure DecodeHeader(const AHeaderData: array of Byte; var AFrame: TFrameData);
  begin
      Move(AHeaderData, AFrame.Data, SizeOf(AFrame.Data));
      AFrame.VersionID:= (AHeaderData[1] shr 3) and 3;
      AFrame.LayerID:= (AHeaderData[1] shr 1) and 3;
      AFrame.ProtectionBit:= (AHeaderData[1] and 1) <> 1;
      AFrame.BitRateID:= AHeaderData[2] shr 4;
      AFrame.SampleRateID:= (AHeaderData[2] shr 2) and 3;
      AFrame.PaddingBit:= ((AHeaderData[2] shr 1) and 1) = 1;
      AFrame.PrivateBit:= (AHeaderData[2] and 1) = 1;
      AFrame.ModeID:= (AHeaderData[3] shr 6) and 3;
      AFrame.ModeExtensionID:= (AHeaderData[3] shr 4) and 3;
      AFrame.CopyrightBit:= ((AHeaderData[3] shr 3) and 1) = 1;
      AFrame.OriginalBit:= ((AHeaderData[3] shr 2) and 1) = 1;
      AFrame.EmphasisID:= AHeaderData[3] and 3;
  end;
  function IsFrameHeader(const AHeaderData: array of Byte): Boolean;
  begin
      if ((AHeaderData[0] and $FF) <> $FF) or
         ((AHeaderData[1] and $E0) <> $E0) or
         (((AHeaderData[1] shr 3) and 3) = 1) or
         (((AHeaderData[1] shr 1) and 3) = 0) or
         ((AHeaderData[2] and $F0) = $F0) or
         ((AHeaderData[2] and $F0) = 0) or
         (((AHeaderData[2] shr 2) and 3) = 3) or
         ((AHeaderData[3] and 3) = 2) then Result:= False
      else Result:= True;
  end;
  function ValidFrameAt(const AIndex: Word; AData: array of Byte): Boolean;
  var HeaderData: array [1..4] of Byte;
  begin
      HeaderData[1]:= AData[AIndex];
      HeaderData[2]:= AData[AIndex + 1];
      HeaderData[3]:= AData[AIndex + 2];
      HeaderData[4]:= AData[AIndex + 3];
      Result:= IsFrameHeader(HeaderData);
  end;

  function FindVendorID(const AData: array of Byte; ASize: Word): string;
  var Iterator: Integer;
      VendorID: string;
  begin
      Result:= '';
      if (SizeOf(AData) - ASize - 8) < 0 then ASize:= SizeOf(AData) - 8;
      for Iterator:= 0 to ASize do begin
        VendorID:= Chr(AData[SizeOf(AData) - Iterator - 8]) + Chr(AData[SizeOf(AData) - Iterator - 7]) + Chr(AData[SizeOf(AData) - Iterator - 6]) + Chr(AData[SizeOf(AData) - Iterator - 5]);
        if VendorID = VENDOR_ID_LAME then begin
          Result:= VendorID + Chr(AData[SizeOf(AData) - Iterator - 4]) + Chr(AData[SizeOf(AData) - Iterator - 3]) + Chr(AData[SizeOf(AData) - Iterator - 2]) + Chr(AData[SizeOf(AData) - Iterator - 1]);
          break;
        end;
        if VendorID = VENDOR_ID_GOGO_NEW then begin
          Result:= VendorID;
          break;
        end;
      end;
  end;

  function FindFrame(const AData: array of Byte; var AVBR: TVBRData): TFrameData;
  var HeaderData: array [1..4] of Byte;
      Iterator, VBRIdx: Integer;
      VBRDeviation: Byte;
  begin
      { Search for valid frame }
      FillChar(Result, SizeOf(Result), 0);
      Move(AData, HeaderData, SizeOf(HeaderData));
      for Iterator:= 0 to SizeOf(AData) - MAX_MPEG_FRAME_LENGTH do begin
        { Decode data if frame header found }
        if IsFrameHeader(HeaderData) then begin
          DecodeHeader(HeaderData, Result);
          { Check for next frame and try to find VBR header }
          VBRIdx:= Iterator + GetFrameLength(Result);

          if (VBRIdx < SizeOf(AData)) and ValidFrameAt(VBRIdx, AData) then begin
            Result.Found:= true;
            Result.Position:= Iterator;
            Result.Size:= GetFrameLength(Result);

            VBRDeviation:= Iterator + SizeOf(HeaderData);

            Result.Xing:= (AData[VBRDeviation] = 0) and (AData[VBRDeviation + 1] = 0) and (AData[VBRDeviation + 2] = 0) and (AData[VBRDeviation + 3] = 0) and (AData[VBRDeviation + 4] = 0) and (AData[VBRDeviation + 5] = 0);

            if Result.VersionID = MPEG_VERSION_1 then
              if Result.ModeID <> MPEG_CM_MONO then VBRDeviation:= 36 else VBRDeviation:= 21
            else
              if Result.ModeID <> MPEG_CM_MONO then VBRDeviation:= 21 else VBRDeviation:= 13;

            VBRDeviation:= Iterator + VBRDeviation;

            FillChar(AVBR, SizeOf(AVBR), 0);
            if Chr(AData[VBRDeviation]) + Chr(AData[VBRDeviation + 1]) + Chr(AData[VBRDeviation + 2]) + Chr(AData[VBRDeviation + 3]) = VBR_ID_XING then begin
              { Extract Xing VBR info at given position }
              FillChar(AVBR, SizeOf(AVBR), 0);
              AVBR.Found:= true;
              AVBR.ID:= VBR_ID_XING;
              AVBR.Frames:= AData[VBRDeviation + 8] * $1000000 + AData[VBRDeviation + 9] * $10000 + AData[VBRDeviation + 10] * $100 + AData[VBRDeviation + 11];
              AVBR.Bytes:= AData[VBRDeviation + 12] * $1000000 + AData[VBRDeviation + 13] * $10000 + AData[VBRDeviation + 14] * $100 + AData[VBRDeviation + 15];
              AVBR.Scale:= AData[VBRDeviation + 119];
              { Vendor ID can be not present }
              AVBR.VendorID:= Chr(AData[VBRDeviation + 120]) + Chr(AData[VBRDeviation + 121]) + Chr(AData[VBRDeviation + 122]) + Chr(AData[VBRDeviation + 123]) + Chr(AData[VBRDeviation + 124]) + Chr(AData[VBRDeviation + 125]) + Chr(AData[VBRDeviation + 126]) + Chr(AData[VBRDeviation + 127]);
            end;
            if Chr(AData[VBRDeviation]) + Chr(AData[VBRDeviation + 1]) + Chr(AData[VBRDeviation + 2]) + Chr(AData[VBRDeviation + 3]) = VBR_ID_FHG then begin
              { Extract FhG VBR info at given position }
              FillChar(AVBR, SizeOf(AVBR), 0);
              AVBR.Found:= true;
              AVBR.ID:= VBR_ID_FHG;
              AVBR.Scale:= AData[VBRDeviation + 9];
              AVBR.Bytes:= AData[VBRDeviation + 10] * $1000000 + AData[VBRDeviation + 11] * $10000 + AData[VBRDeviation + 12] * $100 + AData[VBRDeviation + 13];
              AVBR.Frames:= AData[VBRDeviation + 14] * $1000000 + AData[VBRDeviation + 15] * $10000 + AData[VBRDeviation + 16] * $100 + AData[VBRDeviation + 17];
            end;

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

  function GetEncoderID(Frame: TFrameData; VBR: TVBRData; VendorID: string): Byte;
  begin
      Result:= 0;
      if Frame.Found then
        if VBR.Found then begin
          if Copy(VBR.VendorID, 1, 4) = VENDOR_ID_LAME then Result:= MPEG_ENCODER_LAME;
          if Copy(VBR.VendorID, 1, 4) = VENDOR_ID_GOGO_NEW then Result:= MPEG_ENCODER_GOGO;
          if Copy(VBR.VendorID, 1, 4) = VENDOR_ID_GOGO_OLD then Result:= MPEG_ENCODER_GOGO;
          if (VBR.ID = VBR_ID_XING) and (Copy(VBR.VendorID, 1, 4) <> VENDOR_ID_LAME) and (Copy(VBR.VendorID, 1, 4) <> VENDOR_ID_GOGO_NEW) and (Copy(VBR.VendorID, 1, 4) <> VENDOR_ID_GOGO_OLD) then Result:= MPEG_ENCODER_XING;
          if VBR.ID = VBR_ID_FHG then Result:= MPEG_ENCODER_FHG;
          if (Copy(VendorID, 1, 4) = VENDOR_ID_LAME) then Result:= MPEG_ENCODER_LAME;
        end
        else begin
          Result:= MPEG_ENCODER_FHG;
          if (Frame.OriginalBit) and (Frame.ProtectionBit) then Result:= MPEG_ENCODER_LAME;
          if (MPEG_BIT_RATE[Frame.VersionID, Frame.LayerID, Frame.BitRateID] <= 160) and (Frame.ModeID = MPEG_CM_STEREO) then Result:= MPEG_ENCODER_BLADE;
          if (Frame.CopyrightBit) and (Frame.OriginalBit) and (not Frame.ProtectionBit) then Result:= MPEG_ENCODER_XING;
          if (Frame.Xing) and (Frame.OriginalBit) then Result:= MPEG_ENCODER_XING;
          if Frame.LayerID = MPEG_LAYER_II then Result:= MPEG_ENCODER_QDESIGN;
          if (Frame.ModeID = MPEG_CM_DUAL_CHANNEL) and (Frame.ProtectionBit) then Result:= MPEG_ENCODER_SHINE;
          if Copy(VendorID, 1, 4) = VENDOR_ID_LAME then Result:= MPEG_ENCODER_LAME;
          if Copy(VendorID, 1, 4) = VENDOR_ID_GOGO_NEW then Result:= MPEG_ENCODER_GOGO;
        end;
  end;

var SourceFile: PStream;
    Data: array [1..MAX_MPEG_FRAME_LENGTH * 2] of Byte;
    Transferred: DWORD;
    Position : Int64;
    Frame: TFrameData;
    MPEGStart, MPEGEnd: Int64;
    Encoder, VendorID, VendID: string;
    VBR: TVBRData;
    EncoderID: Byte;
    Frames: Integer;
begin
    with TAudioMPEG(AudioInfo^) do begin
      Result:= False;
      try
        ResetData;
        Frame.VersionID:= MPEG_VERSION_UNKNOWN;
        Frame.SampleRateID:= MPEG_SAMPLE_RATE_UNKNOWN;
        Frame.ModeID:= MPEG_CM_UNKNOWN;
        Frame.ModeExtensionID:= MPEG_CM_EXTENSION_UNKNOWN;
        Frame.EmphasisID:= MPEG_EMPHASIS_UNKNOWN;
        MPEGStart:= 0;
        MPEGEnd:= 0;
        try
          SourceFile:= OpenFileRead(AFileName);
          FFileSize:= SourceFile.Size;

          TagInfo[tID3v2].ReadFromFile(AFileName);
          TagInfo[tID3v1].ReadFromFile(AFileName);
          TagInfo[tAPE].ReadFromFile(AFileName);

          Position:= TagInfo[tID3v2].TagSize;
          SourceFile.Seek(Position, spBegin);

          Transferred:= SourceFile.Read(Data, SizeOf(Data));
          Frame:= FindFrame(Data, VBR);
          // Search for vendor ID at the beginning
          VendorID:= FindVendorID(Data, Frame.Size * 5);

          { Try to find the first frame if no frame at the beginning found ]}
          if (not Frame.Found) and (Transferred = SizeOf(Data)) then
            repeat
              Transferred:= SourceFile.Read(Data, SizeOf(Data));
              Inc(Position, Transferred);
              Frame:= FindFrame(Data, VBR);
            until (Frame.Found) or (Transferred < SizeOf(Data));

          if Frame.Found then begin
            Frame.Position:= Position + Frame.Position;
            MPEGStart:= Frame.Position;
            MPEGEnd:= FFileSize - TagInfo[tID3v1].TagSize + PTagID3v1(TagInfo[tID3v1]).Lyrics.TagSize + TagInfo[tAPE].TagSize;
          end;

          { Search for vendor ID at the end if CBR encoded }
          if (Frame.Found) and (VendorID = '') then begin
            Position:= FFileSize - SizeOf(Data) - TagInfo[tID3v1].TagSize + PTagID3v1(TagInfo[tID3v1]).Lyrics.TagSize + TagInfo[tAPE].TagSize;
            SourceFile.Seek(Position, spBegin);
            SourceFile.Read(Data, SizeOf(Data));
            VendorID:= FindVendorID(Data, Frame.Size * 5);
          end;

          if not Frame.Found then Exit;

          if (VBR.Found) and (VBR.Frames > 0) then FBitRate:= Round((VBR.Bytes / VBR.Frames - GetPadding(Frame)) * MPEG_SAMPLE_RATE[Frame.VersionID, Frame.SampleRateID] / GetCoefficient(Frame) / 1000)
          else FBitRate:= MPEG_BIT_RATE[Frame.VersionID, Frame.LayerID, Frame.BitRateID];

          FSampleRate:= MPEG_SAMPLE_RATE[Frame.VersionID, Frame.SampleRateID];

          EncoderID:= GetEncoderID(Frame, VBR, VendorID);
          FBits:= 16;

          Encoder:= MPEG_ENCODER[EncoderID];
          if VBR.VendorID <> '' then VendID:= VBR.VendorID;
          if VendorID <> '' then VendID:= VendorID;
          if (EncoderID = MPEG_ENCODER_LAME) and (Length(VendID) >= 8) and
             (VendID[5] in ['0'..'9']) and (VendID[6] = '.') and (VendID[7] in ['0'..'9']) and (VendID[8] in ['0'..'9']) then
            Encoder:= Encoder + #32 + VendID[5] + VendID[6] + VendID[7] + VendID[8];


  //        if Frame.Found then begin
            if VBR.Found then begin
              FBitrateType:= btVBR;
              Frames:= VBR.Frames;
            end
            else begin
              FBitrateType:= btCBR;
              Frames:= (MPEGEnd - MPEGStart - Frame.Position) div GetFrameLength(Frame);
            end;

            if (VBR.Found) and (VBR.Frames > 0) then begin
              FSamples:= VBR.Frames * GetCoefficient(Frame) * 8;
              FDuration:= FSamples / MPEG_SAMPLE_RATE[Frame.VersionID, Frame.SampleRateID];
            end
            else begin
              FSamples:= Frames * GetCoefficient(Frame) * 8;
              FDuration:= (MPEGEnd - MPEGStart - Frame.Position) / MPEG_BIT_RATE[Frame.VersionID, Frame.LayerID, Frame.BitRateID] / 1000 * 8;
            end;

            {$IFDEF USE_OtherAudioProps}
            OtherProps.Add('Version='+MPEG_VERSION[Frame.VersionID]);
            OtherProps.Add('Layer='+MPEG_LAYER[Frame.LayerID]);
            OtherProps.Add('Emphasis='+MPEG_EMPHASIS[Frame.EmphasisID]);
            {$ENDIF}
            FChannelMode:= MPEG_CM_MODE[Frame.ModeID];
            case Frame.ModeID of
               3: FChannels:= 1;
               0, 1, 2: FChannels:= 2;
               else FChannels:= 0;
            end;

  //        end;
          {$IFDEF USE_OtherAudioProps}
          OtherProps.Add('Encoder='+Encoder);
          OtherProps.Add('EncoderID='+Int2Str(EncoderID));
          OtherProps.Add('Frames='+Int2Str(Frames));
          {$ENDIF}
          FValid:= (Frame.Found) and (FBitrate >= MIN_MPEG_BIT_RATE) and (FBitrate <= MAX_MPEG_BIT_RATE) and (FDuration >= MIN_ALLOWED_DURATION);

          if Valid then begin
             if Frame.ModeID = MPEG_CM_MONO then FRatio:= FFileSize / (FSamples * (1 * FBits / 8) + 44) * 100
             else FRatio:= FFileSize / (FSamples * (2 * FBits / 8) + 44) * 100;
          end;

          Result:= true;
        finally
          SourceFile.Free;
        end;
      except
      end;
    end;  
end;

end.
