From: Lukáš Lalinský Date: Sat, 27 Nov 2010 20:58:57 +0000 (+0000) Subject: Fix reading of WavPack streams without a length information in the header X-Git-Tag: v1.7rc1~28 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=56343767ce35175d2f2e96d0ae9e284283859be4;p=taglib Fix reading of WavPack streams without a length information in the header 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 --- diff --git a/NEWS b/NEWS index 6ded56fa..b8fd7c10 100644 --- 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) =========================== diff --git a/taglib/wavpack/wavpackfile.cpp b/taglib/wavpack/wavpackfile.cpp index 3a2f25ca..9983dfed 100644 --- a/taglib/wavpack/wavpackfile.cpp +++ b/taglib/wavpack/wavpackfile.cpp @@ -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); } } diff --git a/taglib/wavpack/wavpackproperties.cpp b/taglib/wavpack/wavpackproperties.cpp index 697f223a..05436f92 100644 --- a/taglib/wavpack/wavpackproperties.cpp +++ b/taglib/wavpack/wavpackproperties.cpp @@ -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; +} + diff --git a/taglib/wavpack/wavpackproperties.h b/taglib/wavpack/wavpackproperties.h index a77e4de4..7bd28e3f 100644 --- a/taglib/wavpack/wavpackproperties.h +++ b/taglib/wavpack/wavpackproperties.h @@ -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; diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index f5b80c69..a93e4c55 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -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} diff --git a/tests/Makefile.am b/tests/Makefile.am index 94131821..c7585acb 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -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 index 00000000..c06d1071 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 index 00000000..7129a604 --- /dev/null +++ b/tests/test_wavpack.cpp @@ -0,0 +1,28 @@ +#include +#include +#include +#include +#include +#include +#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);