diff --git a/taglib/riff/wav/wavfile.cpp b/taglib/riff/wav/wavfile.cpp index 8c7cdb85..26d9819b 100644 --- a/taglib/riff/wav/wavfile.cpp +++ b/taglib/riff/wav/wavfile.cpp @@ -55,6 +55,11 @@ public: bool hasID3v2 { false }; bool hasInfo { false }; + bool hasiXML { false }; + bool hasBEXT { false }; + + String iXMLData; + ByteVector bextData; }; //////////////////////////////////////////////////////////////////////////////// @@ -108,6 +113,26 @@ RIFF::Info::Tag *RIFF::WAV::File::InfoTag() const return d->tag.access(InfoIndex, false); } +String RIFF::WAV::File::iXMLData() const +{ + return d->iXMLData; +} + +void RIFF::WAV::File::setiXMLData(const String &data) +{ + d->iXMLData = data; +} + +ByteVector RIFF::WAV::File::BEXTData() const +{ + return d->bextData; +} + +void RIFF::WAV::File::setBEXTData(const ByteVector &data) +{ + d->bextData = data; +} + void RIFF::WAV::File::strip(TagTypes tags) { removeTagChunks(tags); @@ -160,6 +185,26 @@ bool RIFF::WAV::File::save(TagTypes tags, StripTags strip, ID3v2::Version versio if(strip == StripOthers) File::strip(static_cast(AllTags & ~tags)); + if(!d->bextData.isEmpty()) { + removeChunk("bext"); + setChunkData("bext", d->bextData); + d->hasBEXT = true; + } + else if(d->hasBEXT) { + removeChunk("bext"); + d->hasBEXT = false; + } + + if(!d->iXMLData.isEmpty()) { + removeChunk("iXML"); + setChunkData("iXML", d->iXMLData.data(String::UTF8)); + d->hasiXML = true; + } + else if(d->hasiXML) { + removeChunk("iXML"); + d->hasiXML = false; + } + if(tags & ID3v2) { removeTagChunks(ID3v2); @@ -191,6 +236,16 @@ bool RIFF::WAV::File::hasInfoTag() const return d->hasInfo; } +bool RIFF::WAV::File::hasiXMLData() const +{ + return d->hasiXML; +} + +bool RIFF::WAV::File::hasBEXTData() const +{ + return d->hasBEXT; +} + //////////////////////////////////////////////////////////////////////////////// // private members //////////////////////////////////////////////////////////////////////////////// @@ -219,6 +274,14 @@ void RIFF::WAV::File::read(bool readProperties) } } } + else if(name == "iXML") { + d->hasiXML = true; + d->iXMLData = String(chunkData(i)); + } + else if(name == "bext") { + d->hasBEXT = true; + d->bextData = chunkData(i); + } } if(!d->tag[ID3v2Index]) diff --git a/taglib/riff/wav/wavfile.h b/taglib/riff/wav/wavfile.h index 32330e51..5d34b2c4 100644 --- a/taglib/riff/wav/wavfile.h +++ b/taglib/riff/wav/wavfile.h @@ -134,6 +134,42 @@ namespace TagLib { */ Info::Tag *InfoTag() const; + /*! + * Returns the raw iXML chunk data as a String. + * Empty if no iXML chunk is present. + * + * \see setiXMLData() + * \see hasiXMLData() + */ + String iXMLData() const; + + /*! + * Sets the iXML chunk data. Pass an empty string to remove the + * iXML chunk on save. + * + * \see iXMLData() + * \see hasiXMLData() + */ + void setiXMLData(const String &data); + + /*! + * Returns the raw BEXT (Broadcast Audio Extension) chunk data + * as a ByteVector. Empty if no BEXT chunk is present. + * + * \see setBEXTData() + * \see hasBEXTData() + */ + ByteVector BEXTData() const; + + /*! + * Sets the BEXT chunk data. Pass an empty ByteVector to remove + * the BEXT chunk on save. + * + * \see BEXTData() + * \see hasBEXTData() + */ + void setBEXTData(const ByteVector &data); + /*! * This will strip the tags that match the OR-ed together TagTypes from the * file. By default it strips all tags. It returns \c true if the tags are @@ -191,6 +227,20 @@ namespace TagLib { */ bool hasInfoTag() const; + /*! + * Returns whether or not the file on disk actually has an iXML chunk. + * + * \see iXMLTag + */ + bool hasiXMLData() const; + + /*! + * Returns whether or not the file on disk actually has a BEXT chunk. + * + * \see bextTag + */ + bool hasBEXTData() const; + /*! * Returns whether or not the given \a stream can be opened as a WAV * file. diff --git a/tests/test_wav.cpp b/tests/test_wav.cpp index 5491dd67..5782a3c7 100644 --- a/tests/test_wav.cpp +++ b/tests/test_wav.cpp @@ -61,6 +61,10 @@ class TestWAV : public CppUnit::TestFixture CPPUNIT_TEST(testWaveFormatExtensible); CPPUNIT_TEST(testInvalidChunk); CPPUNIT_TEST(testRIFFInfoProperties); + CPPUNIT_TEST(testBEXTTag); + CPPUNIT_TEST(testBEXTTagWithOtherTags); + CPPUNIT_TEST(testiXMLTag); + CPPUNIT_TEST(testiXMLTagWithOtherTags); CPPUNIT_TEST_SUITE_END(); public: @@ -482,6 +486,151 @@ public: } } + void testBEXTTag() + { + ScopedFileCopy copy("empty", ".wav"); + string filename = copy.fileName(); + + { + RIFF::WAV::File f(filename.c_str()); + CPPUNIT_ASSERT(f.isValid()); + CPPUNIT_ASSERT(!f.hasBEXTData()); + CPPUNIT_ASSERT(f.BEXTData().isEmpty()); + + f.setBEXTData(ByteVector("test bext data")); + f.save(); + CPPUNIT_ASSERT(f.hasBEXTData()); + } + { + RIFF::WAV::File f(filename.c_str()); + CPPUNIT_ASSERT(f.isValid()); + CPPUNIT_ASSERT(f.hasBEXTData()); + CPPUNIT_ASSERT_EQUAL(ByteVector("test bext data"), f.BEXTData()); + + f.setBEXTData(ByteVector()); + f.save(); + CPPUNIT_ASSERT(!f.hasBEXTData()); + } + { + RIFF::WAV::File f(filename.c_str()); + CPPUNIT_ASSERT(f.isValid()); + CPPUNIT_ASSERT(!f.hasBEXTData()); + CPPUNIT_ASSERT(f.BEXTData().isEmpty()); + } + + // Check if file without BEXT is same as original empty file + const ByteVector origData = PlainFile(TEST_FILE_PATH_C("empty.wav")).readAll(); + const ByteVector fileData = PlainFile(filename.c_str()).readAll(); + CPPUNIT_ASSERT(origData == fileData); + } + + void testBEXTTagWithOtherTags() + { + ScopedFileCopy copy("empty", ".wav"); + string filename = copy.fileName(); + + { + RIFF::WAV::File f(filename.c_str()); + f.ID3v2Tag()->setTitle("ID3v2 Title"); + f.InfoTag()->setTitle("INFO Title"); + f.setBEXTData(ByteVector("bext payload")); + f.save(); + } + { + RIFF::WAV::File f(filename.c_str()); + CPPUNIT_ASSERT(f.hasID3v2Tag()); + CPPUNIT_ASSERT(f.hasInfoTag()); + CPPUNIT_ASSERT(f.hasBEXTData()); + CPPUNIT_ASSERT_EQUAL(String("ID3v2 Title"), f.ID3v2Tag()->title()); + CPPUNIT_ASSERT_EQUAL(String("INFO Title"), f.InfoTag()->title()); + CPPUNIT_ASSERT_EQUAL(ByteVector("bext payload"), f.BEXTData()); + } + } + + void testiXMLTag() + { + ScopedFileCopy copy("empty", ".wav"); + string filename = copy.fileName(); + + { + RIFF::WAV::File f(filename.c_str()); + CPPUNIT_ASSERT(f.isValid()); + CPPUNIT_ASSERT(!f.hasiXMLData()); + CPPUNIT_ASSERT(f.iXMLData().isEmpty()); + + f.setiXMLData("1.0"); + f.save(); + CPPUNIT_ASSERT(f.hasiXMLData()); + } + { + RIFF::WAV::File f(filename.c_str()); + CPPUNIT_ASSERT(f.isValid()); + CPPUNIT_ASSERT(f.hasiXMLData()); + CPPUNIT_ASSERT_EQUAL( + String("1.0"), + f.iXMLData()); + + f.setiXMLData(String()); + f.save(); + CPPUNIT_ASSERT(!f.hasiXMLData()); + } + { + RIFF::WAV::File f(filename.c_str()); + CPPUNIT_ASSERT(f.isValid()); + CPPUNIT_ASSERT(!f.hasiXMLData()); + CPPUNIT_ASSERT(f.iXMLData().isEmpty()); + } + + // Check if file without iXML is same as original empty file + const ByteVector origData = PlainFile(TEST_FILE_PATH_C("empty.wav")).readAll(); + const ByteVector fileData = PlainFile(filename.c_str()).readAll(); + CPPUNIT_ASSERT(origData == fileData); + } + + void testiXMLTagWithOtherTags() + { + ScopedFileCopy copy("empty", ".wav"); + string filename = copy.fileName(); + + { + RIFF::WAV::File f(filename.c_str()); + f.ID3v2Tag()->setTitle("ID3v2 Title"); + f.setiXMLData("1"); + f.setBEXTData(ByteVector("bext data")); + f.save(); + } + { + RIFF::WAV::File f(filename.c_str()); + CPPUNIT_ASSERT(f.hasID3v2Tag()); + CPPUNIT_ASSERT(f.hasiXMLData()); + CPPUNIT_ASSERT(f.hasBEXTData()); + CPPUNIT_ASSERT_EQUAL(String("ID3v2 Title"), f.ID3v2Tag()->title()); + CPPUNIT_ASSERT_EQUAL( + String("1"), + f.iXMLData()); + CPPUNIT_ASSERT_EQUAL(ByteVector("bext data"), f.BEXTData()); + + f.setiXMLData(String()); + f.setBEXTData(ByteVector()); + f.strip(); + CPPUNIT_ASSERT(f.save()); + } + { + RIFF::WAV::File f(filename.c_str()); + CPPUNIT_ASSERT(f.isValid()); + CPPUNIT_ASSERT(!f.hasID3v2Tag()); + CPPUNIT_ASSERT(!f.hasiXMLData()); + CPPUNIT_ASSERT(f.iXMLData().isEmpty()); + CPPUNIT_ASSERT(!f.hasBEXTData()); + CPPUNIT_ASSERT(f.BEXTData().isEmpty()); + } + + // Check if file without tags is same as original empty file + const ByteVector origData = PlainFile(TEST_FILE_PATH_C("empty.wav")).readAll(); + const ByteVector fileData = PlainFile(filename.c_str()).readAll(); + CPPUNIT_ASSERT(origData == fileData); + } + }; CPPUNIT_TEST_SUITE_REGISTRATION(TestWAV);