]> granicus.if.org Git - taglib/commitdiff
FLAC: AudioProperties improvements
authorTsuda Kageyu <tsuda.kageyu@gmail.com>
Thu, 21 May 2015 03:15:11 +0000 (12:15 +0900)
committerTsuda Kageyu <tsuda.kageyu@gmail.com>
Fri, 31 Jul 2015 15:49:21 +0000 (00:49 +0900)
Add lengthInSeconds(), lengthInMilliseconds() properties. (#503)
Add bitsPerSample() property besides sampleWidth(). (#360)
Remove some data members which are not needed to carry.
Add some tests for audio properties.
Add some supplementary comments.

taglib/flac/flacproperties.cpp
taglib/flac/flacproperties.h
tests/data/sinewave.flac [new file with mode: 0644]
tests/test_flac.cpp

index e591193ef9d3e40d8048909fab81dd793730fe67..486ce2f52e5c1638ad621fd6a4fdbe9370f1fd33 100644 (file)
@@ -34,24 +34,18 @@ using namespace TagLib;
 class FLAC::Properties::PropertiesPrivate
 {
 public:
-  PropertiesPrivate(ByteVector d, long st, ReadStyle s) :
-    data(d),
-    streamLength(st),
-    style(s),
+  PropertiesPrivate() :
     length(0),
     bitrate(0),
     sampleRate(0),
-    sampleWidth(0),
+    bitsPerSample(0),
     channels(0),
     sampleFrames(0) {}
 
-  ByteVector data;
-  long streamLength;
-  ReadStyle style;
   int length;
   int bitrate;
   int sampleRate;
-  int sampleWidth;
+  int bitsPerSample;
   int channels;
   unsigned long long sampleFrames;
   ByteVector signature;
@@ -61,16 +55,18 @@ public:
 // public members
 ////////////////////////////////////////////////////////////////////////////////
 
-FLAC::Properties::Properties(ByteVector data, long streamLength, ReadStyle style) : AudioProperties(style)
+FLAC::Properties::Properties(ByteVector data, long streamLength, ReadStyle style) :
+  AudioProperties(style),
+  d(new PropertiesPrivate())
 {
-  d = new PropertiesPrivate(data, streamLength, style);
-  read();
+  read(data, streamLength);
 }
 
-FLAC::Properties::Properties(File *file, ReadStyle style) : AudioProperties(style)
+FLAC::Properties::Properties(File *file, ReadStyle style) :
+  AudioProperties(style),
+  d(new PropertiesPrivate())
 {
-  d = new PropertiesPrivate(file->streamInfoData(), file->streamLength(), style);
-  read();
+  read(file->streamInfoData(), file->streamLength());
 }
 
 FLAC::Properties::~Properties()
@@ -79,6 +75,16 @@ FLAC::Properties::~Properties()
 }
 
 int FLAC::Properties::length() const
+{
+  return lengthInSeconds();
+}
+
+int FLAC::Properties::lengthInSeconds() const
+{
+  return d->length / 1000;
+}
+
+int FLAC::Properties::lengthInMilliseconds() const
 {
   return d->length;
 }
@@ -93,9 +99,14 @@ int FLAC::Properties::sampleRate() const
   return d->sampleRate;
 }
 
+int FLAC::Properties::bitsPerSample() const
+{
+  return d->bitsPerSample;
+}
+
 int FLAC::Properties::sampleWidth() const
 {
-  return d->sampleWidth;
+  return bitsPerSample();
 }
 
 int FLAC::Properties::channels() const
@@ -117,9 +128,9 @@ ByteVector FLAC::Properties::signature() const
 // private members
 ////////////////////////////////////////////////////////////////////////////////
 
-void FLAC::Properties::read()
+void FLAC::Properties::read(const ByteVector &data, long streamLength)
 {
-  if(d->data.size() < 18) {
+  if(data.size() < 18) {
     debug("FLAC::Properties::read() - FLAC properties must contain at least 18 bytes.");
     return;
   }
@@ -138,32 +149,28 @@ void FLAC::Properties::read()
   // Maximum frame size (in bytes)
   pos += 3;
 
-  uint flags = d->data.toUInt(pos, true);
+  const uint flags = data.toUInt(pos, true);
   pos += 4;
 
-  d->sampleRate = flags >> 12;
-  d->channels = ((flags >> 9) & 7) + 1;
-  d->sampleWidth = ((flags >> 4) & 31) + 1;
+  d->sampleRate    = flags >> 12;
+  d->channels      = ((flags >> 9) &  7) + 1;
+  d->bitsPerSample = ((flags >> 4) & 31) + 1;
 
   // The last 4 bits are the most significant 4 bits for the 36 bit
   // stream length in samples. (Audio files measured in days)
 
-  unsigned long long hi = flags & 0xf;
-  unsigned long long lo = d->data.toUInt(pos, true);
+  const ulonglong hi = flags & 0xf;
+  const ulonglong lo = data.toUInt(pos, true);
   pos += 4;
 
   d->sampleFrames = (hi << 32) | lo;
 
-  if(d->sampleRate > 0)
-    d->length = int(d->sampleFrames / d->sampleRate);
-
-  // Uncompressed bitrate:
-
-  //d->bitrate = ((d->sampleRate * d->channels) / 1000) * d->sampleWidth;
-
-  // Real bitrate:
-
-  d->bitrate = d->length > 0 ? ((d->streamLength * 8UL) / d->length) / 1000 : 0;
+  if(d->sampleFrames > 0 && d->sampleRate > 0) {
+    const double length = d->sampleFrames * 1000.0 / d->sampleRate;
+    d->length  = static_cast<int>(length + 0.5);
+    d->bitrate = static_cast<int>(streamLength * 8.0 / length + 0.5);
+  }
 
-  d->signature = d->data.mid(pos, 32);
+  if(data.size() >= pos + 16)
+    d->signature = data.mid(pos, 16);
 }
index a9cede0ac14e61df42b1446e75fe389ebcb7bc96..6f13ce6225054ca7c5d0ccd255e68a998762bea5 100644 (file)
@@ -64,16 +64,61 @@ namespace TagLib {
        */
       virtual ~Properties();
 
-      // Reimplementations.
-
+      /*!
+       * Returns the length of the file in seconds.  The length is rounded down to
+       * the nearest whole second.
+       *
+       * \note This method is just an alias of lengthInSeconds().
+       *
+       * \deprecated
+       */
       virtual int length() const;
+
+      /*!
+       * Returns the length of the file in seconds.  The length is rounded down to
+       * the nearest whole second.
+       *
+       * \see lengthInMilliseconds()
+       */
+      // BIC: make virtual
+      int lengthInSeconds() const;
+
+      /*!
+       * Returns the length of the file in milliseconds.
+       *
+       * \see lengthInSeconds()
+       */
+      // BIC: make virtual
+      int lengthInMilliseconds() const;
+
+      /*!
+       * Returns the average bit rate of the file in kb/s.
+       */
       virtual int bitrate() const;
+
+      /*!
+       * Returns the sample rate in Hz.
+       */
       virtual int sampleRate() const;
+
+      /*!
+       * Returns the number of audio channels.
+       */
       virtual int channels() const;
 
+      /*!
+       * Returns the number of bits per audio sample as read from the FLAC
+       * identification header.
+       */
+      int bitsPerSample() const;
+
       /*!
        * Returns the sample width as read from the FLAC identification
        * header.
+       *
+       * \note This method is just an alias of bitsPerSample().
+       *
+       * \deprecated
        */
       int sampleWidth() const;
 
@@ -92,7 +137,7 @@ namespace TagLib {
       Properties(const Properties &);
       Properties &operator=(const Properties &);
 
-      void read();
+      void read(const ByteVector &data, long streamLength);
 
       class PropertiesPrivate;
       PropertiesPrivate *d;
diff --git a/tests/data/sinewave.flac b/tests/data/sinewave.flac
new file mode 100644 (file)
index 0000000..25d31b2
Binary files /dev/null and b/tests/data/sinewave.flac differ
index 164020a611fd8248c2027206373b4340f3d8288b..dd09b3eafb7eee620ca9d7ef44a097b65a9777b9 100644 (file)
@@ -25,6 +25,7 @@ class TestFLAC : public CppUnit::TestFixture
   CPPUNIT_TEST(testSaveMultipleValues);
   CPPUNIT_TEST(testDict);
   CPPUNIT_TEST(testInvalid);
+  CPPUNIT_TEST(testAudioProperties);
   CPPUNIT_TEST(testZeroSizedPadding);
   CPPUNIT_TEST_SUITE_END();
 
@@ -256,6 +257,24 @@ public:
     CPPUNIT_ASSERT_EQUAL(TagLib::uint(0), f.properties().size());
   }
 
+  void testAudioProperties()
+  {
+    FLAC::File f(TEST_FILE_PATH_C("sinewave.flac"));
+    CPPUNIT_ASSERT(f.audioProperties());
+    CPPUNIT_ASSERT_EQUAL(3, f.audioProperties()->length());
+    CPPUNIT_ASSERT_EQUAL(3, f.audioProperties()->lengthInSeconds());
+    CPPUNIT_ASSERT_EQUAL(3550, f.audioProperties()->lengthInMilliseconds());
+    CPPUNIT_ASSERT_EQUAL(145, f.audioProperties()->bitrate());
+    CPPUNIT_ASSERT_EQUAL(44100, f.audioProperties()->sampleRate());
+    CPPUNIT_ASSERT_EQUAL(2, f.audioProperties()->channels());
+    CPPUNIT_ASSERT_EQUAL(16, f.audioProperties()->bitsPerSample());
+    CPPUNIT_ASSERT_EQUAL(16, f.audioProperties()->sampleWidth());
+    CPPUNIT_ASSERT_EQUAL(156556ULL, f.audioProperties()->sampleFrames());
+    CPPUNIT_ASSERT_EQUAL(
+      ByteVector("\xcf\xe3\xd9\xda\xba\xde\xab\x2c\xbf\x2c\xa2\x35\x27\x4b\x7f\x76"),
+      f.audioProperties()->signature());
+  }
+
   void testZeroSizedPadding()
   {
     ScopedFileCopy copy("zero-sized-padding", ".flac");