1 /***************************************************************************
2 copyright : (C) 2004 by Allan Sandfeld Jensen
3 email : kde@carewolf.org
4 ***************************************************************************/
5
6 /***************************************************************************
7 * This library is free software; you can redistribute it and/or modify *
8 * it under the terms of the GNU Lesser General Public License version *
9 * 2.1 as published by the Free Software Foundation. *
10 * *
11 * This library is distributed in the hope that it will be useful, but *
12 * WITHOUT ANY WARRANTY; without even the implied warranty of *
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
14 * Lesser General Public License for more details. *
15 * *
16 * You should have received a copy of the GNU Lesser General Public *
17 * License along with this library; if not, write to the Free Software *
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
19 * 02110-1301 USA *
20 * *
21 * Alternatively, this file is available under the Mozilla Public *
22 * License Version 1.1. You may obtain a copy of the License at *
23 * http://www.mozilla.org/MPL/ *
24 ***************************************************************************/
25
26 #include <tstring.h>
27 #include <tdebug.h>
28 #include <bitset>
29 #include <math.h>
30
31 #include "mpcproperties.h"
32 #include "mpcfile.h"
33
34 using namespace TagLib;
35
36 class MPC::Properties::PropertiesPrivate
37 {
38 public:
PropertiesPrivate()39 PropertiesPrivate() :
40 version(0),
41 length(0),
42 bitrate(0),
43 sampleRate(0),
44 channels(0),
45 totalFrames(0),
46 sampleFrames(0),
47 trackGain(0),
48 trackPeak(0),
49 albumGain(0),
50 albumPeak(0) {}
51
52 int version;
53 int length;
54 int bitrate;
55 int sampleRate;
56 int channels;
57 unsigned int totalFrames;
58 unsigned int sampleFrames;
59 unsigned int trackGain;
60 unsigned int trackPeak;
61 unsigned int albumGain;
62 unsigned int albumPeak;
63 String flags;
64 };
65
66 ////////////////////////////////////////////////////////////////////////////////
67 // public members
68 ////////////////////////////////////////////////////////////////////////////////
69
Properties(const ByteVector & data,long streamLength,ReadStyle style)70 MPC::Properties::Properties(const ByteVector &data, long streamLength, ReadStyle style) :
71 AudioProperties(style),
72 d(new PropertiesPrivate())
73 {
74 readSV7(data, streamLength);
75 }
76
Properties(File * file,long streamLength,ReadStyle style)77 MPC::Properties::Properties(File *file, long streamLength, ReadStyle style) :
78 AudioProperties(style),
79 d(new PropertiesPrivate())
80 {
81 ByteVector magic = file->readBlock(4);
82 if(magic == "MPCK") {
83 // Musepack version 8
84 readSV8(file, streamLength);
85 }
86 else {
87 // Musepack version 7 or older, fixed size header
88 readSV7(magic + file->readBlock(MPC::HeaderSize - 4), streamLength);
89 }
90 }
91
~Properties()92 MPC::Properties::~Properties()
93 {
94 delete d;
95 }
96
length() const97 int MPC::Properties::length() const
98 {
99 return lengthInSeconds();
100 }
101
lengthInSeconds() const102 int MPC::Properties::lengthInSeconds() const
103 {
104 return d->length / 1000;
105 }
106
lengthInMilliseconds() const107 int MPC::Properties::lengthInMilliseconds() const
108 {
109 return d->length;
110 }
111
bitrate() const112 int MPC::Properties::bitrate() const
113 {
114 return d->bitrate;
115 }
116
sampleRate() const117 int MPC::Properties::sampleRate() const
118 {
119 return d->sampleRate;
120 }
121
channels() const122 int MPC::Properties::channels() const
123 {
124 return d->channels;
125 }
126
mpcVersion() const127 int MPC::Properties::mpcVersion() const
128 {
129 return d->version;
130 }
131
totalFrames() const132 unsigned int MPC::Properties::totalFrames() const
133 {
134 return d->totalFrames;
135 }
136
sampleFrames() const137 unsigned int MPC::Properties::sampleFrames() const
138 {
139 return d->sampleFrames;
140 }
141
trackGain() const142 int MPC::Properties::trackGain() const
143 {
144 return d->trackGain;
145 }
146
trackPeak() const147 int MPC::Properties::trackPeak() const
148 {
149 return d->trackPeak;
150 }
151
albumGain() const152 int MPC::Properties::albumGain() const
153 {
154 return d->albumGain;
155 }
156
albumPeak() const157 int MPC::Properties::albumPeak() const
158 {
159 return d->albumPeak;
160 }
161
162 ////////////////////////////////////////////////////////////////////////////////
163 // private members
164 ////////////////////////////////////////////////////////////////////////////////
165
166 namespace
167 {
readSize(File * file,unsigned int & sizeLength,bool & eof)168 unsigned long readSize(File *file, unsigned int &sizeLength, bool &eof)
169 {
170 sizeLength = 0;
171 eof = false;
172
173 unsigned char tmp;
174 unsigned long size = 0;
175
176 do {
177 const ByteVector b = file->readBlock(1);
178 if(b.isEmpty()) {
179 eof = true;
180 break;
181 }
182
183 tmp = b[0];
184 size = (size << 7) | (tmp & 0x7F);
185 sizeLength++;
186 } while((tmp & 0x80));
187 return size;
188 }
189
readSize(const ByteVector & data,unsigned int & pos)190 unsigned long readSize(const ByteVector &data, unsigned int &pos)
191 {
192 unsigned char tmp;
193 unsigned long size = 0;
194
195 do {
196 tmp = data[pos++];
197 size = (size << 7) | (tmp & 0x7F);
198 } while((tmp & 0x80) && (pos < data.size()));
199 return size;
200 }
201
202 // This array looks weird, but the same as original MusePack code found at:
203 // https://www.musepack.net/index.php?pg=src
204 const unsigned short sftable [8] = { 44100, 48000, 37800, 32000, 0, 0, 0, 0 };
205 }
206
readSV8(File * file,long streamLength)207 void MPC::Properties::readSV8(File *file, long streamLength)
208 {
209 bool readSH = false, readRG = false;
210
211 while(!readSH && !readRG) {
212 const ByteVector packetType = file->readBlock(2);
213
214 unsigned int packetSizeLength;
215 bool eof;
216 const unsigned long packetSize = readSize(file, packetSizeLength, eof);
217 if(eof) {
218 debug("MPC::Properties::readSV8() - Reached to EOF.");
219 break;
220 }
221
222 const unsigned long dataSize = packetSize - 2 - packetSizeLength;
223
224 const ByteVector data = file->readBlock(dataSize);
225 if(data.size() != dataSize) {
226 debug("MPC::Properties::readSV8() - dataSize doesn't match the actual data size.");
227 break;
228 }
229
230 if(packetType == "SH") {
231 // Stream Header
232 // http://trac.musepack.net/wiki/SV8Specification#StreamHeaderPacket
233
234 if(dataSize <= 5) {
235 debug("MPC::Properties::readSV8() - \"SH\" packet is too short to parse.");
236 break;
237 }
238
239 readSH = true;
240
241 unsigned int pos = 4;
242 d->version = data[pos];
243 pos += 1;
244 d->sampleFrames = readSize(data, pos);
245 if(pos > dataSize - 3) {
246 debug("MPC::Properties::readSV8() - \"SH\" packet is corrupt.");
247 break;
248 }
249
250 const unsigned long begSilence = readSize(data, pos);
251 if(pos > dataSize - 2) {
252 debug("MPC::Properties::readSV8() - \"SH\" packet is corrupt.");
253 break;
254 }
255
256 const unsigned short flags = data.toUShort(pos, true);
257 pos += 2;
258
259 d->sampleRate = sftable[(flags >> 13) & 0x07];
260 d->channels = ((flags >> 4) & 0x0F) + 1;
261
262 const unsigned int frameCount = d->sampleFrames - begSilence;
263 if(frameCount > 0 && d->sampleRate > 0) {
264 const double length = frameCount * 1000.0 / d->sampleRate;
265 d->length = static_cast<int>(length + 0.5);
266 d->bitrate = static_cast<int>(streamLength * 8.0 / length + 0.5);
267 }
268 }
269 else if (packetType == "RG") {
270 // Replay Gain
271 // http://trac.musepack.net/wiki/SV8Specification#ReplaygainPacket
272
273 if(dataSize <= 9) {
274 debug("MPC::Properties::readSV8() - \"RG\" packet is too short to parse.");
275 break;
276 }
277
278 readRG = true;
279
280 const int replayGainVersion = data[0];
281 if(replayGainVersion == 1) {
282 d->trackGain = data.toShort(1, true);
283 d->trackPeak = data.toShort(3, true);
284 d->albumGain = data.toShort(5, true);
285 d->albumPeak = data.toShort(7, true);
286 }
287 }
288
289 else if(packetType == "SE") {
290 break;
291 }
292
293 else {
294 file->seek(dataSize, File::Current);
295 }
296 }
297 }
298
readSV7(const ByteVector & data,long streamLength)299 void MPC::Properties::readSV7(const ByteVector &data, long streamLength)
300 {
301 if(data.startsWith("MP+")) {
302 d->version = data[3] & 15;
303 if(d->version < 7)
304 return;
305
306 d->totalFrames = data.toUInt(4, false);
307
308 const unsigned int flags = data.toUInt(8, false);
309 d->sampleRate = sftable[(flags >> 16) & 0x03];
310 d->channels = 2;
311
312 const unsigned int gapless = data.toUInt(5, false);
313
314 d->trackGain = data.toShort(14, false);
315 d->trackPeak = data.toShort(12, false);
316 d->albumGain = data.toShort(18, false);
317 d->albumPeak = data.toShort(16, false);
318
319 // convert gain info
320 if(d->trackGain != 0) {
321 int tmp = (int)((64.82 - (short)d->trackGain / 100.) * 256. + .5);
322 if(tmp >= (1 << 16) || tmp < 0) tmp = 0;
323 d->trackGain = tmp;
324 }
325
326 if(d->albumGain != 0) {
327 int tmp = (int)((64.82 - d->albumGain / 100.) * 256. + .5);
328 if(tmp >= (1 << 16) || tmp < 0) tmp = 0;
329 d->albumGain = tmp;
330 }
331
332 if (d->trackPeak != 0)
333 d->trackPeak = (int)(log10((double)d->trackPeak) * 20 * 256 + .5);
334
335 if (d->albumPeak != 0)
336 d->albumPeak = (int)(log10((double)d->albumPeak) * 20 * 256 + .5);
337
338 bool trueGapless = (gapless >> 31) & 0x0001;
339 if(trueGapless) {
340 unsigned int lastFrameSamples = (gapless >> 20) & 0x07FF;
341 d->sampleFrames = d->totalFrames * 1152 - lastFrameSamples;
342 }
343 else
344 d->sampleFrames = d->totalFrames * 1152 - 576;
345 }
346 else {
347 const unsigned int headerData = data.toUInt(0, false);
348
349 d->bitrate = (headerData >> 23) & 0x01ff;
350 d->version = (headerData >> 11) & 0x03ff;
351 d->sampleRate = 44100;
352 d->channels = 2;
353
354 if(d->version >= 5)
355 d->totalFrames = data.toUInt(4, false);
356 else
357 d->totalFrames = data.toUShort(6, false);
358
359 d->sampleFrames = d->totalFrames * 1152 - 576;
360 }
361
362 if(d->sampleFrames > 0 && d->sampleRate > 0) {
363 const double length = d->sampleFrames * 1000.0 / d->sampleRate;
364 d->length = static_cast<int>(length + 0.5);
365
366 if(d->bitrate == 0)
367 d->bitrate = static_cast<int>(streamLength * 8.0 / length + 0.5);
368 }
369 }
370