summaryrefslogtreecommitdiff
path: root/plugins/Watrack/formats/fmt_ape.pas
blob: fbabdac19c691c992d3d2f0ef479b7d597fe59cf (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
{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.