1 /***************************************************************************
2 copyright : (C) 2010 by Alex Novichkov
3 email : novichko@atnet.ru
4
5 copyright : (C) 2006 by Lukáš Lalinský
6 email : lalinsky@gmail.com
7 (original WavPack implementation)
8 ***************************************************************************/
9
10 /***************************************************************************
11 * This library is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU Lesser General Public License version *
13 * 2.1 as published by the Free Software Foundation. *
14 * *
15 * This library is distributed in the hope that it will be useful, but *
16 * WITHOUT ANY WARRANTY; without even the implied warranty of *
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
18 * Lesser General Public License for more details. *
19 * *
20 * You should have received a copy of the GNU Lesser General Public *
21 * License along with this library; if not, write to the Free Software *
22 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
23 * 02110-1301 USA *
24 * *
25 * Alternatively, this file is available under the Mozilla Public *
26 * License Version 1.1. You may obtain a copy of the License at *
27 * http://www.mozilla.org/MPL/ *
28 ***************************************************************************/
29
30 #include <tstring.h>
31 #include <tdebug.h>
32 #include <bitset>
33 #include "id3v2tag.h"
34 #include "apeproperties.h"
35 #include "apefile.h"
36 #include "apetag.h"
37 #include "apefooter.h"
38
39 using namespace TagLib;
40
41 class APE::Properties::PropertiesPrivate
42 {
43 public:
PropertiesPrivate()44 PropertiesPrivate() :
45 length(0),
46 bitrate(0),
47 sampleRate(0),
48 channels(0),
49 version(0),
50 bitsPerSample(0),
51 sampleFrames(0) {}
52
53 int length;
54 int bitrate;
55 int sampleRate;
56 int channels;
57 int version;
58 int bitsPerSample;
59 unsigned int sampleFrames;
60 };
61
62 ////////////////////////////////////////////////////////////////////////////////
63 // public members
64 ////////////////////////////////////////////////////////////////////////////////
65
Properties(File *,ReadStyle style)66 APE::Properties::Properties(File *, ReadStyle style) :
67 AudioProperties(style),
68 d(new PropertiesPrivate())
69 {
70 debug("APE::Properties::Properties() -- This constructor is no longer used.");
71 }
72
Properties(File * file,long streamLength,ReadStyle style)73 APE::Properties::Properties(File *file, long streamLength, ReadStyle style) :
74 AudioProperties(style),
75 d(new PropertiesPrivate())
76 {
77 read(file, streamLength);
78 }
79
~Properties()80 APE::Properties::~Properties()
81 {
82 delete d;
83 }
84
length() const85 int APE::Properties::length() const
86 {
87 return lengthInSeconds();
88 }
89
lengthInSeconds() const90 int APE::Properties::lengthInSeconds() const
91 {
92 return d->length / 1000;
93 }
94
lengthInMilliseconds() const95 int APE::Properties::lengthInMilliseconds() const
96 {
97 return d->length;
98 }
99
bitrate() const100 int APE::Properties::bitrate() const
101 {
102 return d->bitrate;
103 }
104
sampleRate() const105 int APE::Properties::sampleRate() const
106 {
107 return d->sampleRate;
108 }
109
channels() const110 int APE::Properties::channels() const
111 {
112 return d->channels;
113 }
114
version() const115 int APE::Properties::version() const
116 {
117 return d->version;
118 }
119
bitsPerSample() const120 int APE::Properties::bitsPerSample() const
121 {
122 return d->bitsPerSample;
123 }
124
sampleFrames() const125 unsigned int APE::Properties::sampleFrames() const
126 {
127 return d->sampleFrames;
128 }
129
130 ////////////////////////////////////////////////////////////////////////////////
131 // private members
132 ////////////////////////////////////////////////////////////////////////////////
133
134 namespace
135 {
headerVersion(const ByteVector & header)136 int headerVersion(const ByteVector &header)
137 {
138 if(header.size() < 6 || !header.startsWith("MAC "))
139 return -1;
140
141 return header.toUShort(4, false);
142 }
143 }
144
read(File * file,long streamLength)145 void APE::Properties::read(File *file, long streamLength)
146 {
147 // First, we assume that the file pointer is set at the first descriptor.
148 long offset = file->tell();
149 int version = headerVersion(file->readBlock(6));
150
151 // Next, we look for the descriptor.
152 if(version < 0) {
153 offset = file->find("MAC ", offset);
154 file->seek(offset);
155 version = headerVersion(file->readBlock(6));
156 }
157
158 if(version < 0) {
159 debug("APE::Properties::read() -- APE descriptor not found");
160 return;
161 }
162
163 d->version = version;
164
165 if(d->version >= 3980)
166 analyzeCurrent(file);
167 else
168 analyzeOld(file);
169
170 if(d->sampleFrames > 0 && d->sampleRate > 0) {
171 const double length = d->sampleFrames * 1000.0 / d->sampleRate;
172 d->length = static_cast<int>(length + 0.5);
173 d->bitrate = static_cast<int>(streamLength * 8.0 / length + 0.5);
174 }
175 }
176
analyzeCurrent(File * file)177 void APE::Properties::analyzeCurrent(File *file)
178 {
179 // Read the descriptor
180 file->seek(2, File::Current);
181 const ByteVector descriptor = file->readBlock(44);
182 if(descriptor.size() < 44) {
183 debug("APE::Properties::analyzeCurrent() -- descriptor is too short.");
184 return;
185 }
186
187 const unsigned int descriptorBytes = descriptor.toUInt(0, false);
188
189 if((descriptorBytes - 52) > 0)
190 file->seek(descriptorBytes - 52, File::Current);
191
192 // Read the header
193 const ByteVector header = file->readBlock(24);
194 if(header.size() < 24) {
195 debug("APE::Properties::analyzeCurrent() -- MAC header is too short.");
196 return;
197 }
198
199 // Get the APE info
200 d->channels = header.toShort(18, false);
201 d->sampleRate = header.toUInt(20, false);
202 d->bitsPerSample = header.toShort(16, false);
203
204 const unsigned int totalFrames = header.toUInt(12, false);
205 if(totalFrames == 0)
206 return;
207
208 const unsigned int blocksPerFrame = header.toUInt(4, false);
209 const unsigned int finalFrameBlocks = header.toUInt(8, false);
210 d->sampleFrames = (totalFrames - 1) * blocksPerFrame + finalFrameBlocks;
211 }
212
analyzeOld(File * file)213 void APE::Properties::analyzeOld(File *file)
214 {
215 const ByteVector header = file->readBlock(26);
216 if(header.size() < 26) {
217 debug("APE::Properties::analyzeOld() -- MAC header is too short.");
218 return;
219 }
220
221 const unsigned int totalFrames = header.toUInt(18, false);
222
223 // Fail on 0 length APE files (catches non-finalized APE files)
224 if(totalFrames == 0)
225 return;
226
227 const short compressionLevel = header.toShort(0, false);
228 unsigned int blocksPerFrame;
229 if(d->version >= 3950)
230 blocksPerFrame = 73728 * 4;
231 else if(d->version >= 3900 || (d->version >= 3800 && compressionLevel == 4000))
232 blocksPerFrame = 73728;
233 else
234 blocksPerFrame = 9216;
235
236 // Get the APE info
237 d->channels = header.toShort(4, false);
238 d->sampleRate = header.toUInt(6, false);
239
240 const unsigned int finalFrameBlocks = header.toUInt(22, false);
241 d->sampleFrames = (totalFrames - 1) * blocksPerFrame + finalFrameBlocks;
242
243 // Get the bit depth from the RIFF-fmt chunk.
244 file->seek(16, File::Current);
245 const ByteVector fmt = file->readBlock(28);
246 if(fmt.size() < 28 || !fmt.startsWith("WAVEfmt ")) {
247 debug("APE::Properties::analyzeOld() -- fmt header is too short.");
248 return;
249 }
250
251 d->bitsPerSample = fmt.toShort(26, false);
252 }
253