From 872cafb0b9019bcc88640fb4961f6bb39fcb84e4 Mon Sep 17 00:00:00 2001 From: David Bryant Date: Tue, 30 Jun 2020 22:19:38 -0700 Subject: [PATCH 1/6] Several fixes for WavPack files, including issue #962 Fixes to properly handle WavPack files with leading and trailing non-audio blocks, non-standard sampling rates, and DSD audio (introduced in WavPack 5). --- taglib/wavpack/wavpackproperties.cpp | 186 +++++++++++++++++++++++---- taglib/wavpack/wavpackproperties.h | 2 + 2 files changed, 166 insertions(+), 22 deletions(-) diff --git a/taglib/wavpack/wavpackproperties.cpp b/taglib/wavpack/wavpackproperties.cpp index c1d04fd2..5bc1af4b 100644 --- a/taglib/wavpack/wavpackproperties.cpp +++ b/taglib/wavpack/wavpackproperties.cpp @@ -147,7 +147,8 @@ namespace #define BYTES_STORED 3 #define MONO_FLAG 4 -#define LOSSLESS_FLAG 8 +#define HYBRID_FLAG 8 +#define DSD_FLAG 0x80000000 // block is encoded DSD (1-bit PCM) #define SHIFT_LSB 13 #define SHIFT_MASK (0x1fL << SHIFT_LSB) @@ -158,8 +159,16 @@ namespace #define MIN_STREAM_VERS 0x402 #define MAX_STREAM_VERS 0x410 +#define INITIAL_BLOCK 0x800 #define FINAL_BLOCK 0x1000 +#define ID_DSD_BLOCK 0x0e +#define ID_OPTIONAL_DATA 0x20 +#define ID_UNIQUE 0x3f +#define ID_ODD_SIZE 0x40 +#define ID_LARGE 0x80 +#define ID_SAMPLE_RATE (ID_OPTIONAL_DATA | 0x7) + void WavPack::Properties::read(File *file, long streamLength) { long offset = 0; @@ -178,17 +187,50 @@ void WavPack::Properties::read(File *file, long streamLength) break; } + const unsigned int blockSize = data.toUInt(4, false); + const unsigned int sampleFrames = data.toUInt(12, false); + const unsigned int blockSamples = data.toUInt(20, false); const unsigned int flags = data.toUInt(24, false); + unsigned int sampleRate = sample_rates[(flags & SRATE_MASK) >> SRATE_LSB]; - if(offset == 0) { + if (!blockSamples) { // ignore blocks with no samples + offset += blockSize + 8; + continue; + } + + if (blockSize < 24 || blockSize > 1048576) { + debug("WavPack::Properties::read() -- Invalid block header found."); + break; + } + + // For non-standard sample rates or DSD audio files, we must read and parse the block + // to actually determine the sample rate. + + if(!sampleRate || (flags & DSD_FLAG)) { + const unsigned int adjusted_block_size = blockSize - 24; + const ByteVector block = file->readBlock(adjusted_block_size); + + if(block.size() < adjusted_block_size) { + debug("WavPack::Properties::read() -- block is too short."); + break; + } + + if (!sampleRate) + sampleRate = getNonStandardRate(reinterpret_cast(block.data()), adjusted_block_size); + + if (sampleRate && (flags & DSD_FLAG)) + sampleRate <<= getDsdRateShifter(reinterpret_cast(block.data()), adjusted_block_size); + } + + if(flags & INITIAL_BLOCK) { d->version = data.toShort(8, false); if(d->version < MIN_STREAM_VERS || d->version > MAX_STREAM_VERS) break; d->bitsPerSample = ((flags & BYTES_STORED) + 1) * 8 - ((flags & SHIFT_MASK) >> SHIFT_LSB); - d->sampleRate = sample_rates[(flags & SRATE_MASK) >> SRATE_LSB]; - d->lossless = !(flags & LOSSLESS_FLAG); - d->sampleFrames = data.toUInt(12, false); + d->sampleRate = sampleRate; + d->lossless = !(flags & HYBRID_FLAG); + d->sampleFrames = sampleFrames; } d->channels += (flags & MONO_FLAG) ? 1 : 2; @@ -196,7 +238,6 @@ void WavPack::Properties::read(File *file, long streamLength) if(flags & FINAL_BLOCK) break; - const unsigned int blockSize = data.toUInt(4, false); offset += blockSize + 8; } @@ -212,25 +253,126 @@ void WavPack::Properties::read(File *file, long streamLength) unsigned int WavPack::Properties::seekFinalIndex(File *file, long streamLength) { - const long offset = file->rfind("wvpk", streamLength); - if(offset == -1) - return 0; + long offset = streamLength; - file->seek(offset); - const ByteVector data = file->readBlock(32); - if(data.size() < 32) - return 0; + while (offset >= 32) { + offset = file->rfind("wvpk", offset - 32); - const int version = data.toShort(8, false); - if(version < MIN_STREAM_VERS || version > MAX_STREAM_VERS) - return 0; + if(offset == -1) + return 0; - const unsigned int flags = data.toUInt(24, false); - if(!(flags & FINAL_BLOCK)) - return 0; + file->seek(offset); + const ByteVector data = file->readBlock(32); + if(data.size() < 32) + return 0; - const unsigned int blockIndex = data.toUInt(16, false); - const unsigned int blockSamples = data.toUInt(20, false); + const int version = data.toShort(8, false); + if(version < MIN_STREAM_VERS || version > MAX_STREAM_VERS) + return 0; - return blockIndex + blockSamples; + const unsigned int blockIndex = data.toUInt(16, false); + const unsigned int blockSamples = data.toUInt(20, false); + const unsigned int flags = data.toUInt(24, false); + + if (blockSamples && (flags & FINAL_BLOCK)) + return blockIndex + blockSamples; + } + + return 0; +} + +// Given a WavPack block (complete, but not including the 32-byte header), parse the metadata +// blocks until an ID_SAMPLE_RATE block is found and return the non-standard sample rate +// contained there, or zero if no such block is found. + +int WavPack::Properties::getNonStandardRate(unsigned char const *buffer, int bcount) +{ + unsigned char meta_id, c1, c2; + int meta_bc; + + while (bcount >= 2) { + meta_id = *buffer++; + c1 = *buffer++; + + meta_bc = c1 << 1; + bcount -= 2; + + if (meta_id & ID_LARGE) { + if (bcount < 2) + return 0; + + c1 = *buffer++; + c2 = *buffer++; + meta_bc += (static_cast(c1) << 9) + (static_cast(c2) << 17); + bcount -= 2; + } + + if (bcount < meta_bc) + return 0; + + // if we got a sample rate, return it + + if ((meta_id & ID_UNIQUE) == ID_SAMPLE_RATE && meta_bc == 4) { + int sample_rate = static_cast(*buffer++); + sample_rate |= static_cast(*buffer++) << 8; + sample_rate |= static_cast(*buffer++) << 16; + + // only use 4th byte if it's really there + + if (!(meta_id & ID_ODD_SIZE)) + sample_rate |= static_cast(buffer[3] & 0x7f) << 24; + + return sample_rate; + } + + bcount -= meta_bc; + buffer += meta_bc; + } + + return 0; +} + +// Given a WavPack block (complete, but not including the 32-byte header), parse the metadata +// blocks until a DSD audio data block is found and return the sample-rate shift value +// contained there, or zero if no such block is found. The nominal sample rate of DSD audio +// files (found in the header) must be left-shifted by this amount to get the actual "byte" +// sample rate. Note that 8-bit bytes are the "atoms" of the DSD audio coding (for decoding, +// seeking, etc), so the shifted rate must be further multiplied by 8 to get the actual DSD +// bit sample rate. + +int WavPack::Properties::getDsdRateShifter(unsigned char const *buffer, int bcount) +{ + unsigned char meta_id, c1, c2; + int meta_bc; + + while (bcount >= 2) { + meta_id = *buffer++; + c1 = *buffer++; + + meta_bc = c1 << 1; + bcount -= 2; + + if (meta_id & ID_LARGE) { + if (bcount < 2) + return 0; + + c1 = *buffer++; + c2 = *buffer++; + meta_bc += (static_cast(c1) << 9) + (static_cast(c2) << 17); + bcount -= 2; + } + + if (bcount < meta_bc) + return 0; + + // if we got DSD block, return the specified rate shift amount + + if ((meta_id & ID_UNIQUE) == ID_DSD_BLOCK && meta_bc && *buffer <= 31) + return *buffer; + + bcount -= meta_bc; + buffer += meta_bc; + } + + return 0; } diff --git a/taglib/wavpack/wavpackproperties.h b/taglib/wavpack/wavpackproperties.h index e6acdcc3..ddc51f7d 100644 --- a/taglib/wavpack/wavpackproperties.h +++ b/taglib/wavpack/wavpackproperties.h @@ -139,6 +139,8 @@ namespace TagLib { void read(File *file, long streamLength); unsigned int seekFinalIndex(File *file, long streamLength); + int getNonStandardRate(unsigned char const *buffer, int bcount); + int getDsdRateShifter(unsigned char const *buffer, int bcount); class PropertiesPrivate; PropertiesPrivate *d; From ec9c49b9649a63abba10b1f37d32cefdbd8e3909 Mon Sep 17 00:00:00 2001 From: David Bryant Date: Wed, 1 Jul 2020 08:35:20 -0700 Subject: [PATCH 2/6] add test for WavPack DSD (issue #962) --- tests/data/dsd_stereo.wv | Bin 0 -> 52595 bytes tests/test_wavpack.cpp | 16 ++++++++++++++++ 2 files changed, 16 insertions(+) create mode 100644 tests/data/dsd_stereo.wv diff --git a/tests/data/dsd_stereo.wv b/tests/data/dsd_stereo.wv new file mode 100644 index 0000000000000000000000000000000000000000..80619270fd5d5851887eef497f535ece8427fe56 GIT binary patch literal 52595 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%0D}~_LAV2(o?6A%P~bc#88C^ z3SJTO^5Cpf-=?GdG~nWKmA`y9-$7%#U9!E_oj69U#;>)tnsl|y`Rq;=n7Vja{j^Fy zBfzc}2LQ(qE$+sh&y=_AI(W10L|}Vrr5d{zU$eYp_0H5j^?;}~9Df3lJpWE(lSL0l z!m>s4cH7L9Ic|WtlCS}v{UK9g(R%WifQR(_KXe9b+pZ%jVCsx8`vGqlF>^lA*Br1V zG^r$y8Bs8Fc(l?qQ+R;~cz?UYuoLz!qwec#9)o@+t?q&q#%tvL&q?8HBSbec3n(a5 z<*%We2zM#Nklg+4dM#9i)N)4{18aE7+x&Zrwz-m4Int@()gtw`bMObHGnbRyj=2&-S52K0uu+~J`Yea5p4A?NR+V->B1svVX-o}|n zIkvpHa}8-~AGtkoevyzuD%Ey zEvtIj?qx!*rS^9RH{4)4+b;A6%L4+HRv`9j{n!3R zk2<&t*K}_L7)L{qWfcE_rzr`fvOW#$X<0`5u{Ah-!8^~2HF9-ATSjmRadUm(iGvla z>Y>)b_&p8po3oT+J*J~LmuDz zI%`oR6GZQqRr}Sg>)Cqk>=@KyH9vQ?cN>K)`}K+4(vX7Sw_6(fp{<-xJ-8NKmi!Gw zbkzRXCMAZGRt92PAYzQ>qY5Go(KWGO8a#`R^362a^J5~Mqav0Up*nL(385}6XE#Ab z^rFuuwk%9Rj0Wn^VsDBx;Alljv$d0;{^+h220q@c@pLIrFZ{Chp4In%@xqw${|LuPm&f)tC>UI$b=`X z+q8SgQd!b+&Y))m->CV-t1^X9K?vLT1e*5V5lgwrkgo`f5HumFE@ejeo9gl8^S#5% zO$E_!#Lf)gaqlZIzP({OVcaE$)y8j#pB;XDv@$bL_8qz#qkH2Rf#p0o_@c?>z>|Gt zc3jk1^W|!UzG1Lkz#?VX`VD#`>S|W#rHkd-8=2*J;9kc&VSw=vmlYijO9f|ISB{6}@Ih@dQ4!^R@bju3oYEX$d+ugSE4%KXRYq z5NN7KQls)iVU0a?7nJh@JB%E)3bWAczZ77=^H*Z*Gyp&$C)GLnZ?OLhNitlcOdfF6 zAeKK7B@|abx;M*F@BE%MgqZ8WAqINZALs#qobx%_f3Fq(I{^Sp{{RR&2pJdzFhQaH zoP-U7{epP^X~2;|K5plAKM!F;C$y`u1psrGz5s}ik1*0t p*v!vOSl0>jAChpilZ!V1bWZ&L!^X`C4FIXCs{RjZQUA;V{ttRSp{f7? literal 0 HcmV?d00001 diff --git a/tests/test_wavpack.cpp b/tests/test_wavpack.cpp index 6c64f08d..07bf49b0 100644 --- a/tests/test_wavpack.cpp +++ b/tests/test_wavpack.cpp @@ -41,6 +41,7 @@ class TestWavPack : public CppUnit::TestFixture CPPUNIT_TEST_SUITE(TestWavPack); CPPUNIT_TEST(testNoLengthProperties); CPPUNIT_TEST(testMultiChannelProperties); + CPPUNIT_TEST(testDsdStereoProperties); CPPUNIT_TEST(testTaggedProperties); CPPUNIT_TEST(testFuzzedFile); CPPUNIT_TEST(testStripAndProperties); @@ -79,6 +80,21 @@ public: CPPUNIT_ASSERT_EQUAL(1031, f.audioProperties()->version()); } + void testDsdStereoProperties() + { + WavPack::File f(TEST_FILE_PATH_C("dsd_stereo.wv")); + CPPUNIT_ASSERT(f.audioProperties()); + CPPUNIT_ASSERT_EQUAL(0, f.audioProperties()->lengthInSeconds()); + CPPUNIT_ASSERT_EQUAL(200, f.audioProperties()->lengthInMilliseconds()); + CPPUNIT_ASSERT_EQUAL(2096, f.audioProperties()->bitrate()); + CPPUNIT_ASSERT_EQUAL(2, f.audioProperties()->channels()); + CPPUNIT_ASSERT_EQUAL(8, f.audioProperties()->bitsPerSample()); + CPPUNIT_ASSERT_EQUAL(true, f.audioProperties()->isLossless()); + CPPUNIT_ASSERT_EQUAL(352800, f.audioProperties()->sampleRate()); + CPPUNIT_ASSERT_EQUAL(70560U, f.audioProperties()->sampleFrames()); + CPPUNIT_ASSERT_EQUAL(1040, f.audioProperties()->version()); + } + void testTaggedProperties() { WavPack::File f(TEST_FILE_PATH_C("tagged.wv")); From 6806dc4cf28d435acfdd13d0f95d89be83d582e5 Mon Sep 17 00:00:00 2001 From: David Bryant Date: Thu, 2 Jul 2020 16:15:51 -0700 Subject: [PATCH 3/6] make WavPack seekFinalIndex() more robust to false triggers --- taglib/wavpack/wavpackproperties.cpp | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/taglib/wavpack/wavpackproperties.cpp b/taglib/wavpack/wavpackproperties.cpp index 5bc1af4b..a87a63be 100644 --- a/taglib/wavpack/wavpackproperties.cpp +++ b/taglib/wavpack/wavpackproperties.cpp @@ -256,7 +256,7 @@ unsigned int WavPack::Properties::seekFinalIndex(File *file, long streamLength) long offset = streamLength; while (offset >= 32) { - offset = file->rfind("wvpk", offset - 32); + offset = file->rfind("wvpk", offset - 4); if(offset == -1) return 0; @@ -266,13 +266,17 @@ unsigned int WavPack::Properties::seekFinalIndex(File *file, long streamLength) if(data.size() < 32) return 0; - const int version = data.toShort(8, false); - if(version < MIN_STREAM_VERS || version > MAX_STREAM_VERS) - return 0; - + const unsigned int blockSize = data.toUInt(4, false); const unsigned int blockIndex = data.toUInt(16, false); const unsigned int blockSamples = data.toUInt(20, false); const unsigned int flags = data.toUInt(24, false); + const int version = data.toShort(8, false); + + // try not to trigger on a spurious "wvpk" in WavPack binary block data + + if(version < MIN_STREAM_VERS || version > MAX_STREAM_VERS || (blockSize & 1) || + blockSize < 24 || blockSize >= 1048576 || blockSamples > 131072) + continue; if (blockSamples && (flags & FINAL_BLOCK)) return blockIndex + blockSamples; From 43bc11541da8fa6d80029845cab5834c06fe93ab Mon Sep 17 00:00:00 2001 From: David Bryant Date: Thu, 2 Jul 2020 17:22:03 -0700 Subject: [PATCH 4/6] correctly read very high sample rates from WavPack files --- taglib/wavpack/wavpackproperties.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/taglib/wavpack/wavpackproperties.cpp b/taglib/wavpack/wavpackproperties.cpp index a87a63be..8d7ab9ea 100644 --- a/taglib/wavpack/wavpackproperties.cpp +++ b/taglib/wavpack/wavpackproperties.cpp @@ -324,7 +324,7 @@ int WavPack::Properties::getNonStandardRate(unsigned char const *buffer, int bco // only use 4th byte if it's really there if (!(meta_id & ID_ODD_SIZE)) - sample_rate |= static_cast(buffer[3] & 0x7f) << 24; + sample_rate |= static_cast(*buffer & 0x7f) << 24; return sample_rate; } From d84e86da9cc7494f5e28b88b39b058c86eb7c974 Mon Sep 17 00:00:00 2001 From: Urs Fleisch Date: Tue, 22 Dec 2020 08:22:49 +0100 Subject: [PATCH 5/6] Clean up code to better match the TagLib style --- taglib/wavpack/wavpackproperties.cpp | 220 +++++++++++++-------------- taglib/wavpack/wavpackproperties.h | 2 - 2 files changed, 106 insertions(+), 116 deletions(-) diff --git a/taglib/wavpack/wavpackproperties.cpp b/taglib/wavpack/wavpackproperties.cpp index 8d7ab9ea..d5808be5 100644 --- a/taglib/wavpack/wavpackproperties.cpp +++ b/taglib/wavpack/wavpackproperties.cpp @@ -27,6 +27,7 @@ * http://www.mozilla.org/MPL/ * ***************************************************************************/ +#include #include #include @@ -138,13 +139,6 @@ unsigned int WavPack::Properties::sampleFrames() const // private members //////////////////////////////////////////////////////////////////////////////// -namespace -{ - const unsigned int sample_rates[] = { - 6000, 8000, 9600, 11025, 12000, 16000, 22050, 24000, - 32000, 44100, 48000, 64000, 88200, 96000, 192000, 0 }; -} - #define BYTES_STORED 3 #define MONO_FLAG 4 #define HYBRID_FLAG 8 @@ -169,6 +163,100 @@ namespace #define ID_LARGE 0x80 #define ID_SAMPLE_RATE (ID_OPTIONAL_DATA | 0x7) +namespace +{ + const unsigned int sampleRates[] = { + 6000, 8000, 9600, 11025, 12000, 16000, 22050, 24000, + 32000, 44100, 48000, 64000, 88200, 96000, 192000, 0 }; + + /*! + * Given a WavPack \a block (complete, but not including the 32-byte header), + * parse the metadata blocks until an \a id block is found and return the + * contained data, or zero if no such block is found. + * Supported values for \a id are ID_SAMPLE_RATE and ID_DSD_BLOCK. + */ + int getMetaDataChunk(const ByteVector &block, unsigned char id) + { + if(id != ID_SAMPLE_RATE && id != ID_DSD_BLOCK) + return 0; + + const int blockSize = static_cast(block.size()); + int index = 0; + + while(index + 1 < blockSize) { + const unsigned char metaId = static_cast(block[index]); + int metaBc = static_cast(block[index + 1]) << 1; + index += 2; + + if(metaId & ID_LARGE) { + if(index + 2 > blockSize) + return 0; + + metaBc += (static_cast(static_cast(block[index])) << 9) + + (static_cast(static_cast(block[index + 1])) << 17); + index += 2; + } + + if(index + metaBc > blockSize) + return 0; + + // if we got a sample rate, return it + + if(id == ID_SAMPLE_RATE && (metaId & ID_UNIQUE) == ID_SAMPLE_RATE && metaBc == 4) { + int sampleRate = static_cast(static_cast(block[index])); + sampleRate |= static_cast(static_cast(block[index + 1])) << 8; + sampleRate |= static_cast(static_cast(block[index + 2])) << 16; + + // only use 4th byte if it's really there + + if(!(metaId & ID_ODD_SIZE)) + sampleRate |= static_cast(static_cast(block[index + 3]) & 0x7f) << 24; + + return sampleRate; + } + + // if we got DSD block, return the specified rate shift amount + + if(id == ID_DSD_BLOCK && (metaId & ID_UNIQUE) == ID_DSD_BLOCK && metaBc > 0) { + const unsigned char rateShift = static_cast(block[index]); + if(rateShift <= 31) + return rateShift; + } + + index += metaBc; + } + + return 0; + } + + /*! + * Given a WavPack block (complete, but not including the 32-byte header), + * parse the metadata blocks until an ID_SAMPLE_RATE block is found and + * return the non-standard sample rate contained there, or zero if no such + * block is found. + */ + int getNonStandardRate(const ByteVector &block) + { + return getMetaDataChunk(block, ID_SAMPLE_RATE); + } + + /*! + * Given a WavPack block (complete, but not including the 32-byte header), + * parse the metadata blocks until a DSD audio data block is found and return + * the sample-rate shift value contained there, or zero if no such block is + * found. The nominal sample rate of DSD audio files (found in the header) + * must be left-shifted by this amount to get the actual "byte" sample rate. + * Note that 8-bit bytes are the "atoms" of the DSD audio coding (for + * decoding, seeking, etc), so the shifted rate must be further multiplied by + * 8 to get the actual DSD bit sample rate. + */ + int getDsdRateShifter(const ByteVector &block) + { + return getMetaDataChunk(block, ID_DSD_BLOCK); + } + +} + void WavPack::Properties::read(File *file, long streamLength) { long offset = 0; @@ -191,14 +279,14 @@ void WavPack::Properties::read(File *file, long streamLength) const unsigned int sampleFrames = data.toUInt(12, false); const unsigned int blockSamples = data.toUInt(20, false); const unsigned int flags = data.toUInt(24, false); - unsigned int sampleRate = sample_rates[(flags & SRATE_MASK) >> SRATE_LSB]; + unsigned int sampleRate = sampleRates[(flags & SRATE_MASK) >> SRATE_LSB]; - if (!blockSamples) { // ignore blocks with no samples + if(!blockSamples) { // ignore blocks with no samples offset += blockSize + 8; continue; } - if (blockSize < 24 || blockSize > 1048576) { + if(blockSize < 24 || blockSize > 1048576) { debug("WavPack::Properties::read() -- Invalid block header found."); break; } @@ -207,19 +295,19 @@ void WavPack::Properties::read(File *file, long streamLength) // to actually determine the sample rate. if(!sampleRate || (flags & DSD_FLAG)) { - const unsigned int adjusted_block_size = blockSize - 24; - const ByteVector block = file->readBlock(adjusted_block_size); + const unsigned int adjustedBlockSize = blockSize - 24; + const ByteVector block = file->readBlock(adjustedBlockSize); - if(block.size() < adjusted_block_size) { + if(block.size() < adjustedBlockSize) { debug("WavPack::Properties::read() -- block is too short."); break; } - if (!sampleRate) - sampleRate = getNonStandardRate(reinterpret_cast(block.data()), adjusted_block_size); + if(!sampleRate) + sampleRate = static_cast(getNonStandardRate(block)); - if (sampleRate && (flags & DSD_FLAG)) - sampleRate <<= getDsdRateShifter(reinterpret_cast(block.data()), adjusted_block_size); + if(sampleRate && (flags & DSD_FLAG)) + sampleRate <<= getDsdRateShifter(block); } if(flags & INITIAL_BLOCK) { @@ -228,7 +316,7 @@ void WavPack::Properties::read(File *file, long streamLength) break; d->bitsPerSample = ((flags & BYTES_STORED) + 1) * 8 - ((flags & SHIFT_MASK) >> SHIFT_LSB); - d->sampleRate = sampleRate; + d->sampleRate = static_cast(sampleRate); d->lossless = !(flags & HYBRID_FLAG); d->sampleFrames = sampleFrames; } @@ -284,99 +372,3 @@ unsigned int WavPack::Properties::seekFinalIndex(File *file, long streamLength) return 0; } - -// Given a WavPack block (complete, but not including the 32-byte header), parse the metadata -// blocks until an ID_SAMPLE_RATE block is found and return the non-standard sample rate -// contained there, or zero if no such block is found. - -int WavPack::Properties::getNonStandardRate(unsigned char const *buffer, int bcount) -{ - unsigned char meta_id, c1, c2; - int meta_bc; - - while (bcount >= 2) { - meta_id = *buffer++; - c1 = *buffer++; - - meta_bc = c1 << 1; - bcount -= 2; - - if (meta_id & ID_LARGE) { - if (bcount < 2) - return 0; - - c1 = *buffer++; - c2 = *buffer++; - meta_bc += (static_cast(c1) << 9) + (static_cast(c2) << 17); - bcount -= 2; - } - - if (bcount < meta_bc) - return 0; - - // if we got a sample rate, return it - - if ((meta_id & ID_UNIQUE) == ID_SAMPLE_RATE && meta_bc == 4) { - int sample_rate = static_cast(*buffer++); - sample_rate |= static_cast(*buffer++) << 8; - sample_rate |= static_cast(*buffer++) << 16; - - // only use 4th byte if it's really there - - if (!(meta_id & ID_ODD_SIZE)) - sample_rate |= static_cast(*buffer & 0x7f) << 24; - - return sample_rate; - } - - bcount -= meta_bc; - buffer += meta_bc; - } - - return 0; -} - -// Given a WavPack block (complete, but not including the 32-byte header), parse the metadata -// blocks until a DSD audio data block is found and return the sample-rate shift value -// contained there, or zero if no such block is found. The nominal sample rate of DSD audio -// files (found in the header) must be left-shifted by this amount to get the actual "byte" -// sample rate. Note that 8-bit bytes are the "atoms" of the DSD audio coding (for decoding, -// seeking, etc), so the shifted rate must be further multiplied by 8 to get the actual DSD -// bit sample rate. - -int WavPack::Properties::getDsdRateShifter(unsigned char const *buffer, int bcount) -{ - unsigned char meta_id, c1, c2; - int meta_bc; - - while (bcount >= 2) { - meta_id = *buffer++; - c1 = *buffer++; - - meta_bc = c1 << 1; - bcount -= 2; - - if (meta_id & ID_LARGE) { - if (bcount < 2) - return 0; - - c1 = *buffer++; - c2 = *buffer++; - meta_bc += (static_cast(c1) << 9) + (static_cast(c2) << 17); - bcount -= 2; - } - - if (bcount < meta_bc) - return 0; - - // if we got DSD block, return the specified rate shift amount - - if ((meta_id & ID_UNIQUE) == ID_DSD_BLOCK && meta_bc && *buffer <= 31) - return *buffer; - - bcount -= meta_bc; - buffer += meta_bc; - } - - return 0; -} diff --git a/taglib/wavpack/wavpackproperties.h b/taglib/wavpack/wavpackproperties.h index ddc51f7d..e6acdcc3 100644 --- a/taglib/wavpack/wavpackproperties.h +++ b/taglib/wavpack/wavpackproperties.h @@ -139,8 +139,6 @@ namespace TagLib { void read(File *file, long streamLength); unsigned int seekFinalIndex(File *file, long streamLength); - int getNonStandardRate(unsigned char const *buffer, int bcount); - int getDsdRateShifter(unsigned char const *buffer, int bcount); class PropertiesPrivate; PropertiesPrivate *d; From a00b3499b4a762ed038a75854505acb0e6a6b20f Mon Sep 17 00:00:00 2001 From: Urs Fleisch Date: Tue, 22 Dec 2020 13:16:21 +0100 Subject: [PATCH 6/6] WavPack: Add test with non-standard sample rate --- tests/data/non_standard_rate.wv | Bin 0 -> 132 bytes tests/test_wavpack.cpp | 16 ++++++++++++++++ 2 files changed, 16 insertions(+) create mode 100644 tests/data/non_standard_rate.wv diff --git a/tests/data/non_standard_rate.wv b/tests/data/non_standard_rate.wv new file mode 100644 index 0000000000000000000000000000000000000000..ccc90277e3c0b92e4840f41b2d7057e4a5cf0101 GIT binary patch literal 132 zcmXRfE6A>4U|d7Hsho><19C>G@D<~G5Sfg#*6%rz~y zL_q+kj*)?h;RQ1T!vcN=1{MYZhLpsT#2la+CI)68pOp=47?en70;x=80&%q%x`6cm O`MUr0nbxeU-U0wcI2fn^ literal 0 HcmV?d00001 diff --git a/tests/test_wavpack.cpp b/tests/test_wavpack.cpp index 07bf49b0..591529fb 100644 --- a/tests/test_wavpack.cpp +++ b/tests/test_wavpack.cpp @@ -42,6 +42,7 @@ class TestWavPack : public CppUnit::TestFixture CPPUNIT_TEST(testNoLengthProperties); CPPUNIT_TEST(testMultiChannelProperties); CPPUNIT_TEST(testDsdStereoProperties); + CPPUNIT_TEST(testNonStandardRateProperties); CPPUNIT_TEST(testTaggedProperties); CPPUNIT_TEST(testFuzzedFile); CPPUNIT_TEST(testStripAndProperties); @@ -95,6 +96,21 @@ public: CPPUNIT_ASSERT_EQUAL(1040, f.audioProperties()->version()); } + void testNonStandardRateProperties() + { + WavPack::File f(TEST_FILE_PATH_C("non_standard_rate.wv")); + CPPUNIT_ASSERT(f.audioProperties()); + CPPUNIT_ASSERT_EQUAL(3, f.audioProperties()->lengthInSeconds()); + CPPUNIT_ASSERT_EQUAL(3675, f.audioProperties()->lengthInMilliseconds()); + CPPUNIT_ASSERT_EQUAL(0, f.audioProperties()->bitrate()); + CPPUNIT_ASSERT_EQUAL(2, f.audioProperties()->channels()); + CPPUNIT_ASSERT_EQUAL(16, f.audioProperties()->bitsPerSample()); + CPPUNIT_ASSERT_EQUAL(true, f.audioProperties()->isLossless()); + CPPUNIT_ASSERT_EQUAL(1000, f.audioProperties()->sampleRate()); + CPPUNIT_ASSERT_EQUAL(3675U, f.audioProperties()->sampleFrames()); + CPPUNIT_ASSERT_EQUAL(1040, f.audioProperties()->version()); + } + void testTaggedProperties() { WavPack::File f(TEST_FILE_PATH_C("tagged.wv"));