diff --git a/taglib/fileref.cpp b/taglib/fileref.cpp index 4403a5fb..2b5d5f23 100644 --- a/taglib/fileref.cpp +++ b/taglib/fileref.cpp @@ -265,7 +265,7 @@ File *FileRef::create(FileName fileName, bool readAudioProperties, return new MP4::File(fileName, readAudioProperties, audioPropertiesStyle); if(ext == "WMA" || ext == "ASF") return new ASF::File(fileName, readAudioProperties, audioPropertiesStyle); - if(ext == "AIF" || ext == "AIFF") + if(ext == "AIF" || ext == "AIFF" || ext == "AFC" || ext == "AIFC") return new RIFF::AIFF::File(fileName, readAudioProperties, audioPropertiesStyle); if(ext == "WAV") return new RIFF::WAV::File(fileName, readAudioProperties, audioPropertiesStyle); diff --git a/taglib/riff/aiff/aiffproperties.cpp b/taglib/riff/aiff/aiffproperties.cpp index 1afb4a99..63fed45f 100644 --- a/taglib/riff/aiff/aiffproperties.cpp +++ b/taglib/riff/aiff/aiffproperties.cpp @@ -38,16 +38,17 @@ public: sampleRate(0), channels(0), sampleWidth(0), - sampleFrames(0) - { - - } + sampleFrames(0) {} int length; int bitrate; int sampleRate; int channels; int sampleWidth; + + ByteVector compressionType; + String compressionName; + uint sampleFrames; }; @@ -96,12 +97,31 @@ TagLib::uint RIFF::AIFF::Properties::sampleFrames() const return d->sampleFrames; } +bool RIFF::AIFF::Properties::isAiffC() const +{ + return (!d->compressionType.isEmpty()); +} + +ByteVector RIFF::AIFF::Properties::compressionType() const +{ + return d->compressionType; +} + +String RIFF::AIFF::Properties::compressionName() const +{ + return d->compressionName; +} //////////////////////////////////////////////////////////////////////////////// // private members //////////////////////////////////////////////////////////////////////////////// void RIFF::AIFF::Properties::read(const ByteVector &data) { + if(data.size() < 18) { + debug("RIFF::AIFF::Properties::read() - \"COMM\" chunk is too short for AIFF."); + return; + } + d->channels = data.toShort(0U); d->sampleFrames = data.toUInt(2U); d->sampleWidth = data.toShort(6U); @@ -109,4 +129,9 @@ void RIFF::AIFF::Properties::read(const ByteVector &data) d->sampleRate = (int)sampleRate; d->bitrate = (int)((sampleRate * d->sampleWidth * d->channels) / 1000.0); d->length = d->sampleRate > 0 ? d->sampleFrames / d->sampleRate : 0; + + if(data.size() >= 23) { + d->compressionType = data.mid(18, 4); + d->compressionName = String(data.mid(23, static_cast(data[22]))); + } } diff --git a/taglib/riff/aiff/aiffproperties.h b/taglib/riff/aiff/aiffproperties.h index 68e90b79..d0778704 100644 --- a/taglib/riff/aiff/aiffproperties.h +++ b/taglib/riff/aiff/aiffproperties.h @@ -67,6 +67,30 @@ namespace TagLib { int sampleWidth() const; uint sampleFrames() const; + /*! + * Returns true if the file is in AIFF-C format, false if AIFF format. + */ + bool isAiffC() const; + + /*! + * Returns the compression type of the AIFF-C file. For example, "NONE" for + * not compressed, "ACE2" for ACE 2-to-1. + * + * If the file is in AIFF format, always returns an empty vector. + * + * \see isAiffC() + */ + ByteVector compressionType() const; + + /*! + * Returns the concrete compression name of the AIFF-C file. + * + * If the file is in AIFF format, always returns an empty string. + * + * \see isAiffC() + */ + String compressionName() const; + private: Properties(const Properties &); Properties &operator=(const Properties &); diff --git a/tests/data/alaw.aifc b/tests/data/alaw.aifc new file mode 100644 index 00000000..33b4ea2a Binary files /dev/null and b/tests/data/alaw.aifc differ diff --git a/tests/data/segfault.aif b/tests/data/segfault.aif new file mode 100644 index 00000000..5dce192b Binary files /dev/null and b/tests/data/segfault.aif differ diff --git a/tests/test_aiff.cpp b/tests/test_aiff.cpp index 00a12088..e90f5280 100644 --- a/tests/test_aiff.cpp +++ b/tests/test_aiff.cpp @@ -13,6 +13,8 @@ class TestAIFF : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(TestAIFF); CPPUNIT_TEST(testReading); + CPPUNIT_TEST(testAiffCProperties); + CPPUNIT_TEST(testReading); CPPUNIT_TEST_SUITE_END(); public: @@ -27,6 +29,24 @@ public: delete f; } + void testAiffCProperties() + { + ScopedFileCopy copy("alaw", ".aifc"); + string filename = copy.fileName(); + + RIFF::AIFF::File *f = new RIFF::AIFF::File(filename.c_str()); + CPPUNIT_ASSERT(f->audioProperties()->isAiffC()); + CPPUNIT_ASSERT_EQUAL(ByteVector("ALAW"), f->audioProperties()->compressionType()); + CPPUNIT_ASSERT_EQUAL(String("SGI CCITT G.711 A-law"), f->audioProperties()->compressionName()); + delete f; + } + + void testFuzzedFiles() + { + RIFF::AIFF::File f(TEST_FILE_PATH_C("segfault.aif")); + CPPUNIT_ASSERT(!f.isValid()); + } + }; CPPUNIT_TEST_SUITE_REGISTRATION(TestAIFF);