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