unit AudioFLAC;

interface

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

type TFLACAudioInfo = PAudioInfo;

function NewAudioFLAC: TFLACAudioInfo;

implementation

uses ATLLib;

const
    STREAMINFO = 0;

type
  TAudioFLAC = object(TAudioInfo)
  private
  end;

function AudioFLACReader(const AFileName: KOLstring; AudioInfo: TFLACAudioInfo): Boolean; forward;

function NewAudioFLAC: TFLACAudioInfo;
begin
    New(Result, Create);
    with TAudioFLAC(Result^) do begin
      TagAdd(tID3v1, NewTagID3v1);
      TagAdd(tID3v2, NewTagID3v2);
      TagAdd(tVorbis, NewTagVorbis);
      FAudioVendor:= aFLAC;
      FExistsProps:= [apSampleRate, apBitrate, apChannels, apBits, apSamples, apDuration, apRatio];
      FReader:= AudioFLACReader;
    end;
end;

function AudioFLACReader(const AFileName: KOLstring; AudioInfo: TFLACAudioInfo): Boolean;
var SourceFile: PStream;
    BlockType: Byte;
    Last: Boolean;
    AudioOffset, BlockLength: Cardinal;
    Buff: array [1..18] of Byte;
begin
    with TAudioFLAC(AudioInfo^) do begin
      Result:= False;
      try
        ResetData;
        SourceFile:= OpenFileRead(AFileName);

        TagInfo[tID3v1].ReadFromFile(AFileName);
        TagInfo[tID3v2].ReadFromFile(AFileName);
        TagInfo[tVorbis].ReadFromFile(AFileName);
        try
          FFileSize:= SourceFile.Size;

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

          if fLaC_ID <> SourceFile.ReadStrLen(SizeOf(fLaC_ID)) then Exit;
          FValid:= True;

          BlockLength:= 0;
          repeat
            SourceFile.Seek(BlockLength, spCurrent);
            AudioOffset:= SourceFile.Position;

            SourceFile.Read(Buff, 4);
            Last:= (Buff[1] shr 7) = 1;
            BlockType:= Buff[1] and $7F;
            BlockLength:= (Buff[2] shl 16) + (Buff[3] shl 8) + Buff[4];
            if (BlockType = STREAMINFO) then begin
              AudioOffset:= SourceFile.Position;
              SourceFile.Read(Buff, 18);
              SourceFile.Position:= AudioOffset;
            end
          until (SourceFile.Position >= SourceFile.Size) or Last;
          AudioOffset:= AudioOffset + BlockLength + TagInfo[tID3v1].TagSize + PTagID3v1(TagInfo[tID3v1]).Lyrics.TagSize;

          FChannels:= (Buff[13] shr 1) and $7 + 1;
          FSampleRate:= (Buff[11] shl 12) + (Buff[12] shl 4) + (Buff[13] shr 4);
          FBits:= ((Buff[13] and 1) shl 4) + (Buff[14] shr 4) + 1;
          FSamples:= (Buff[15] shl 24) + (Buff[16] shl 16) + (Buff[17] shl 8) + Buff[18];

          FDuration:= FSamples / FSampleRate;
          FBitrate:= Round(((FFileSize - AudioOffset) / 1000) * 8 / FDuration);
          FRatio:= (FFileSize - AudioOffset) / (FSamples * FChannels * FBits / 8) * 100;

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

end.
