]> granicus.if.org Git - taglib/commitdiff
Fix reading of WavPack streams without a length information in the header
authorLukáš Lalinský <lalinsky@gmail.com>
Sat, 27 Nov 2010 20:58:57 +0000 (20:58 +0000)
committerLukáš Lalinský <lalinsky@gmail.com>
Sat, 27 Nov 2010 20:58:57 +0000 (20:58 +0000)
When the WavPack's total_samples header fiels contains -1, try to find
the final block and get the number of samples from there as
block_index + block_samples.

BUG:258016

git-svn-id: svn://anonsvn.kde.org/home/kde/trunk/kdesupport/taglib@1201476 283d02a7-25f6-0310-bc7c-ecb5cbfe19da

NEWS
taglib/wavpack/wavpackfile.cpp
taglib/wavpack/wavpackproperties.cpp
taglib/wavpack/wavpackproperties.h
tests/CMakeLists.txt
tests/Makefile.am
tests/data/no_length.wv [new file with mode: 0644]
tests/test_wavpack.cpp [new file with mode: 0644]

diff --git a/NEWS b/NEWS
index 6ded56fa5090b05718491f6f34cfd95dfd79260c..b8fd7c107b87108fd64cd3050c10cc9eb23bcbd3 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -8,6 +8,8 @@ TagLib 1.7
  * Exposed FLAC MD5 signature of the uncompressed audio stream via
    FLAC::Properties::signature(). (BUG:160172)
  * Added function ByteVector::toHex() for hex-encoding of byte vectors.
+ * WavPack reader now tries to get the audio length by finding the final
+   block, if the header doesn't have the information. (BUG:258016)
 
 TagLib 1.6.3 (Apr 17, 2010)
 ===========================
index 3a2f25caf612afcfb5b209221a9c6ebf2c2540af..9983dfed9cae140bc2edd1b3ee7c673c7ca19c0d 100644 (file)
@@ -230,8 +230,7 @@ void WavPack::File::read(bool readProperties, Properties::ReadStyle /* propertie
 
   if(readProperties) {
     seek(0);
-    d->properties = new Properties(readBlock(WavPack::HeaderSize),
-                                   length() - d->APESize);
+    d->properties = new Properties(this, length() - d->APESize);
   }
 }
 
index 697f223a6803470c352aa5f94a441eccef94488e..05436f922dd9d484d0ceac66438a27268ce6e8a2 100644 (file)
@@ -48,7 +48,8 @@ public:
     sampleRate(0),
     channels(0),
     version(0),
-    bitsPerSample(0) {}
+    bitsPerSample(0),
+    file(0) {}
 
   ByteVector data;
   long streamLength;
@@ -59,6 +60,7 @@ public:
   int channels;
   int version;
   int bitsPerSample;
+  File *file;
 };
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -71,6 +73,14 @@ WavPack::Properties::Properties(const ByteVector &data, long streamLength, ReadS
   read();
 }
 
+WavPack::Properties::Properties(File *file, long streamLength, ReadStyle style) : AudioProperties(style)
+{
+  ByteVector data = file->readBlock(32);
+  d = new PropertiesPrivate(data, streamLength, style);
+  d->file = file;
+  read();
+}
+
 WavPack::Properties::~Properties()
 {
   delete d;
@@ -122,12 +132,19 @@ static const unsigned int sample_rates[] = { 6000, 8000, 9600, 11025, 12000,
 #define SRATE_LSB       23
 #define SRATE_MASK      (0xfL << SRATE_LSB)
 
+#define MIN_STREAM_VERS     0x402
+#define MAX_STREAM_VERS     0x410
+
+#define FINAL_BLOCK     0x1000
+
 void WavPack::Properties::read()
 {
   if(!d->data.startsWith("wvpk"))
     return;
 
   d->version = d->data.mid(8, 2).toShort(false);
+  if(d->version < MIN_STREAM_VERS || d->version > MAX_STREAM_VERS)
+    return;
 
   unsigned int flags = d->data.mid(24, 4).toUInt(false);
   d->bitsPerSample = ((flags & BYTES_STORED) + 1) * 8 -
@@ -136,11 +153,43 @@ void WavPack::Properties::read()
   d->channels = (flags & MONO_FLAG) ? 1 : 2;
 
   unsigned int samples = d->data.mid(12, 4).toUInt(false);
-  if (samples == ~0u) {
-    samples = 0;
+  if(samples == ~0u) {
+    if(d->file && d->style != Fast) {
+      samples = seekFinalIndex(); 
+    }
+    else {
+      samples = 0;
+    }
   }
   d->length = d->sampleRate > 0 ? (samples + (d->sampleRate / 2)) / d->sampleRate : 0;
 
   d->bitrate = d->length > 0 ? ((d->streamLength * 8L) / d->length) / 1000 : 0;
 }
 
+unsigned int WavPack::Properties::seekFinalIndex()
+{
+  ByteVector blockID("wvpk", 4);
+
+  long offset = d->streamLength;
+  while(offset > 0) {
+    offset = d->file->rfind(blockID, offset);
+    if(offset == -1)
+      return 0;
+    d->file->seek(offset);
+    ByteVector data = d->file->readBlock(32);
+    if(data.size() != 32)
+      return 0;
+    int version = data.mid(8, 2).toShort(false);
+    if(version < MIN_STREAM_VERS || version > MAX_STREAM_VERS)
+      continue;
+    unsigned int flags = data.mid(24, 4).toUInt(false);
+    if(!(flags & FINAL_BLOCK))
+      return 0;
+    unsigned int blockIndex = data.mid(16, 4).toUInt(false);
+    unsigned int blockSamples = data.mid(20, 4).toUInt(false);
+    return blockIndex + blockSamples;
+  }
+
+  return 0;
+}
+
index a77e4de469952dc7168972133455c3c57b79edf2..7bd28e3f9ebd4eb44c5cf1b1791161e9d663e9b1 100644 (file)
@@ -54,9 +54,18 @@ namespace TagLib {
       /*!
        * Create an instance of WavPack::Properties with the data read from the
        * ByteVector \a data.
+       *
+       * \deprecated This constructor will be dropped in favor of the one below
+       * in a future version.
        */
       Properties(const ByteVector &data, long streamLength, ReadStyle style = Average);
 
+      /*!
+       * Create an instance of WavPack::Properties.
+       */
+      // BIC: merge with the above constructor
+      Properties(File *file, long streamLength, ReadStyle style = Average);
+
       /*!
        * Destroys this WavPack::Properties instance.
        */
@@ -84,6 +93,7 @@ namespace TagLib {
       Properties &operator=(const Properties &);
 
       void read();
+      unsigned int seekFinalIndex();
 
       class PropertiesPrivate;
       PropertiesPrivate *d;
index f5b80c6912a93e08d846751ec1db0ee638f5e276..a93e4c5520c75c0f5fa588d7dc98c338152a4915 100644 (file)
@@ -18,6 +18,7 @@ INCLUDE_DIRECTORIES(
   ${CMAKE_CURRENT_SOURCE_DIR}/../taglib/ogg/vorbis
   ${CMAKE_CURRENT_SOURCE_DIR}/../taglib/ogg/flac
   ${CMAKE_CURRENT_SOURCE_DIR}/../taglib/flac
+  ${CMAKE_CURRENT_SOURCE_DIR}/../taglib/wavpack
 )
 
 SET(test_runner_SRCS
@@ -42,6 +43,7 @@ SET(test_runner_SRCS
   test_ape.cpp
   test_apetag.cpp
   test_wav.cpp
+  test_wavpack.cpp
 )
 IF(WITH_MP4)
    SET(test_runner_SRCS ${test_runner_SRCS}
index 9413182109a1794d2a45ea29e799ddb2cd8dc1e2..c7585acb1b939c3c72fc580542b65c045c240382 100644 (file)
@@ -12,7 +12,8 @@ INCLUDES = \
         -I$(top_srcdir)/taglib/flac \
         -I$(top_srcdir)/taglib/riff \
         -I$(top_srcdir)/taglib/riff/aiff \
-        -I$(top_srcdir)/taglib/mpeg/id3v2/frames
+        -I$(top_srcdir)/taglib/mpeg/id3v2/frames \
+        -I$(top_srcdir)/taglib/wavpack
 
 test_runner_SOURCES = \
        main.cpp \
@@ -32,7 +33,8 @@ test_runner_SOURCES = \
        test_aiff.cpp \
        test_ogg.cpp \
        test_oggflac.cpp \
-       test_flac.cpp
+       test_flac.cpp \
+       test_wavpack.cpp
 
 if build_tests
 TESTS = test_runner
diff --git a/tests/data/no_length.wv b/tests/data/no_length.wv
new file mode 100644 (file)
index 0000000..c06d107
Binary files /dev/null and b/tests/data/no_length.wv differ
diff --git a/tests/test_wavpack.cpp b/tests/test_wavpack.cpp
new file mode 100644 (file)
index 0000000..7129a60
--- /dev/null
@@ -0,0 +1,28 @@
+#include <cppunit/extensions/HelperMacros.h>
+#include <string>
+#include <stdio.h>
+#include <tag.h>
+#include <tbytevectorlist.h>
+#include <wavpackfile.h>
+#include "utils.h"
+
+using namespace std;
+using namespace TagLib;
+
+class TestWavPack : public CppUnit::TestFixture
+{
+  CPPUNIT_TEST_SUITE(TestWavPack);
+  CPPUNIT_TEST(testLengthScan);
+  CPPUNIT_TEST_SUITE_END();
+
+public:
+
+  void testLengthScan()
+  {
+    WavPack::File f("data/no_length.wv");
+    CPPUNIT_ASSERT_EQUAL(4, f.audioProperties()->length());
+  }
+
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(TestWavPack);