diff --git a/taglib/riff/wav/infotag.cpp b/taglib/riff/wav/infotag.cpp index 6caedc29..8fe8c52f 100644 --- a/taglib/riff/wav/infotag.cpp +++ b/taglib/riff/wav/infotag.cpp @@ -27,6 +27,8 @@ #include +#include "tbytevector.h" +#include "tpropertymap.h" #include "riffutils.h" using namespace TagLib; @@ -164,6 +166,86 @@ bool RIFF::Info::Tag::isEmpty() const return d->fieldListMap.isEmpty(); } +namespace +{ + const Map propertyKeyForId = { + {"IPRD", "ALBUM"}, + {"IENG", "ARRANGER"}, + {"IART", "ARTIST"}, + {"IBSU", "ARTISTWEBPAGE"}, + {"IBPM", "BPM"}, + {"ICMT", "COMMENT"}, + {"IMUS", "COMPOSER"}, + {"ICOP", "COPYRIGHT"}, + {"ICRD", "DATE"}, + {"PRT1", "DISCSUBTITLE"}, + {"ITCH", "ENCODEDBY"}, + {"ISFT", "ENCODING"}, + {"IDIT", "ENCODINGTIME"}, + {"IGNR", "GENRE"}, + {"ISRC", "ISRC"}, + {"IPUB", "LABEL"}, + {"ILNG", "LANGUAGE"}, + {"IWRI", "LYRICIST"}, + {"IMED", "MEDIA"}, + {"ISTR", "PERFORMER"}, + {"ICNT", "RELEASECOUNTRY"}, + {"IEDT", "REMIXER"}, + {"INAM", "TITLE"}, + {"IPRT", "TRACKNUMBER"} + }; +} // namespace + +PropertyMap RIFF::Info::Tag::properties() const +{ + PropertyMap props; + for(const auto &[id, value] : std::as_const(d->fieldListMap)) { + String key = propertyKeyForId.value(id); + if(!key.isEmpty()) { + props[key].append(value); + } + else { + props.addUnsupportedData(key); + } + } + return props; +} + +void RIFF::Info::Tag::removeUnsupportedProperties(const StringList &props) +{ + for(const auto &id : props) + d->fieldListMap.erase(id.data(String::Latin1)); +} + +PropertyMap RIFF::Info::Tag::setProperties(const PropertyMap &props) +{ + static Map idForPropertyKey; + if(idForPropertyKey.isEmpty()) { + for(const auto &[id, key] : propertyKeyForId) { + idForPropertyKey[key] = id; + } + } + + const PropertyMap origProps = properties(); + for(const auto &[key, _] : origProps) { + if(!props.contains(key) || props.value(key).isEmpty()) { + d->fieldListMap.erase(idForPropertyKey.value(key)); + } + } + + PropertyMap ignoredProps; + for(const auto &[key, value] : props) { + ByteVector id = idForPropertyKey.value(key); + if(!id.isEmpty() && !value.isEmpty()) { + d->fieldListMap[id] = value.front(); + } + else { + ignoredProps.insert(key, value); + } + } + return ignoredProps; +} + FieldListMap RIFF::Info::Tag::fieldListMap() const { return d->fieldListMap; diff --git a/taglib/riff/wav/infotag.h b/taglib/riff/wav/infotag.h index efe29880..1935e4d1 100644 --- a/taglib/riff/wav/infotag.h +++ b/taglib/riff/wav/infotag.h @@ -130,6 +130,10 @@ namespace TagLib { bool isEmpty() const override; + PropertyMap properties() const override; + void removeUnsupportedProperties(const StringList &props) override; + PropertyMap setProperties(const PropertyMap &props) override; + /*! * Returns a copy of the internal fields of the tag. The returned map directly * reflects the contents of the "INFO" chunk. diff --git a/tests/test_wav.cpp b/tests/test_wav.cpp index 01bdf340..8e11afc2 100644 --- a/tests/test_wav.cpp +++ b/tests/test_wav.cpp @@ -60,6 +60,7 @@ class TestWAV : public CppUnit::TestFixture CPPUNIT_TEST(testPCMWithFactChunk); CPPUNIT_TEST(testWaveFormatExtensible); CPPUNIT_TEST(testInvalidChunk); + CPPUNIT_TEST(testRIFFInfoProperties); CPPUNIT_TEST_SUITE_END(); public: @@ -403,6 +404,84 @@ public: } } + void testRIFFInfoProperties() + { + PropertyMap tags; + tags["ALBUM"] = StringList("Album"); + tags["ARRANGER"] = StringList("Arranger"); + tags["ARTIST"] = StringList("Artist"); + tags["ARTISTWEBPAGE"] = StringList("Artist Webpage"); + tags["BPM"] = StringList("123"); + tags["COMMENT"] = StringList("Comment"); + tags["COMPOSER"] = StringList("Composer"); + tags["COPYRIGHT"] = StringList("2023 Copyright"); + tags["DATE"] = StringList("2023"); + tags["DISCSUBTITLE"] = StringList("Disc Subtitle"); + tags["ENCODEDBY"] = StringList("Encoded by"); + tags["ENCODING"] = StringList("Encoding"); + tags["ENCODINGTIME"] = StringList("2023-11-25 15:42:39"); + tags["GENRE"] = StringList("Genre"); + tags["ISRC"] = StringList("UKAAA0500001"); + tags["LABEL"] = StringList("Label"); + tags["LANGUAGE"] = StringList("eng"); + tags["LYRICIST"] = StringList("Lyricist"); + tags["MEDIA"] = StringList("Media"); + tags["PERFORMER"] = StringList("Performer"); + tags["RELEASECOUNTRY"] = StringList("Release Country"); + tags["REMIXER"] = StringList("Remixer"); + tags["TITLE"] = StringList("Title"); + tags["TRACKNUMBER"] = StringList("2/4"); + + ScopedFileCopy copy("empty", ".wav"); + { + RIFF::WAV::File f(copy.fileName().c_str()); + RIFF::Info::Tag *infoTag = f.InfoTag(); + CPPUNIT_ASSERT(infoTag->isEmpty()); + PropertyMap properties = infoTag->properties(); + CPPUNIT_ASSERT(properties.isEmpty()); + infoTag->setProperties(tags); + f.save(); + } + { + const RIFF::WAV::File f(copy.fileName().c_str()); + RIFF::Info::Tag *infoTag = f.InfoTag(); + CPPUNIT_ASSERT(!infoTag->isEmpty()); + PropertyMap properties = infoTag->properties(); + if (tags != properties) { + CPPUNIT_ASSERT_EQUAL(tags.toString(), properties.toString()); + } + CPPUNIT_ASSERT(tags == properties); + + const RIFF::Info::FieldListMap expectedFields = { + {"IPRD", "Album"}, + {"IENG", "Arranger"}, + {"IART", "Artist"}, + {"IBSU", "Artist Webpage"}, + {"IBPM", "123"}, + {"ICMT", "Comment"}, + {"IMUS", "Composer"}, + {"ICOP", "2023 Copyright"}, + {"ICRD", "2023"}, + {"PRT1", "Disc Subtitle"}, + {"ITCH", "Encoded by"}, + {"ISFT", "Encoding"}, + {"IDIT", "2023-11-25 15:42:39"}, + {"IGNR", "Genre"}, + {"ISRC", "UKAAA0500001"}, + {"IPUB", "Label"}, + {"ILNG", "eng"}, + {"IWRI", "Lyricist"}, + {"IMED", "Media"}, + {"ISTR", "Performer"}, + {"ICNT", "Release Country"}, + {"IEDT", "Remixer"}, + {"INAM", "Title"}, + {"IPRT", "2/4"} + }; + CPPUNIT_ASSERT(expectedFields == infoTag->fieldListMap()); + } + } + }; CPPUNIT_TEST_SUITE_REGISTRATION(TestWAV);