]> granicus.if.org Git - taglib/commitdiff
MPC v8 audio properties
authorAlex Novichkov <novichkov.qoobar@gmail.com>
Wed, 11 Jul 2012 11:08:10 +0000 (13:08 +0200)
committerLukáš Lalinský <lalinsky@gmail.com>
Wed, 11 Jul 2012 11:08:10 +0000 (13:08 +0200)
taglib/mpc/mpcfile.cpp
taglib/mpc/mpcfile.h
taglib/mpc/mpcproperties.cpp
taglib/mpc/mpcproperties.h

index ff965bb6139cba7c07ba6213d647f18505e2286d..7e33fdb4eb0990c5b36a1514c660334e3a64059a 100644 (file)
@@ -310,8 +310,18 @@ void MPC::File::read(bool readProperties, Properties::ReadStyle /* propertiesSty
   // Look for MPC metadata
 
   if(readProperties) {
-    d->properties = new Properties(readBlock(MPC::HeaderSize),
+    // Checking MPC version
+    ByteVector magic = readBlock(4);
+    if (magic == MPC::V8MagicTitle) {
+      // Musepack version 8 or newer, we should find "SH" packet and "RG" packet
+      d->properties = new Properties(ByteVector("MPCK")+findHeaderPacket(),
                                    length() - d->ID3v2Size - d->APESize);
+    }
+    else {
+      seek(tell()-4);
+      d->properties = new Properties(readBlock(MPC::HeaderSize),
+                                   length() - d->ID3v2Size - d->APESize);
+    }
   }
 }
 
@@ -359,3 +369,42 @@ long MPC::File::findID3v2()
 
   return -1;
 }
+
+/*
+The structure of MPC v8 packet:
+key   16 bit
+size  n*8 bit, 0<n<10
+data  size*8
+
+This function returns only data for corresponding key
+*/
+ByteVector MPC::File::findHeaderPacket()
+{
+  ByteVector packet;
+  if(!isValid())
+    return packet;
+
+  ByteVector key = readBlock(2);
+  uint sizelength=0;
+  long size = readSize(sizelength);
+
+  if (key=="SH")
+    return readBlock(size-2-sizelength+12); //12 is the size of the next packet
+                                            //that contains replaygain info
+
+  return packet;
+}
+
+long MPC::File::readSize(uint &sizelength)
+{
+  unsigned char tmp;
+  long size = 0;
+
+  do {
+    ByteVector b = readBlock(1);
+    tmp = b[0];
+    size = (size << 7) | (tmp & 0x7F);
+    sizelength++;
+  } while((tmp & 0x80));
+  return size;
+}
\ No newline at end of file
index c906ae6709de18a00855cbc761657112c84e65b4..e5d1085691155fb18a35663e7c1204864db538c2 100644 (file)
@@ -32,6 +32,8 @@
 
 #include "mpcproperties.h"
 
+#include "tlist.h"
+
 namespace TagLib {
 
   class Tag;
@@ -180,7 +182,6 @@ namespace TagLib {
        */
       void remove(int tags = AllTags);
 
-
     private:
       File(const File &);
       File &operator=(const File &);
@@ -190,6 +191,8 @@ namespace TagLib {
       long findAPE();
       long findID3v1();
       long findID3v2();
+      ByteVector findHeaderPacket();
+      long readSize(uint &sizelength);
 
       class FilePrivate;
       FilePrivate *d;
index 3ce44cc2bb0b8dbea01b7483952e8154c2382884..1afa4da0634be6c53bc281bdfc359f7ab3bac0b4 100644 (file)
@@ -26,6 +26,7 @@
 #include <tstring.h>
 #include <tdebug.h>
 #include <bitset>
+#include <math.h>
 
 #include "mpcproperties.h"
 #include "mpcfile.h"
@@ -45,7 +46,11 @@ public:
     sampleRate(0),
     channels(0),
     totalFrames(0),
-    sampleFrames(0) {}
+    sampleFrames(0),
+    trackGain(0),
+    trackPeak(0),
+    albumGain(0),
+    albumPeak(0) {}
 
   ByteVector data;
   long streamLength;
@@ -57,6 +62,11 @@ public:
   int channels;
   uint totalFrames;
   uint sampleFrames;
+  uint trackGain;
+  uint trackPeak;
+  uint albumGain;
+  uint albumPeak;
+  String flags;
 };
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -109,14 +119,94 @@ uint MPC::Properties::sampleFrames() const
   return d->sampleFrames;
 }
 
+int MPC::Properties::trackGain() const
+{
+  return d->trackGain;
+}
+
+int MPC::Properties::trackPeak() const
+{
+  return d->trackPeak;
+}
+
+int MPC::Properties::albumGain() const
+{
+  return d->albumGain;
+}
+
+int MPC::Properties::albumPeak() const
+{
+  return d->albumPeak;
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 // private members
 ////////////////////////////////////////////////////////////////////////////////
 
+
+ulong readSize(const ByteVector &b, uint &sizelength)
+{
+  unsigned char tmp;
+  ulong size = 0;
+  sizelength = 0;
+
+  do {
+    tmp = b[sizelength];
+    size = (size << 7) | (tmp & 0x7F);
+    sizelength++;
+  } while((tmp & 0x80));
+  return size;
+}
+
 static const unsigned short sftable [4] = { 44100, 48000, 37800, 32000 };
 
 void MPC::Properties::read()
 {
+  if (d->data.startsWith("MPCK")) {
+    //no checksum checking so far, this code isn't working
+    //quint16 crc=qChecksum(b.mid(4).data(), b.length()-4);
+    //uint crc1=d->data.mid(0,2).toUInt(false);
+
+
+    int pos=8;
+    d->version = d->data[pos];
+    pos+=1;
+    uint sizelength=0;
+    d->sampleFrames = readSize(d->data.mid(pos), sizelength);
+    pos+=sizelength;
+    ulong begSilence = readSize(d->data.mid(pos), sizelength);
+    pos+=sizelength;
+
+    std::bitset<16> flags(TAGLIB_CONSTRUCT_BITSET(d->data.mid(pos,2).toUShort(true)));
+    pos+=2;
+
+    d->sampleRate = sftable[flags[15] * 4 + flags[14]*2 + flags[13]];
+    d->channels = flags[7] * 8 + flags[6]*4 + flags[5]*2+flags[4] + 1;
+
+    //bool msUsed = flags[3];
+    //int audioBlockFrames = (flags[2]*4 + flags[1]*2 + flags[0]) * 2;
+
+    if ((d->sampleFrames - begSilence) != 0)
+      d->bitrate = d->streamLength * 8.0 * d->sampleRate/(d->sampleFrames-begSilence);
+      d->bitrate = d->bitrate / 1000;
+
+    d->length = (d->sampleFrames - begSilence) / d->sampleRate;
+
+    // Replaygain info scanning
+    pos = d->data.find(ByteVector("RG"),pos);
+    if (pos>=0) {
+      int replayGainVersion = d->data[pos+3];
+      if (replayGainVersion==1) {
+        d->trackGain = d->data.mid(pos+4,2).toUInt(true);
+        d->trackPeak = d->data.mid(pos+6,2).toUInt(true);
+        d->albumGain = d->data.mid(pos+8,2).toUInt(true);
+        d->albumPeak = d->data.mid(pos+10,2).toUInt(true);
+      }
+    }
+
+    return;
+  }
+
   if(!d->data.startsWith("MP+"))
     return;
 
@@ -130,6 +220,31 @@ void MPC::Properties::read()
     d->channels = 2;
 
     uint gapless = d->data.mid(5, 4).toUInt(false);
+
+    d->trackGain = d->data.mid(14,2).toShort(false);
+    d->trackPeak = d->data.mid(12,2).toUInt(false);
+    d->albumGain = d->data.mid(18,2).toShort(false);
+    d->albumPeak = d->data.mid(16,2).toUInt(false);
+
+    // convert gain info
+    if (d->trackGain != 0) {
+      int tmp = (int)((64.82 - (short)d->trackGain / 100.) * 256. + .5);
+      if (tmp >= (1 << 16) || tmp < 0) tmp = 0;
+      d->trackGain = tmp;
+    }
+
+  if (d->albumGain != 0) {
+    int tmp = (int)((64.82 - d->albumGain / 100.) * 256. + .5);
+    if (tmp >= (1 << 16) || tmp < 0) tmp = 0;
+    d->albumGain = tmp;
+  }
+
+  if (d->trackPeak != 0)
+    d->trackPeak = (int) (log10(d->trackPeak) * 20 * 256 + .5);
+
+  if (d->albumPeak != 0)
+    d->albumPeak = (int) (log10(d->albumPeak) * 20 * 256 + .5);
+
     bool trueGapless = (gapless >> 31) & 0x0001;
     if(trueGapless) {
       uint lastFrameSamples = (gapless >> 20) & 0x07FF;
index 8225306f89c8231bdfcc66e12ab1c5f0186a9c04..f67d434d4243ddef950df8ed6b91986315549f04 100644 (file)
@@ -36,6 +36,7 @@ namespace TagLib {
     class File;
 
     static const uint HeaderSize = 8*7;
+    static const char * V8MagicTitle = "MPCK";
 
     //! An implementation of audio property reading for MPC
 
@@ -66,12 +67,38 @@ namespace TagLib {
       virtual int channels() const;
 
       /*!
-       * Returns the version of the bitstream (SV4-SV7)
+       * Returns the version of the bitstream (SV4-SV8)
        */
       int mpcVersion() const;
       uint totalFrames() const;
       uint sampleFrames() const;
 
+      /*!
+      * Returns the track gain as an integer value,
+      * to convert to dB: trackGain in dB = 64.82 - (trackGain / 256)
+      */
+      int trackGain() const;
+
+      /*!
+      * Returns the track peak as an integer value,
+      * to convert to dB: trackPeak in dB = trackPeak / 256
+      * to convert to floating [-1..1]: trackPeak = 10^(trackPeak / 256 / 20)/32768
+      */
+      int trackPeak() const;
+
+      /*!
+      * Returns the album gain as an integer value,
+      * to convert to dB: albumGain in dB = 64.82 - (albumGain / 256)
+      */
+      int albumGain() const;
+
+      /*!
+      * Returns the album peak as an integer value,
+      * to convert to dB: albumPeak in dB = albumPeak / 256
+      * to convert to floating [-1..1]: albumPeak = 10^(albumPeak / 256 / 20)/32768
+      */
+      int albumPeak() const;
+
     private:
       Properties(const Properties &);
       Properties &operator=(const Properties &);