{APE file}
unit fmt_APE;
{$include compilers.inc}

interface
uses wat_api;

function ReadAPE(var Info:tSongInfo):boolean; cdecl;

implementation

uses windows,common,io,tags,srv_format;

const
  defID = $2043414D;
type
(* Old Version ?
  tMonkeyHeader = record
    ID              :dword; { Always "MAC " }
    VersionID       :word;  { Version number * 1000 (3.91 = 3910) }
    CompressionID   :word;  { Compression level code }
    Flags           :word;  { Any format flags }
    Channels        :word;  { Number of channels }
    SampleRate      :dword; { Sample rate (hz) }
    HeaderBytes     :dword; { Header length (without header ID) }
    TerminatingBytes:dword; { Extended data }
    Frames          :dword; { Number of frames in the file }
    FinalSamples    :dword; { Number of samples in the final frame }
    PeakLevel       :dword; { Peak level (if stored) }
    SeekElements    :dword; { Number of seek elements (if stored) }
  end;
*)
  tMonkeyHeader = packed record
    ID               :dword; // should equal 'MAC '
    VersionID        :dword; // version number * 1000 (3.81 = 3810)
    nDescriptorBytes :dword; // descriptor bytes
    nHeaderBytes     :dword; // APEHeader bytes
    nSeekTableBytes  :dword; // bytes of the seek table
    nHeaderDataBytes :dword; // header data bytes (from original file)
    nFrameDataBytes  :dword; // bytes of APE frame data
    nFrameDataBytesHi:dword; // the high order number of APE frame data bytes
    nTerminatingBytes:dword; // the terminating data of the file (w/o tag data)
    cFileMD5:array [0..15] of byte;
  end;
type
  tAPEHeader = packed record
    nCompressionLevel:word;  // the compression level
    nFormatFlags     :word;  // any format flags (for future use)
    nBlocksPerFrame  :dword; // the number of audio blocks in one frame
    nFinalFrameBlocks:dword; // the number of audio blocks in the final frame
    nTotalFrames     :dword; // the total number of frames
    nBitsPerSample   :word;  // the bits per sample (typically 16)
    nChannels        :word;  // the number of channels (1 or 2)
    nSampleRate      :dword; // the sample rate (typically 44100)
  end;

const
  MONKEY_COMPRESSION_FAST       = 1000; // Fast (poor)
  MONKEY_COMPRESSION_NORMAL     = 2000; // Normal (good)
  MONKEY_COMPRESSION_HIGH       = 3000; // High (very good)
  MONKEY_COMPRESSION_EXTRA_HIGH = 4000; // Extra high (best)
const
  MONKEY_FLAG_8_BIT          = 1;  // Audio 8-bit
  MONKEY_FLAG_CRC            = 2;  // New CRC32 error detection
  MONKEY_FLAG_PEAK_LEVEL     = 4;  // Peak level stored
  MONKEY_FLAG_24_BIT         = 8;  // Audio 24-bit
  MONKEY_FLAG_SEEK_ELEMENTS  = 16; // Number of seek elements stored
  MONKEY_FLAG_WAV_NOT_STORED = 32; // WAV header not stored

function ReadAPE(var Info:tSongInfo):boolean; cdecl;
var
  f:THANDLE;
  hdr:tMonkeyHeader;
  hdr1:tAPEHeader;
  blocks:dword;
begin
  result:=false;
  f:=Reset(Info.mfile);
  if f=THANDLE(INVALID_HANDLE_VALUE) then
    exit;
  ReadID3v2(f,Info);
  BlockRead(f,hdr ,SizeOf(tMonkeyHeader));
  BlockRead(f,hdr1,SizeOf(tAPEHeader)); //hdr.nHeaderBytes
  if hdr1.nTotalFrames=0 then
    blocks:=0
  else
    blocks:=(hdr1.nTotalFrames-1)*hdr1.nBlocksPerFrame+hdr1.nFinalFrameBlocks;
  Info.khz     :=hdr1.nSampleRate div 1000;
  if hdr1.nSampleRate<>0 then
    Info.total :=blocks div hdr1.nSampleRate;
  Info.channels:=hdr1.nChannels;
//  Info.kbps:=Info.khz*deep*Info.channels/1152
//  Info.kbps:=(blocks*Info.channels*hdr1.nBitsPerSample) div (Info.total*8000);
//  Info.kbps    :=((hdr1.nBitsPerSample div 8)*hdr1.nSamplerate) div 1000;
(* Old version ?
  if (hdr.ID<>DefID) or (hdr.SampleRate=0) or (hdr.Channels=0) then
    exit;
  if (hdr.VersionID>=3900) or
    ((hdr.VersionID>=3800) and
    (hdr.CompressionID=MONKEY_COMPRESSION_EXTRA_HIGH)) then
    tmp:=73728
  else
    tmp:=9216;
  tmp:=(hdr.Frames-1)*tmp+hdr.FinalSamples;
  Info.total   :=tmp div hdr.SampleRate;
  Info.khz     :=hdr.SampleRate div 1000;
  Info.channels:=hdr.Channels;

  Info.kbps:=tmp;//samples
  if      (hdr.Flags and MONKEY_FLAG_8_BIT)<>0 then tmp:=8
  else if (hdr.Flags and MONKEY_FLAG_24_BIT)<>0 then tmp:=24
  else tmp:=16;
  Info.kbps:=((Info.kbps*tmp*hdr.Channels) div Info.Total) div 1000;
*)
  ReadAPEv2(f,Info);
  ReadID3v1(f,Info);
  CloseHandle(f);
  result:=true;
end;

var
  LocalFormatLink:twFormat;

procedure InitLink;
begin
  LocalFormatLink.Next:=FormatLink;

  LocalFormatLink.This.proc :=@ReadAPE;
  LocalFormatLink.This.ext  :='APE';
  LocalFormatLink.This.flags:=0;

  FormatLink:=@LocalFormatLink;
end;

initialization
  InitLink;
end.