From 3823afcc879836cfa0438588eb0507e9dd6b934b Mon Sep 17 00:00:00 2001 From: Tsuda Kageyu Date: Thu, 21 May 2015 15:12:31 +0900 Subject: [PATCH] Ogg Vorbis: AudioProperties improvements Add lengthInSeconds(), lengthInMilliseconds() properties. (#503) Remove some data members which are not needed to carry. Add some tests for audio properties. Add some supplementary comments. --- taglib/ogg/vorbis/vorbisproperties.cpp | 65 +++++++++++++++++--------- taglib/ogg/vorbis/vorbisproperties.h | 41 ++++++++++++++-- tests/test_ogg.cpp | 16 +++++++ 3 files changed, 98 insertions(+), 24 deletions(-) diff --git a/taglib/ogg/vorbis/vorbisproperties.cpp b/taglib/ogg/vorbis/vorbisproperties.cpp index b5e88bfd..d9dfc0a8 100644 --- a/taglib/ogg/vorbis/vorbisproperties.cpp +++ b/taglib/ogg/vorbis/vorbisproperties.cpp @@ -36,9 +36,7 @@ using namespace TagLib; class Vorbis::Properties::PropertiesPrivate { public: - PropertiesPrivate(File *f, ReadStyle s) : - file(f), - style(s), + PropertiesPrivate() : length(0), bitrate(0), sampleRate(0), @@ -48,8 +46,6 @@ public: bitrateNominal(0), bitrateMinimum(0) {} - File *file; - ReadStyle style; int length; int bitrate; int sampleRate; @@ -72,10 +68,11 @@ namespace TagLib { // public members //////////////////////////////////////////////////////////////////////////////// -Vorbis::Properties::Properties(File *file, ReadStyle style) : AudioProperties(style) +Vorbis::Properties::Properties(File *file, ReadStyle style) : + AudioProperties(style), + d(new PropertiesPrivate()) { - d = new PropertiesPrivate(file, style); - read(); + read(file); } Vorbis::Properties::~Properties() @@ -84,13 +81,23 @@ Vorbis::Properties::~Properties() } int Vorbis::Properties::length() const +{ + return lengthInSeconds(); +} + +int Vorbis::Properties::lengthInSeconds() const +{ + return d->length / 1000; +} + +int Vorbis::Properties::lengthInMilliseconds() const { return d->length; } int Vorbis::Properties::bitrate() const { - return int(float(d->bitrate) / float(1000) + 0.5); + return d->bitrate; } int Vorbis::Properties::sampleRate() const @@ -127,11 +134,15 @@ int Vorbis::Properties::bitrateMinimum() const // private members //////////////////////////////////////////////////////////////////////////////// -void Vorbis::Properties::read() +void Vorbis::Properties::read(File *file) { // Get the identification header from the Ogg implementation. - ByteVector data = d->file->packet(0); + const ByteVector data = file->packet(0); + if(data.size() < 28) { + debug("Vorbis::Properties::read() -- data is too short."); + return; + } uint pos = 0; @@ -158,26 +169,38 @@ void Vorbis::Properties::read() pos += 4; d->bitrateMinimum = data.toUInt(pos, false); - - // TODO: Later this should be only the "fast" mode. - d->bitrate = d->bitrateNominal; + pos += 4; // Find the length of the file. See http://wiki.xiph.org/VorbisStreamLength/ // for my notes on the topic. - const Ogg::PageHeader *first = d->file->firstPageHeader(); - const Ogg::PageHeader *last = d->file->lastPageHeader(); + const Ogg::PageHeader *first = file->firstPageHeader(); + const Ogg::PageHeader *last = file->lastPageHeader(); if(first && last) { - long long start = first->absoluteGranularPosition(); - long long end = last->absoluteGranularPosition(); + const long long start = first->absoluteGranularPosition(); + const long long end = last->absoluteGranularPosition(); + + if(start >= 0 && end >= 0 && d->sampleRate > 0) { + const long long frameCount = end - start; - if(start >= 0 && end >= 0 && d->sampleRate > 0) - d->length = (int)((end - start) / (long long) d->sampleRate); - else + if(frameCount > 0) { + const double length = frameCount * 1000.0 / d->sampleRate; + + d->length = static_cast(length + 0.5); + d->bitrate = static_cast(file->length() * 8.0 / length + 0.5); + } + } + else { debug("Vorbis::Properties::read() -- Either the PCM values for the start or " "end of this file was incorrect or the sample rate is zero."); + } } else debug("Vorbis::Properties::read() -- Could not find valid first and last Ogg pages."); + + // Alternative to the actual average bitrate. + + if(d->bitrate == 0 && d->bitrateNominal > 0) + d->bitrate = static_cast(d->bitrateNominal / 1000.0 + 0.5); } diff --git a/taglib/ogg/vorbis/vorbisproperties.h b/taglib/ogg/vorbis/vorbisproperties.h index de46985b..9da0ac9d 100644 --- a/taglib/ogg/vorbis/vorbisproperties.h +++ b/taglib/ogg/vorbis/vorbisproperties.h @@ -67,11 +67,46 @@ 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; /*! @@ -101,7 +136,7 @@ namespace TagLib { Properties(const Properties &); Properties &operator=(const Properties &); - void read(); + void read(File *file); class PropertiesPrivate; PropertiesPrivate *d; diff --git a/tests/test_ogg.cpp b/tests/test_ogg.cpp index 8d69371e..f5f27bbc 100644 --- a/tests/test_ogg.cpp +++ b/tests/test_ogg.cpp @@ -20,6 +20,7 @@ class TestOGG : public CppUnit::TestFixture CPPUNIT_TEST(testSplitPackets); CPPUNIT_TEST(testDictInterface1); CPPUNIT_TEST(testDictInterface2); + CPPUNIT_TEST(testAudioProperties); CPPUNIT_TEST_SUITE_END(); public: @@ -104,6 +105,21 @@ public: delete f; } + void testAudioProperties() + { + Ogg::Vorbis::File f(TEST_FILE_PATH_C("empty.ogg")); + CPPUNIT_ASSERT(f.audioProperties()); + CPPUNIT_ASSERT_EQUAL(3, f.audioProperties()->length()); + CPPUNIT_ASSERT_EQUAL(3, f.audioProperties()->lengthInSeconds()); + CPPUNIT_ASSERT_EQUAL(3685, f.audioProperties()->lengthInMilliseconds()); + CPPUNIT_ASSERT_EQUAL(9, f.audioProperties()->bitrate()); + CPPUNIT_ASSERT_EQUAL(2, f.audioProperties()->channels()); + CPPUNIT_ASSERT_EQUAL(44100, f.audioProperties()->sampleRate()); + CPPUNIT_ASSERT_EQUAL(0, f.audioProperties()->vorbisVersion()); + CPPUNIT_ASSERT_EQUAL(0, f.audioProperties()->bitrateMaximum()); + CPPUNIT_ASSERT_EQUAL(112000, f.audioProperties()->bitrateNominal()); + CPPUNIT_ASSERT_EQUAL(0, f.audioProperties()->bitrateMinimum()); + } }; CPPUNIT_TEST_SUITE_REGISTRATION(TestOGG); -- 2.40.0