mirror of
https://github.com/taglib/taglib.git
synced 2025-05-27 21:20:26 -04:00
WavPack: AudioProperties improvements
Add lengthInSeconds(), lengthInMilliseconds() properties. (#503) Add isLossless() property. Support multi channel. (#92) Remove some data members which are not needed to carry. Add some tests for audio properties. Add some supplementary comments.
This commit is contained in:
parent
447a4739c5
commit
22f250eaa4
@ -272,10 +272,8 @@ void WavPack::File::read(bool readProperties, Properties::ReadStyle /* propertie
|
||||
|
||||
// Look for WavPack audio properties
|
||||
|
||||
if(readProperties) {
|
||||
seek(0);
|
||||
if(readProperties)
|
||||
d->properties = new Properties(this, length() - d->APESize);
|
||||
}
|
||||
}
|
||||
|
||||
long WavPack::File::findAPE()
|
||||
|
@ -33,53 +33,50 @@
|
||||
#include "wavpackproperties.h"
|
||||
#include "wavpackfile.h"
|
||||
|
||||
// Implementation of this class is based on the information at:
|
||||
// http://www.wavpack.com/file_format.txt
|
||||
|
||||
using namespace TagLib;
|
||||
|
||||
class WavPack::Properties::PropertiesPrivate
|
||||
{
|
||||
public:
|
||||
PropertiesPrivate(const ByteVector &d, long length, ReadStyle s) :
|
||||
data(d),
|
||||
streamLength(length),
|
||||
style(s),
|
||||
PropertiesPrivate() :
|
||||
length(0),
|
||||
bitrate(0),
|
||||
sampleRate(0),
|
||||
channels(0),
|
||||
version(0),
|
||||
bitsPerSample(0),
|
||||
sampleFrames(0),
|
||||
file(0) {}
|
||||
lossless(false),
|
||||
sampleFrames(0) {}
|
||||
|
||||
ByteVector data;
|
||||
long streamLength;
|
||||
ReadStyle style;
|
||||
int length;
|
||||
int bitrate;
|
||||
int sampleRate;
|
||||
int channels;
|
||||
int version;
|
||||
int bitsPerSample;
|
||||
bool lossless;
|
||||
uint sampleFrames;
|
||||
File *file;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// public members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
WavPack::Properties::Properties(const ByteVector &data, long streamLength, ReadStyle style) : AudioProperties(style)
|
||||
WavPack::Properties::Properties(const ByteVector & /*data*/, long /*streamLength*/, ReadStyle style) :
|
||||
AudioProperties(style),
|
||||
d(new PropertiesPrivate())
|
||||
{
|
||||
d = new PropertiesPrivate(data, streamLength, style);
|
||||
read();
|
||||
debug("WavPack::Properties::Properties() -- This constructor is no longer used.");
|
||||
}
|
||||
|
||||
WavPack::Properties::Properties(File *file, long streamLength, ReadStyle style) : AudioProperties(style)
|
||||
WavPack::Properties::Properties(File *file, long streamLength, ReadStyle style) :
|
||||
AudioProperties(style),
|
||||
d(new PropertiesPrivate())
|
||||
{
|
||||
ByteVector data = file->readBlock(32);
|
||||
d = new PropertiesPrivate(data, streamLength, style);
|
||||
d->file = file;
|
||||
read();
|
||||
read(file, streamLength);
|
||||
}
|
||||
|
||||
WavPack::Properties::~Properties()
|
||||
@ -88,6 +85,16 @@ WavPack::Properties::~Properties()
|
||||
}
|
||||
|
||||
int WavPack::Properties::length() const
|
||||
{
|
||||
return lengthInSeconds();
|
||||
}
|
||||
|
||||
int WavPack::Properties::lengthInSeconds() const
|
||||
{
|
||||
return d->length / 1000;
|
||||
}
|
||||
|
||||
int WavPack::Properties::lengthInMilliseconds() const
|
||||
{
|
||||
return d->length;
|
||||
}
|
||||
@ -117,6 +124,11 @@ int WavPack::Properties::bitsPerSample() const
|
||||
return d->bitsPerSample;
|
||||
}
|
||||
|
||||
bool WavPack::Properties::isLossless() const
|
||||
{
|
||||
return d->lossless;
|
||||
}
|
||||
|
||||
TagLib::uint WavPack::Properties::sampleFrames() const
|
||||
{
|
||||
return d->sampleFrames;
|
||||
@ -126,12 +138,16 @@ TagLib::uint WavPack::Properties::sampleFrames() const
|
||||
// private members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static const unsigned int sample_rates[] = {
|
||||
6000, 8000, 9600, 11025, 12000, 16000, 22050, 24000,
|
||||
32000, 44100, 48000, 64000, 88200, 96000, 192000, 0 };
|
||||
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 LOSSLESS_FLAG 8
|
||||
|
||||
#define SHIFT_LSB 13
|
||||
#define SHIFT_MASK (0x1fL << SHIFT_LSB)
|
||||
@ -144,44 +160,64 @@ static const unsigned int sample_rates[] = {
|
||||
|
||||
#define FINAL_BLOCK 0x1000
|
||||
|
||||
void WavPack::Properties::read()
|
||||
void WavPack::Properties::read(File *file, long streamLength)
|
||||
{
|
||||
if(!d->data.startsWith("wvpk"))
|
||||
return;
|
||||
long offset = 0;
|
||||
|
||||
d->version = d->data.toShort(8, false);
|
||||
if(d->version < MIN_STREAM_VERS || d->version > MAX_STREAM_VERS)
|
||||
return;
|
||||
while(true) {
|
||||
file->seek(offset);
|
||||
const ByteVector data = file->readBlock(32);
|
||||
|
||||
const unsigned int flags = d->data.toUInt(24, false);
|
||||
d->bitsPerSample = ((flags & BYTES_STORED) + 1) * 8 -
|
||||
((flags & SHIFT_MASK) >> SHIFT_LSB);
|
||||
d->sampleRate = sample_rates[(flags & SRATE_MASK) >> SRATE_LSB];
|
||||
d->channels = (flags & MONO_FLAG) ? 1 : 2;
|
||||
|
||||
unsigned int samples = d->data.toUInt(12, false);
|
||||
if(samples == ~0u) {
|
||||
if(d->file && d->style != Fast) {
|
||||
samples = seekFinalIndex();
|
||||
if(data.size() < 32) {
|
||||
debug("WavPack::Properties::read() -- data is too short.");
|
||||
break;
|
||||
}
|
||||
else {
|
||||
samples = 0;
|
||||
|
||||
if(!data.startsWith("wvpk")) {
|
||||
debug("WavPack::Properties::read() -- Block header not found.");
|
||||
break;
|
||||
}
|
||||
|
||||
const uint flags = data.toUInt(24, false);
|
||||
|
||||
if(offset == 0) {
|
||||
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->channels += (flags & MONO_FLAG) ? 1 : 2;
|
||||
|
||||
if(flags & FINAL_BLOCK)
|
||||
break;
|
||||
|
||||
const uint blockSize = data.toUInt(4, false);
|
||||
offset += blockSize + 8;
|
||||
}
|
||||
d->length = d->sampleRate > 0 ? (samples + (d->sampleRate / 2)) / d->sampleRate : 0;
|
||||
d->sampleFrames = samples;
|
||||
|
||||
d->bitrate = d->length > 0 ? ((d->streamLength * 8L) / d->length) / 1000 : 0;
|
||||
if(d->sampleFrames == ~0u)
|
||||
d->sampleFrames = seekFinalIndex(file, streamLength);
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int WavPack::Properties::seekFinalIndex()
|
||||
uint WavPack::Properties::seekFinalIndex(File *file, long streamLength)
|
||||
{
|
||||
const long offset = d->file->rfind("wvpk", d->streamLength);
|
||||
const long offset = file->rfind("wvpk", streamLength);
|
||||
if(offset == -1)
|
||||
return 0;
|
||||
|
||||
d->file->seek(offset);
|
||||
const ByteVector data = d->file->readBlock(32);
|
||||
file->seek(offset);
|
||||
const ByteVector data = file->readBlock(32);
|
||||
if(data.size() < 32)
|
||||
return 0;
|
||||
|
||||
@ -189,12 +225,12 @@ unsigned int WavPack::Properties::seekFinalIndex()
|
||||
if(version < MIN_STREAM_VERS || version > MAX_STREAM_VERS)
|
||||
return 0;
|
||||
|
||||
const unsigned int flags = data.toUInt(24, false);
|
||||
const uint flags = data.toUInt(24, false);
|
||||
if(!(flags & FINAL_BLOCK))
|
||||
return 0;
|
||||
|
||||
const unsigned int blockIndex = data.toUInt(16, false);
|
||||
const unsigned int blockSamples = data.toUInt(20, false);
|
||||
const uint blockIndex = data.toUInt(16, false);
|
||||
const uint blockSamples = data.toUInt(20, false);
|
||||
|
||||
return blockIndex + blockSamples;
|
||||
}
|
||||
|
@ -71,9 +71,36 @@ 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;
|
||||
|
||||
/*!
|
||||
@ -81,12 +108,24 @@ namespace TagLib {
|
||||
*/
|
||||
virtual int sampleRate() const;
|
||||
|
||||
/*!
|
||||
* Returns the number of audio channels.
|
||||
*/
|
||||
virtual int channels() const;
|
||||
|
||||
/*!
|
||||
* Returns number of bits per sample.
|
||||
* Returns the number of bits per audio sample.
|
||||
*/
|
||||
int bitsPerSample() const;
|
||||
|
||||
/*!
|
||||
* Returns whether or not the file is lossless encoded.
|
||||
*/
|
||||
bool isLossless() const;
|
||||
|
||||
/*!
|
||||
* Returns the total number of audio samples in file.
|
||||
*/
|
||||
uint sampleFrames() const;
|
||||
|
||||
/*!
|
||||
@ -98,8 +137,8 @@ namespace TagLib {
|
||||
Properties(const Properties &);
|
||||
Properties &operator=(const Properties &);
|
||||
|
||||
void read();
|
||||
unsigned int seekFinalIndex();
|
||||
void read(File *file, long streamLength);
|
||||
uint seekFinalIndex(File *file, long streamLength);
|
||||
|
||||
class PropertiesPrivate;
|
||||
PropertiesPrivate *d;
|
||||
|
BIN
tests/data/four_channels.wv
Normal file
BIN
tests/data/four_channels.wv
Normal file
Binary file not shown.
@ -12,27 +12,43 @@ using namespace TagLib;
|
||||
class TestWavPack : public CppUnit::TestFixture
|
||||
{
|
||||
CPPUNIT_TEST_SUITE(TestWavPack);
|
||||
CPPUNIT_TEST(testBasic);
|
||||
CPPUNIT_TEST(testLengthScan);
|
||||
CPPUNIT_TEST(testNoLengthProperties);
|
||||
CPPUNIT_TEST(testMultiChannelProperties);
|
||||
CPPUNIT_TEST(testFuzzedFile);
|
||||
CPPUNIT_TEST_SUITE_END();
|
||||
|
||||
public:
|
||||
|
||||
void testBasic()
|
||||
void testNoLengthProperties()
|
||||
{
|
||||
WavPack::File f(TEST_FILE_PATH_C("no_length.wv"));
|
||||
WavPack::Properties *props = f.audioProperties();
|
||||
CPPUNIT_ASSERT_EQUAL(44100, props->sampleRate());
|
||||
CPPUNIT_ASSERT_EQUAL(2, props->channels());
|
||||
CPPUNIT_ASSERT_EQUAL(1, props->bitrate());
|
||||
CPPUNIT_ASSERT_EQUAL(0x407, props->version());
|
||||
CPPUNIT_ASSERT(f.audioProperties());
|
||||
CPPUNIT_ASSERT_EQUAL(3, f.audioProperties()->length());
|
||||
CPPUNIT_ASSERT_EQUAL(3, f.audioProperties()->lengthInSeconds());
|
||||
CPPUNIT_ASSERT_EQUAL(3705, f.audioProperties()->lengthInMilliseconds());
|
||||
CPPUNIT_ASSERT_EQUAL(1, 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(44100, f.audioProperties()->sampleRate());
|
||||
CPPUNIT_ASSERT_EQUAL(163392U, f.audioProperties()->sampleFrames());
|
||||
CPPUNIT_ASSERT_EQUAL(1031, f.audioProperties()->version());
|
||||
}
|
||||
|
||||
void testLengthScan()
|
||||
void testMultiChannelProperties()
|
||||
{
|
||||
WavPack::File f(TEST_FILE_PATH_C("no_length.wv"));
|
||||
WavPack::Properties *props = f.audioProperties();
|
||||
CPPUNIT_ASSERT_EQUAL(4, props->length());
|
||||
WavPack::File f(TEST_FILE_PATH_C("four_channels.wv"));
|
||||
CPPUNIT_ASSERT(f.audioProperties());
|
||||
CPPUNIT_ASSERT_EQUAL(3, f.audioProperties()->length());
|
||||
CPPUNIT_ASSERT_EQUAL(3, f.audioProperties()->lengthInSeconds());
|
||||
CPPUNIT_ASSERT_EQUAL(3833, f.audioProperties()->lengthInMilliseconds());
|
||||
CPPUNIT_ASSERT_EQUAL(112, f.audioProperties()->bitrate());
|
||||
CPPUNIT_ASSERT_EQUAL(4, f.audioProperties()->channels());
|
||||
CPPUNIT_ASSERT_EQUAL(16, f.audioProperties()->bitsPerSample());
|
||||
CPPUNIT_ASSERT_EQUAL(false, f.audioProperties()->isLossless());
|
||||
CPPUNIT_ASSERT_EQUAL(44100, f.audioProperties()->sampleRate());
|
||||
CPPUNIT_ASSERT_EQUAL(169031U, f.audioProperties()->sampleFrames());
|
||||
CPPUNIT_ASSERT_EQUAL(1031, f.audioProperties()->version());
|
||||
}
|
||||
|
||||
void testFuzzedFile()
|
||||
|
Loading…
Reference in New Issue
Block a user