unit ATLCommon;

// For used TAudioInfo.OtherProps define directive USE_OtherAudioProps
// in project options or moduls
//{$DEFINE USE_OtherAudioProps}

interface

uses Windows, KOL;

type
  TEasyTags = (etTrack, etTitle, etArtist, etAlbum, etYear, etComment, etGenre,
               etComposer, etEncoder, etCopyright, etLink, etDescription, etLanguage,
               etLyrics, etPerformer, etImageLink, etOriginalFile, etRating, etVersion);

  TExistsTags = Set of TEasyTags;

  TTagVendor = (tUnknown, tID3v1, tID3v1Lyrics, tID3v2, tAPE, tVorbis, tTwinVQ, tWMA, tExWMA, tMp4);

  PTagInfo = ^TTagInfo;

  TTagReader = function(const AFileName: KOLstring): Boolean of object;

  TTagInfo = object(TObj)
  private
  protected
    FTagVendor: TTagVendor;
    FTagList: PStrListEx;
    FExists: Boolean;
    FExistsTags: TExistsTags;
    FGenreID: Byte;
    FTagSize: Cardinal;
    function GetTag(const Index: TEasyTags): String; virtual;
    procedure SetTag(const Index: TEasyTags; const Value: String); virtual;

  public
    constructor Create;
    destructor Destroy; virtual;

    procedure ResetData; virtual;
    function ReadFromFile(const AFileName: KOLstring): Boolean; virtual;
    function SaveToFile(const AFileName: KOLstring): Boolean; virtual;
    function RemoveFromFile(const AFileName: KOLstring): Boolean; virtual;

    property Exists: Boolean read FExists;
    property TagSize: Cardinal read FTagSize;

    property TagVendor: TTagVendor read FTagVendor;
    property Version: String index etVersion read GetTag;
    property Track: String index etTrack read GetTag write SetTag;
    property Title: String index etTitle read GetTag write SetTag;
    property Artist: String index etArtist read GetTag write SetTag;
    property Album: String index etAlbum read GetTag write SetTag;
    property Year: String index etYear read GetTag write SetTag;
    property Comment: String index etComment read GetTag write SetTag;
    property Genre: String index etGenre read GetTag write SetTag;
    property Composer: String index etComposer read GetTag write SetTag;
    property Encoder: String index etEncoder read GetTag write SetTag;
    property Copyright: String index etCopyright read GetTag write SetTag;
    property Link: String index etLink read GetTag write SetTag;
    property Description: String index etDescription read GetTag write SetTag;
    property Performer: String index etPerformer read GetTag write SetTag;
    property GenreID: Byte read FGenreID write FGenreID;

    property Tags[const Index: TEasyTags]: String read GetTag write SetTag;
    property ExistsTags: TExistsTags read FExistsTags;
  end;


type
  TAudioVendor = (aWav, aWAVPack, aMonkey, aOGG, aAC3, aAAC, aDTS, aTTA, aWMA,
                  aOptimFrog, aFLAC, aTwinVQ, aCDATrack, aMPEG, aMPEGplus, aMP4, aUnknown);
  TAudioVendors = set of TAudioVendor;

  TBitrateType = (btUnknown, btCBR, btVBR);
  TAudioProps = (apSampleRate, apBitrate, apChannels, apBits, apSamples, apDuration, apRatio, apBitrateType);

  TExistsProps = Set of TAudioProps;

  TTagExists = set of TTagVendor;

  PAudioInfo = ^TAudioInfo;
  TFileReader = function (const AFileName: KOLstring; AudioInfo: PAudioInfo): Boolean;
  TAudioInfo = object(TObj)
  private
    FTagInfoList: PList;
    function GetTagInfo(Index: TTagVendor): PTagInfo;
    function GetChannelMode: String;
  protected
    FReader: TFileReader;
    FTagExists: TTagExists;
    FExistsProps: TExistsProps;
    FAudioVendor: TAudioVendor;
    FValid: Boolean;
    FBits, FChannels: Word;
    FSamples, FFileSize: Int64;
    FBitrate, FSampleRate, FCRC: Cardinal;
    FDuration, FRatio: Double;
    FBitrateType: TBitrateType;
    FChannelMode: String;

  public
    {$IFDEF USE_OtherAudioProps}
    OtherProps: PStrList;
    {$ENDIF}
    constructor Create;
    destructor Destroy; virtual;

    procedure ResetData;
    function ReadFromFile(const AFileName: KOLstring): Boolean;

    property ExistsProps: TExistsProps read FExistsProps;
    property Valid: Boolean read FValid;
    property AudioVendor: TAudioVendor read FAudioVendor;

    property FileSize: Int64 read FFileSize;

    property Channels: Word read FChannels;
    property Bits: Word read FBits;
    property SampleRate: Cardinal read FSampleRate;
    property Samples: Int64 read FSamples;
    property Bitrate: Cardinal read FBitrate;
    property Duration: Double read FDuration;
    property Ratio: Double read FRatio;
    property BitrateType: TBitrateType read FBitrateType;
    property ChannelMode: String read GetChannelMode;

    property TagExists: TTagExists read FTagExists;
    property TagInfo[Index: TTagVendor]: PTagInfo read GetTagInfo;
    procedure TagAdd(Index: TTagVendor; AudioInfo: Pointer);
  end;

implementation

uses AtlLib;

{ TTagInfo }

procedure TTagInfo.SetTag(const Index: TEasyTags; const Value: String);
begin
    FTagList.Items[Integer(Index)]:= Value;
end;

destructor TTagInfo.Destroy;
begin
    FTagList.Free;
    inherited;
end;

procedure TTagInfo.ResetData;
var i: TEasyTags;
begin
    FExists:= False;
    FGenreID:= 255;
    FTagSize:= 0;
    FTagList.Clear;
    for i:= Low(TEasyTags) to High(TEasyTags) do FTagList.Add('');
end;

constructor TTagInfo.Create;
begin
    inherited Create;
    FTagList:= NewStrListEx;
    FExistsTags:= [];
    FTagVendor:= tUnknown;
    ResetData;
end;

function TTagInfo.GetTag(const Index: TEasyTags): String;
begin
    Result:= FTagList.Items[Integer(Index)];
end;

function TTagInfo.ReadFromFile(const AFileName: KOLstring): Boolean;
begin
    Result:= False;
end;

function TTagInfo.RemoveFromFile(const AFileName: KOLstring): Boolean;
begin
    Result:= False;
end;

function TTagInfo.SaveToFile(const AFileName: KOLstring): Boolean;
begin
    Result:= False;
end;

{ TAudioInfo }

procedure TAudioInfo.TagAdd(Index: TTagVendor; AudioInfo: Pointer);
begin
    FTagInfoList.Items[Integer(Index)]:= AudioInfo;
    FTagExists:= FTagExists + [Index];
end;

constructor TAudioInfo.Create;
var i: TTagVendor;
begin
    inherited;
    {$IFDEF USE_OtherAudioProps}
    OtherProps:= NewStrList;
    {$ENDIF}
    FTagInfoList:= NewList;
    for i:= Low(TTagVendor) to High(TTagVendor) do FTagInfoList.Add(nil);
    FAudioVendor:= aUnknown;
    FExistsProps:= [];
    FTagExists:= [];
    FReader:= nil;
    ResetData;
end;

destructor TAudioInfo.Destroy;
var i: TTagVendor;
begin
    FChannelMode:= '';
    for i:= Low(TTagVendor) to High(TTagVendor) do if TagInfo[i] <> nil then TagInfo[i].Free;
    FTagInfoList.Free;
    {$IFDEF USE_OtherAudioProps}
    OtherProps.Free;
    {$ENDIF}
    inherited;
end;

function TAudioInfo.GetChannelMode: String;
const  Channel_Mode: array [0..3] of string = ('Unknown', 'Mono', 'Stereo', 'Multichannel');
begin
    if FChannelMode <> '' then Result:= FChannelMode
    else
      if FChannels > 2 then Result:= Channel_Mode[3] else Result := Channel_Mode[FChannels];
end;

function TAudioInfo.GetTagInfo(Index: TTagVendor): PTagInfo;
begin
    Result:= FTagInfoList.Items[Integer(Index)];
end;

function TAudioInfo.ReadFromFile(const AFileName: KOLstring): Boolean;
begin
    Result:= False;
    if Assigned(FReader) then Result:= FReader(AFileName, @Self);
end;

procedure TAudioInfo.ResetData;
var i: TTagVendor;
begin
     {$IFDEF USE_OtherAudioProps}
    OtherProps.Clear;
    {$ENDIF}
    for i:= Low(TTagVendor) to High(TTagVendor) do if TagInfo[i] <> nil then TagInfo[i].ResetData;

    FValid:= False;
    FFileSize:= 0;
    FBits:= 0;
    FSampleRate:= 0;
    FChannels:= 0;
    FBitrate:= 0;
    FDuration:= -1;
    FRatio:= -1;
    FBitrateType:= btUnknown;
    FSamples:= 0;
    FCRC:= 0;
    FChannelMode:= '';
end;

end.
