From fc6e02da354469b26f64d15ee406db5b4f2b6135 Mon Sep 17 00:00:00 2001 From: Birunthan Mohanathas Date: Thu, 2 Aug 2012 17:25:20 +0300 Subject: [PATCH 01/67] Update header comments to reflect c4163a2 --- taglib/mpeg/id3v2/id3v2framefactory.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/taglib/mpeg/id3v2/id3v2framefactory.h b/taglib/mpeg/id3v2/id3v2framefactory.h index 16419c76..762d7eb3 100644 --- a/taglib/mpeg/id3v2/id3v2framefactory.h +++ b/taglib/mpeg/id3v2/id3v2framefactory.h @@ -123,8 +123,7 @@ namespace TagLib { FrameFactory(); /*! - * Destroys the frame factory. In most cases this will never be called (as - * is typical of singletons). + * Destroys the frame factory. */ virtual ~FrameFactory(); From 60e82e669436007a6ad9dea86145b8f8dfe6b3b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Lalinsk=C3=BD?= Date: Fri, 17 Aug 2012 07:34:44 +0200 Subject: [PATCH 02/67] Ignore additional files --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 5f49ed13..70912fde 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,7 @@ cmake_install.cmake +cmake_uninstall.cmake Makefile +CTestTestfile.cmake CMakeFiles/ *.so *.so.* @@ -16,6 +18,7 @@ CMakeFiles/ /config.h /taglib.pc /tests/test_runner +/tests/Testing /taglib_config.h /taglib-config /bindings/c/taglib_c.pc From e86e5f906b79b4eb0afaebd7f1b7b4f7234ea07c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Lalinsk=C3=BD?= Date: Fri, 17 Aug 2012 07:50:48 +0200 Subject: [PATCH 03/67] ID3 frame IDs with 0 should be recognized as valid http://article.gmane.org/gmane.comp.kde.devel.taglib/2275 --- taglib/mpeg/id3v2/id3v2frame.cpp | 2 +- taglib/mpeg/id3v2/id3v2framefactory.cpp | 2 +- tests/data/w000.mp3 | Bin 0 -> 512 bytes tests/test_id3v2.cpp | 11 +++++++++++ 4 files changed, 13 insertions(+), 2 deletions(-) create mode 100644 tests/data/w000.mp3 diff --git a/taglib/mpeg/id3v2/id3v2frame.cpp b/taglib/mpeg/id3v2/id3v2frame.cpp index 3a5463f5..9c9c640c 100644 --- a/taglib/mpeg/id3v2/id3v2frame.cpp +++ b/taglib/mpeg/id3v2/id3v2frame.cpp @@ -71,7 +71,7 @@ namespace return false; for(ByteVector::ConstIterator it = frameID.begin(); it != frameID.end(); it++) { - if( (*it < 'A' || *it > 'Z') && (*it < '1' || *it > '9') ) { + if( (*it < 'A' || *it > 'Z') && (*it < '0' || *it > '9') ) { return false; } } diff --git a/taglib/mpeg/id3v2/id3v2framefactory.cpp b/taglib/mpeg/id3v2/id3v2framefactory.cpp index 38f46c56..bcf69e1a 100644 --- a/taglib/mpeg/id3v2/id3v2framefactory.cpp +++ b/taglib/mpeg/id3v2/id3v2framefactory.cpp @@ -107,7 +107,7 @@ Frame *FrameFactory::createFrame(const ByteVector &origData, Header *tagHeader) } for(ByteVector::ConstIterator it = frameID.begin(); it != frameID.end(); it++) { - if( (*it < 'A' || *it > 'Z') && (*it < '1' || *it > '9') ) { + if( (*it < 'A' || *it > 'Z') && (*it < '0' || *it > '9') ) { delete header; return 0; } diff --git a/tests/data/w000.mp3 b/tests/data/w000.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..f9c2261764526ff40aba618a1e9b9933acada5fd GIT binary patch literal 512 zcmdUqQA@)x6ore5`rwm4K<|s750-TE!M*9)BBSeCW@Pvz=}fz@G#PD5?Z5XFd=USE z2R@Q>a&K<3h^EILbb1qJX^PDTNK<)vV{1#9(F5I6-}nAu3@p6AwKcLxwXRV`=ae;M zOL9BaQmM2gtmDzvp%+~{RVXJveLl~}0Vg1sJn}GG{kYrd9gkNb+$H!mI5SISBwz3e z@(Yj{+bdJiyX|-yM@Y^9V-IXL<9QswT!UBHRLate&~^}pN}sr>8$pSbMi`#I%~5m? z*0v~=X*Y~9xGT@o-Cnf578>b(c#>bDK4>}q_ygkWFhSiB2>A1IyyefUko+%w1F}q5 ADgXcg literal 0 HcmV?d00001 diff --git a/tests/test_id3v2.cpp b/tests/test_id3v2.cpp index 27533655..9cf2e50e 100644 --- a/tests/test_id3v2.cpp +++ b/tests/test_id3v2.cpp @@ -69,6 +69,7 @@ class TestID3v2 : public CppUnit::TestFixture CPPUNIT_TEST(testDowngradeTo23); // CPPUNIT_TEST(testUpdateFullDate22); TODO TYE+TDA should be upgraded to TDRC together CPPUNIT_TEST(testCompressedFrameWithBrokenLength); + CPPUNIT_TEST(testW000); CPPUNIT_TEST(testPropertyInterface); CPPUNIT_TEST(testPropertyInterface2); CPPUNIT_TEST(testSaveAndStripID3v1ShouldNotAddFrameFromID3v1ToId3v2); @@ -552,6 +553,16 @@ public: CPPUNIT_ASSERT_EQUAL(TagLib::uint(86414), frame->picture().size()); } + void testW000() + { + MPEG::File f(TEST_FILE_PATH_C("w000.mp3"), false); + CPPUNIT_ASSERT(f.ID3v2Tag()->frameListMap().contains("W000")); + ID3v2::UrlLinkFrame *frame = + dynamic_cast(f.ID3v2Tag()->frameListMap()["W000"].front()); + CPPUNIT_ASSERT(frame); + CPPUNIT_ASSERT_EQUAL(String("lukas.lalinsky@example.com____"), frame->url()); + } + void testPropertyInterface() { ScopedFileCopy copy("rare_frames", ".mp3"); From eb63ee8ec62a250f0b23f7ac939653d387a054f0 Mon Sep 17 00:00:00 2001 From: "tsuda.kageyu@gmail.com" Date: Thu, 23 Aug 2012 11:09:22 +0900 Subject: [PATCH 04/67] Remove possible cross binary boundary delete --- taglib/mpeg/id3v1/id3v1tag.cpp | 8 ++++---- taglib/mpeg/id3v1/id3v1tag.h | 2 ++ 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/taglib/mpeg/id3v1/id3v1tag.cpp b/taglib/mpeg/id3v1/id3v1tag.cpp index 94d533ae..e9eb47bf 100644 --- a/taglib/mpeg/id3v1/id3v1tag.cpp +++ b/taglib/mpeg/id3v1/id3v1tag.cpp @@ -194,10 +194,10 @@ void ID3v1::Tag::setTrack(uint i) void ID3v1::Tag::setStringHandler(const StringHandler *handler) { - if(TagPrivate::stringHandler != &defaultStringHandler) - delete TagPrivate::stringHandler; - - TagPrivate::stringHandler = handler; + if (handler) + TagPrivate::stringHandler = handler; + else + TagPrivate::stringHandler = &defaultStringHandler; } //////////////////////////////////////////////////////////////////////////////// diff --git a/taglib/mpeg/id3v1/id3v1tag.h b/taglib/mpeg/id3v1/id3v1tag.h index a90ac531..ad3fed99 100644 --- a/taglib/mpeg/id3v1/id3v1tag.h +++ b/taglib/mpeg/id3v1/id3v1tag.h @@ -154,6 +154,8 @@ namespace TagLib { /*! * Sets the string handler that decides how the ID3v1 data will be * converted to and from binary data. + * If the parameter \a handler is null, default ISO-8859-1 handler + * is restored. * * \see StringHandler */ From fe8053c7d57c217fbce584fafada7a9b734fb171 Mon Sep 17 00:00:00 2001 From: "tsuda.kageyu@gmail.com" Date: Thu, 23 Aug 2012 11:40:32 +0900 Subject: [PATCH 05/67] Support broken Latin-1 encodings in ID3V2 --- taglib/mpeg/id3v2/frames/commentsframe.cpp | 9 +++- .../id3v2/frames/textidentificationframe.cpp | 6 ++- .../frames/unsynchronizedlyricsframe.cpp | 9 +++- taglib/mpeg/id3v2/id3v2frame.cpp | 7 ++- taglib/mpeg/id3v2/id3v2tag.cpp | 31 ++++++++++++ taglib/mpeg/id3v2/id3v2tag.h | 48 +++++++++++++++++++ 6 files changed, 103 insertions(+), 7 deletions(-) diff --git a/taglib/mpeg/id3v2/frames/commentsframe.cpp b/taglib/mpeg/id3v2/frames/commentsframe.cpp index 2c6c49f9..ec70e891 100644 --- a/taglib/mpeg/id3v2/frames/commentsframe.cpp +++ b/taglib/mpeg/id3v2/frames/commentsframe.cpp @@ -158,8 +158,13 @@ void CommentsFrame::parseFields(const ByteVector &data) ByteVectorList l = ByteVectorList::split(data.mid(4), textDelimiter(d->textEncoding), byteAlign, 2); if(l.size() == 2) { - d->description = String(l.front(), d->textEncoding); - d->text = String(l.back(), d->textEncoding); + if(d->textEncoding == String::Latin1) { + d->description = Tag::latin1StringHandler()->parse(l.front()); + d->text = Tag::latin1StringHandler()->parse(l.back()); + } else { + d->description = String(l.front(), d->textEncoding); + d->text = String(l.back(), d->textEncoding); + } } } diff --git a/taglib/mpeg/id3v2/frames/textidentificationframe.cpp b/taglib/mpeg/id3v2/frames/textidentificationframe.cpp index b6a02b06..d9f696b7 100644 --- a/taglib/mpeg/id3v2/frames/textidentificationframe.cpp +++ b/taglib/mpeg/id3v2/frames/textidentificationframe.cpp @@ -213,8 +213,10 @@ void TextIdentificationFrame::parseFields(const ByteVector &data) for(ByteVectorList::Iterator it = l.begin(); it != l.end(); it++) { if(!(*it).isEmpty()) { - String s(*it, d->textEncoding); - d->fieldList.append(s); + if(d->textEncoding == String::Latin1) + d->fieldList.append(Tag::latin1StringHandler()->parse(*it)); + else + d->fieldList.append(String(*it, d->textEncoding)); } } } diff --git a/taglib/mpeg/id3v2/frames/unsynchronizedlyricsframe.cpp b/taglib/mpeg/id3v2/frames/unsynchronizedlyricsframe.cpp index 9d76164d..0b140912 100644 --- a/taglib/mpeg/id3v2/frames/unsynchronizedlyricsframe.cpp +++ b/taglib/mpeg/id3v2/frames/unsynchronizedlyricsframe.cpp @@ -158,8 +158,13 @@ void UnsynchronizedLyricsFrame::parseFields(const ByteVector &data) ByteVectorList::split(data.mid(4), textDelimiter(d->textEncoding), byteAlign, 2); if(l.size() == 2) { - d->description = String(l.front(), d->textEncoding); - d->text = String(l.back(), d->textEncoding); + if(d->textEncoding == String::Latin1) { + d->description = Tag::latin1StringHandler()->parse(l.front()); + d->text = Tag::latin1StringHandler()->parse(l.back()); + } else { + d->description = String(l.front(), d->textEncoding); + d->text = String(l.back(), d->textEncoding); + } } } diff --git a/taglib/mpeg/id3v2/id3v2frame.cpp b/taglib/mpeg/id3v2/id3v2frame.cpp index 9c9c640c..e15adc8c 100644 --- a/taglib/mpeg/id3v2/id3v2frame.cpp +++ b/taglib/mpeg/id3v2/id3v2frame.cpp @@ -36,6 +36,7 @@ #include #include +#include "id3v2tag.h" #include "id3v2frame.h" #include "id3v2synchdata.h" #include "tpropertymap.h" @@ -273,7 +274,11 @@ String Frame::readStringField(const ByteVector &data, String::Type encoding, int if(end < *position) return String::null; - String str = String(data.mid(*position, end - *position), encoding); + String str; + if(encoding == String::Latin1) + str = Tag::latin1StringHandler()->parse(data.mid(*position, end - *position)); + else + str = String(data.mid(*position, end - *position), encoding); *position = end + delimiter.size(); diff --git a/taglib/mpeg/id3v2/id3v2tag.cpp b/taglib/mpeg/id3v2/id3v2tag.cpp index 662fd159..8b623f82 100644 --- a/taglib/mpeg/id3v2/id3v2tag.cpp +++ b/taglib/mpeg/id3v2/id3v2tag.cpp @@ -70,8 +70,26 @@ public: FrameListMap frameListMap; FrameList frameList; + + static const Latin1StringHandler *stringHandler; }; +static const Latin1StringHandler defaultStringHandler; +const ID3v2::Latin1StringHandler *ID3v2::Tag::TagPrivate::stringHandler = &defaultStringHandler; + +//////////////////////////////////////////////////////////////////////////////// +// StringHandler implementation +//////////////////////////////////////////////////////////////////////////////// + +Latin1StringHandler::~Latin1StringHandler() +{ +} + +String Latin1StringHandler::parse(const ByteVector &data) const +{ + return String(data, String::Latin1); +} + //////////////////////////////////////////////////////////////////////////////// // public members //////////////////////////////////////////////////////////////////////////////// @@ -584,6 +602,19 @@ ByteVector ID3v2::Tag::render(int version) const return d->header.render() + tagData; } +Latin1StringHandler const *ID3v2::Tag::latin1StringHandler() +{ + return TagPrivate::stringHandler; +} + +void ID3v2::Tag::setLatin1StringHandler(const Latin1StringHandler *handler) +{ + if(handler) + TagPrivate::stringHandler = handler; + else + TagPrivate::stringHandler = &defaultStringHandler; +} + //////////////////////////////////////////////////////////////////////////////// // protected members //////////////////////////////////////////////////////////////////////////////// diff --git a/taglib/mpeg/id3v2/id3v2tag.h b/taglib/mpeg/id3v2/id3v2tag.h index 94784e76..58bc5b5a 100644 --- a/taglib/mpeg/id3v2/id3v2tag.h +++ b/taglib/mpeg/id3v2/id3v2tag.h @@ -57,6 +57,35 @@ namespace TagLib { typedef List FrameList; typedef Map FrameListMap; + //! An abstraction for the ISO-8859-1 string to data encoding in ID3v2 tags. + + /*! + * ID3v2 tag can store strings in ISO-8859-1 (Latin1), and TagLib only + * supports genuine ISO-8859-1 by default. However, in practice, non + * ISO-8859-1 encodings are often used instead of ISO-8859-1, such as + * Windows-1252 for western languages, Shift_JIS for Japanese and so on. + * + * Here is an option to read such tags by subclassing this class, + * reimplementing parse() and setting your reimplementation as the default + * with ID3v2::Tag::setStringHandler(). + * + * \note Writing non-ISO-8859-1 tags is not implemented intentionally. + * Use UTF-16 or UTF-8 instead. + * + * \see ID3v2::Tag::setStringHandler() + */ + class TAGLIB_EXPORT Latin1StringHandler + { + public: + virtual ~Latin1StringHandler(); + + /*! + * Decode a string from \a data. The default implementation assumes that + * \a data is an ISO-8859-1 (Latin1) character array. + */ + virtual String parse(const ByteVector &data) const; + }; + //! The main class in the ID3v2 implementation /*! @@ -323,6 +352,25 @@ namespace TagLib { */ // BIC: combine with the above method ByteVector render(int version) const; + + /*! + * Gets the current string handler that decides how the "Latin-1" data + * will be converted to and from binary data. + * + * \see Latin1StringHandler + */ + static Latin1StringHandler const *latin1StringHandler(); + + /*! + * Sets the string handler that decides how the "Latin-1" data will be + * converted to and from binary data. + * + * If the parameter \a handler is null, default ISO-8859-1 handler + * is restored. + * + * \see Latin1StringHandler + */ + static void setLatin1StringHandler(const Latin1StringHandler *handler); protected: /*! From 4b4f70253b09f89149fa1d49af548a04410c3278 Mon Sep 17 00:00:00 2001 From: "tsuda.kageyu@gmail.com" Date: Thu, 23 Aug 2012 17:45:25 +0900 Subject: [PATCH 06/67] Comment update for ID3v1::Tag::setStringHandler() --- taglib/mpeg/id3v1/id3v1tag.h | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/taglib/mpeg/id3v1/id3v1tag.h b/taglib/mpeg/id3v1/id3v1tag.h index ad3fed99..9332d4dc 100644 --- a/taglib/mpeg/id3v1/id3v1tag.h +++ b/taglib/mpeg/id3v1/id3v1tag.h @@ -154,8 +154,11 @@ namespace TagLib { /*! * Sets the string handler that decides how the ID3v1 data will be * converted to and from binary data. - * If the parameter \a handler is null, default ISO-8859-1 handler - * is restored. + * If the parameter \a handler is null, the previous handler is + * released and default ISO-8859-1 handler is restored. + * + * \note The caller is responsible for deleting the previous handler + * as needed after it is released. * * \see StringHandler */ From 3fecdbf428c2db85ff81a599c6f6ee26130e788c Mon Sep 17 00:00:00 2001 From: "tsuda.kageyu@gmail.com" Date: Thu, 23 Aug 2012 17:57:00 +0900 Subject: [PATCH 07/67] Comment update for ID3v2::Tag::setLatin1StringHandler() --- taglib/mpeg/id3v2/id3v2tag.h | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/taglib/mpeg/id3v2/id3v2tag.h b/taglib/mpeg/id3v2/id3v2tag.h index 58bc5b5a..e6d6d4fd 100644 --- a/taglib/mpeg/id3v2/id3v2tag.h +++ b/taglib/mpeg/id3v2/id3v2tag.h @@ -364,10 +364,12 @@ namespace TagLib { /*! * Sets the string handler that decides how the "Latin-1" data will be * converted to and from binary data. + * If the parameter \a handler is null, the previous handler is + * released and default ISO-8859-1 handler is restored. + * + * \note The caller is responsible for deleting the previous handler + * as needed after it is released. * - * If the parameter \a handler is null, default ISO-8859-1 handler - * is restored. - * * \see Latin1StringHandler */ static void setLatin1StringHandler(const Latin1StringHandler *handler); From 6c0227ee13afb5a5aabe0c64c0a7613e62d516dd Mon Sep 17 00:00:00 2001 From: Tsuda Kageyu Date: Thu, 23 Aug 2012 19:58:21 +0900 Subject: [PATCH 08/67] Fix compilation errors with Visual Studio 2010 --- taglib/ape/apetag.cpp | 2 +- taglib/mpc/mpcproperties.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/taglib/ape/apetag.cpp b/taglib/ape/apetag.cpp index f1af0028..664ee3a8 100644 --- a/taglib/ape/apetag.cpp +++ b/taglib/ape/apetag.cpp @@ -260,7 +260,7 @@ PropertyMap APE::Tag::setProperties(const PropertyMap &origProps) bool APE::Tag::checkKey(const String &key) { - if(key.size() < 2 or key.size() > 16) + if(key.size() < 2 || key.size() > 16) return false; for(String::ConstIterator it = key.begin(); it != key.end(); it++) // only allow printable ASCII including space (32..127) diff --git a/taglib/mpc/mpcproperties.cpp b/taglib/mpc/mpcproperties.cpp index 7046db99..637bb480 100644 --- a/taglib/mpc/mpcproperties.cpp +++ b/taglib/mpc/mpcproperties.cpp @@ -279,10 +279,10 @@ void MPC::Properties::readSV7(const ByteVector &data) } if (d->trackPeak != 0) - d->trackPeak = (int)(log10(d->trackPeak) * 20 * 256 + .5); + d->trackPeak = (int)(log10((double)d->trackPeak) * 20 * 256 + .5); if (d->albumPeak != 0) - d->albumPeak = (int)(log10(d->albumPeak) * 20 * 256 + .5); + d->albumPeak = (int)(log10((double)d->albumPeak) * 20 * 256 + .5); bool trueGapless = (gapless >> 31) & 0x0001; if(trueGapless) { From 590cd4c9f6a3b897dbc62d033bda92863dec5c2a Mon Sep 17 00:00:00 2001 From: Tsuda Kageyu Date: Thu, 23 Aug 2012 20:54:18 +0900 Subject: [PATCH 09/67] Fix warnings with VS2010 --- taglib/asf/asffile.cpp | 4 ++-- taglib/flac/flacfile.cpp | 2 +- taglib/it/itfile.cpp | 2 +- taglib/mpc/mpcproperties.cpp | 2 +- taglib/mpeg/id3v2/id3v2tag.cpp | 3 ++- taglib/mpeg/mpegproperties.cpp | 2 +- taglib/ogg/flac/oggflacfile.cpp | 4 ++-- taglib/ogg/vorbis/vorbisproperties.cpp | 2 +- taglib/riff/aiff/aiffproperties.cpp | 4 ++-- taglib/toolkit/tstring.cpp | 7 ++++--- taglib/toolkit/unicode.cpp | 6 +++--- 11 files changed, 20 insertions(+), 18 deletions(-) diff --git a/taglib/asf/asffile.cpp b/taglib/asf/asffile.cpp index e2823c7e..455631f8 100644 --- a/taglib/asf/asffile.cpp +++ b/taglib/asf/asffile.cpp @@ -348,7 +348,7 @@ void ASF::File::HeaderExtensionObject::parse(ASF::File *file, uint /*size*/) else { obj = new UnknownObject(guid); } - obj->parse(file, size); + obj->parse(file, (unsigned int)size); objects.append(obj); dataPos += size; } @@ -535,7 +535,7 @@ bool ASF::File::save() data.append(d->objects[i]->render(this)); } data = headerGuid + ByteVector::fromLongLong(data.size() + 30, false) + ByteVector::fromUInt(d->objects.size(), false) + ByteVector("\x01\x02", 2) + data; - insert(data, 0, d->size); + insert(data, 0, (TagLib::ulong)d->size); return true; } diff --git a/taglib/flac/flacfile.cpp b/taglib/flac/flacfile.cpp index 79acb539..a02770a8 100644 --- a/taglib/flac/flacfile.cpp +++ b/taglib/flac/flacfile.cpp @@ -243,7 +243,7 @@ bool FLAC::File::save() } ByteVector padding = ByteVector::fromUInt(paddingLength); padding.resize(paddingLength + 4); - padding[0] = FLAC::MetadataBlock::Padding | LastBlockFlag; + padding[0] = (char)(FLAC::MetadataBlock::Padding | LastBlockFlag); data.append(padding); // Write the data to the file diff --git a/taglib/it/itfile.cpp b/taglib/it/itfile.cpp index 51b57b0c..4e049518 100644 --- a/taglib/it/itfile.cpp +++ b/taglib/it/itfile.cpp @@ -128,7 +128,7 @@ bool IT::File::save() seek(sampleOffset + 20); - if((i + instrumentCount) < lines.size()) + if((TagLib::uint)(i + instrumentCount) < lines.size()) writeString(lines[i + instrumentCount], 25); else writeString(String::null, 25); diff --git a/taglib/mpc/mpcproperties.cpp b/taglib/mpc/mpcproperties.cpp index 7046db99..65d2541f 100644 --- a/taglib/mpc/mpcproperties.cpp +++ b/taglib/mpc/mpcproperties.cpp @@ -214,7 +214,7 @@ void MPC::Properties::readSV8(File *file) d->channels = flags[7] * 8 + flags[6] * 4 + flags[5] * 2 + flags[4] + 1; if((d->sampleFrames - begSilence) != 0) - d->bitrate = d->streamLength * 8.0 * d->sampleRate / (d->sampleFrames - begSilence); + d->bitrate = (int)(d->streamLength * 8.0 * d->sampleRate / (d->sampleFrames - begSilence)); d->bitrate = d->bitrate / 1000; d->length = (d->sampleFrames - begSilence) / d->sampleRate; diff --git a/taglib/mpeg/id3v2/id3v2tag.cpp b/taglib/mpeg/id3v2/id3v2tag.cpp index 8b623f82..ffb0189c 100644 --- a/taglib/mpeg/id3v2/id3v2tag.cpp +++ b/taglib/mpeg/id3v2/id3v2tag.cpp @@ -676,8 +676,9 @@ void ID3v2::Tag::parse(const ByteVector &origData) // portion of the frame data. if(data.at(frameDataPosition) == 0) { - if(d->header.footerPresent()) + if(d->header.footerPresent()) { debug("Padding *and* a footer found. This is not allowed by the spec."); + } d->paddingSize = frameDataLength - frameDataPosition; return; diff --git a/taglib/mpeg/mpegproperties.cpp b/taglib/mpeg/mpegproperties.cpp index 028ee061..3af95664 100644 --- a/taglib/mpeg/mpegproperties.cpp +++ b/taglib/mpeg/mpegproperties.cpp @@ -221,7 +221,7 @@ void MPEG::Properties::read() double length = timePerFrame * d->xingHeader->totalFrames(); d->length = int(length); - d->bitrate = d->length > 0 ? d->xingHeader->totalSize() * 8 / length / 1000 : 0; + d->bitrate = d->length > 0 ? (int)(d->xingHeader->totalSize() * 8 / length / 1000) : 0; } else { // Since there was no valid Xing header found, we hope that we're in a constant diff --git a/taglib/ogg/flac/oggflacfile.cpp b/taglib/ogg/flac/oggflacfile.cpp index 3addbffa..9d9c303d 100644 --- a/taglib/ogg/flac/oggflacfile.cpp +++ b/taglib/ogg/flac/oggflacfile.cpp @@ -263,9 +263,9 @@ void Ogg::FLAC::File::scan() d->hasXiphComment = true; d->commentPacket = ipacket; } - else if(blockType > 5) + else if(blockType > 5) { debug("Ogg::FLAC::File::scan() -- Unknown metadata block"); - + } } // End of metadata, now comes the datastream diff --git a/taglib/ogg/vorbis/vorbisproperties.cpp b/taglib/ogg/vorbis/vorbisproperties.cpp index 6de6a599..c67e1677 100644 --- a/taglib/ogg/vorbis/vorbisproperties.cpp +++ b/taglib/ogg/vorbis/vorbisproperties.cpp @@ -173,7 +173,7 @@ void Vorbis::Properties::read() long long end = last->absoluteGranularPosition(); if(start >= 0 && end >= 0 && d->sampleRate > 0) - d->length = (end - start) / (long long) d->sampleRate; + d->length = (int)((end - start) / (long long) d->sampleRate); 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."); diff --git a/taglib/riff/aiff/aiffproperties.cpp b/taglib/riff/aiff/aiffproperties.cpp index c78f0cba..3ea27582 100644 --- a/taglib/riff/aiff/aiffproperties.cpp +++ b/taglib/riff/aiff/aiffproperties.cpp @@ -154,7 +154,7 @@ void RIFF::AIFF::Properties::read(const ByteVector &data) d->sampleFrames = data.mid(2, 4).toUInt(); d->sampleWidth = data.mid(6, 2).toShort(); double sampleRate = ConvertFromIeeeExtended(reinterpret_cast(data.mid(8, 10).data())); - d->sampleRate = sampleRate; - d->bitrate = (sampleRate * d->sampleWidth * d->channels) / 1000.0; + d->sampleRate = (int)sampleRate; + d->bitrate = (int)((sampleRate * d->sampleWidth * d->channels) / 1000.0); d->length = d->sampleRate > 0 ? d->sampleFrames / d->sampleRate : 0; } diff --git a/taglib/toolkit/tstring.cpp b/taglib/toolkit/tstring.cpp index f99d1c37..d0ff9a71 100644 --- a/taglib/toolkit/tstring.cpp +++ b/taglib/toolkit/tstring.cpp @@ -233,8 +233,9 @@ std::string String::to8Bit(bool unicode) const &target, targetBuffer + outputBufferSize, Unicode::lenientConversion); - if(result != Unicode::conversionOK) + if(result != Unicode::conversionOK) { debug("String::to8Bit() - Unicode conversion error."); + } int newSize = target - targetBuffer; s.resize(newSize); @@ -779,9 +780,9 @@ void String::prepare(Type t) &target, targetBuffer + bufferSize, Unicode::lenientConversion); - if(result != Unicode::conversionOK) + if(result != Unicode::conversionOK) { debug("String::prepare() - Unicode conversion error."); - + } int newSize = target != targetBuffer ? target - targetBuffer - 1 : 0; d->data.resize(newSize); diff --git a/taglib/toolkit/unicode.cpp b/taglib/toolkit/unicode.cpp index b60264d9..1b26977e 100644 --- a/taglib/toolkit/unicode.cpp +++ b/taglib/toolkit/unicode.cpp @@ -155,7 +155,7 @@ ConversionResult ConvertUTF16toUTF8 ( case 4: *--target = (ch | byteMark) & byteMask; ch >>= 6; case 3: *--target = (ch | byteMark) & byteMask; ch >>= 6; case 2: *--target = (ch | byteMark) & byteMask; ch >>= 6; - case 1: *--target = ch | firstByteMark[bytesToWrite]; + case 1: *--target = (UTF8)(ch | firstByteMark[bytesToWrite]); } target += bytesToWrite; } @@ -253,7 +253,7 @@ ConversionResult ConvertUTF8toUTF16 ( result = sourceIllegal; break; } else { - *target++ = ch; /* normal case */ + *target++ = (UTF16)ch; /* normal case */ } } else if (ch > UNI_MAX_UTF16) { if (flags == strictConversion) { @@ -270,7 +270,7 @@ ConversionResult ConvertUTF8toUTF16 ( result = targetExhausted; break; } ch -= halfBase; - *target++ = (ch >> halfShift) + UNI_SUR_HIGH_START; + *target++ = (UTF16)((ch >> halfShift) + UNI_SUR_HIGH_START); *target++ = (ch & halfMask) + UNI_SUR_LOW_START; } } From d163f36d35a1ec61e6e90fca61b9f3094c4d4ec7 Mon Sep 17 00:00:00 2001 From: Tsuda Kageyu Date: Thu, 23 Aug 2012 21:57:12 +0900 Subject: [PATCH 10/67] Fix Visual C++ specific warnings about security --- taglib/toolkit/tfile.cpp | 20 ++++++++++++ taglib/toolkit/tfilestream.cpp | 58 ++++++++++++++++++++++++++++++---- taglib/toolkit/tstring.cpp | 9 ++++++ 3 files changed, 81 insertions(+), 6 deletions(-) diff --git a/taglib/toolkit/tfile.cpp b/taglib/toolkit/tfile.cpp index 09b8fdbc..30cc9fbc 100644 --- a/taglib/toolkit/tfile.cpp +++ b/taglib/toolkit/tfile.cpp @@ -452,12 +452,32 @@ long File::length() bool File::isReadable(const char *file) { + +#if defined(_MSC_VER) && (_MSC_VER >= 1400) // VC++2005 or later + + return _access_s(file, R_OK) == 0; + +#else + return access(file, R_OK) == 0; + +#endif + } bool File::isWritable(const char *file) { + +#if defined(_MSC_VER) && (_MSC_VER >= 1400) // VC++2005 or later + + return _access_s(file, W_OK) == 0; + +#else + return access(file, W_OK) == 0; + +#endif + } //////////////////////////////////////////////////////////////////////////////// diff --git a/taglib/toolkit/tfilestream.cpp b/taglib/toolkit/tfilestream.cpp index e2aebae3..46f05a8d 100644 --- a/taglib/toolkit/tfilestream.cpp +++ b/taglib/toolkit/tfilestream.cpp @@ -87,10 +87,45 @@ FileStream::FileStreamPrivate::FileStreamPrivate(FileName fileName, bool openRea { // First try with read / write mode, if that fails, fall back to read only. -#ifdef _WIN32 +#if defined(_MSC_VER) && (_MSC_VER >= 1400) // VC++2005 or later + + errno_t err; + if(wcslen((const wchar_t *) fileName) > 0) { + if(openReadOnly) + err = _wfopen_s(&file, name, L"rb"); + else { + err = _wfopen_s(&file, name, L"rb+"); + + if(err == 0) + readOnly = false; + else + err = _wfopen_s(&file, name, L"rb"); + } + + if(err == 0) + return; + } + + if(openReadOnly) + err = fopen_s(&file, name, "rb"); + else { + err = fopen_s(&file, name, "rb+"); + + if(err == 0) + readOnly = false; + else + err = fopen_s(&file, name, "rb"); + } + + if(err != 0) { + debug("Could not open file " + String((const char *) name)); + } + +#else // defined(_MSC_VER) && (_MSC_VER >= 1400) + +# ifdef _WIN32 if(wcslen((const wchar_t *) fileName) > 0) { - if(openReadOnly) file = _wfopen(name, L"rb"); else { @@ -104,10 +139,9 @@ FileStream::FileStreamPrivate::FileStreamPrivate(FileName fileName, bool openRea if(file) return; - } -#endif +# endif // defined(_WIN32) if(openReadOnly) file = fopen(name, "rb"); @@ -120,10 +154,12 @@ FileStream::FileStreamPrivate::FileStreamPrivate(FileName fileName, bool openRea file = fopen(name, "rb"); } - if(!file) - { + if(!file) { debug("Could not open file " + String((const char *) name)); } + +#endif // defined(_MSC_VER) && (_MSC_VER >= 1400) + } //////////////////////////////////////////////////////////////////////////////// @@ -379,7 +415,17 @@ long FileStream::length() void FileStream::truncate(long length) { + +#if defined(_MSC_VER) && (_MSC_VER >= 1400) // VC++2005 or later + + ftruncate(_fileno(d->file), length); + +#else + ftruncate(fileno(d->file), length); + +#endif + } TagLib::uint FileStream::bufferSize() diff --git a/taglib/toolkit/tstring.cpp b/taglib/toolkit/tstring.cpp index f99d1c37..bad39749 100644 --- a/taglib/toolkit/tstring.cpp +++ b/taglib/toolkit/tstring.cpp @@ -259,8 +259,17 @@ const char *String::toCString(bool unicode) const std::string buffer = to8Bit(unicode); d->CString = new char[buffer.size() + 1]; + +#if defined(_MSC_VER) && (_MSC_VER >= 1400) // VC++2005 or later + + strcpy_s(d->CString, buffer.size() + 1, buffer.c_str()); + +#else + strcpy(d->CString, buffer.c_str()); +#endif + return d->CString; } From df12b4ffc668344fa3cbd1edc96e3953eff120d1 Mon Sep 17 00:00:00 2001 From: Tsuda Kageyu Date: Mon, 3 Sep 2012 01:45:28 +0900 Subject: [PATCH 11/67] Refectored the FileStream constructor. --- taglib/toolkit/tfilestream.cpp | 119 ++++++++++++++------------------- 1 file changed, 51 insertions(+), 68 deletions(-) diff --git a/taglib/toolkit/tfilestream.cpp b/taglib/toolkit/tfilestream.cpp index 46f05a8d..b1fd9f80 100644 --- a/taglib/toolkit/tfilestream.cpp +++ b/taglib/toolkit/tfilestream.cpp @@ -65,6 +65,51 @@ struct FileNameHandle : public std::string #endif +namespace { + FILE *openFile(const FileName &path, bool readOnly) + { + // Calls a proper variation of fopen() depending on the compiling environment. + +#if defined(_WIN32) + +# if defined(_MSC_VER) && (_MSC_VER >= 1400) + + // Visual C++ 2005 or later. + + FILE *file; + errno_t err; + + if(wcslen(path) > 0) + err = _wfopen_s(&file, path, readOnly ? L"rb" : L"rb+"); + else + err = fopen_s(&file, path, readOnly ? "rb" : "rb+"); + + if(err == 0) + return file; + else + return NULL; + +# else // defined(_MSC_VER) && (_MSC_VER >= 1400) + + // Visual C++.NET 2003 or earlier. + + if(wcslen(path) > 0) + return _wfopen(path, readOnly ? L"rb" : L"rb+"); + else + return fopen(path, readOnly ? "rb" : "rb+"); + +# endif // defined(_MSC_VER) && (_MSC_VER >= 1400) + +#else // defined(_WIN32) + + // Non-Win32 + + return fopen(path, readOnly ? "rb" : "rb+"); + +#endif // defined(_WIN32) + } +} + class FileStream::FileStreamPrivate { public: @@ -87,79 +132,17 @@ FileStream::FileStreamPrivate::FileStreamPrivate(FileName fileName, bool openRea { // First try with read / write mode, if that fails, fall back to read only. -#if defined(_MSC_VER) && (_MSC_VER >= 1400) // VC++2005 or later + if(!openReadOnly) + file = openFile(name, false); - errno_t err; - if(wcslen((const wchar_t *) fileName) > 0) { - if(openReadOnly) - err = _wfopen_s(&file, name, L"rb"); - else { - err = _wfopen_s(&file, name, L"rb+"); - - if(err == 0) - readOnly = false; - else - err = _wfopen_s(&file, name, L"rb"); - } - - if(err == 0) - return; - } - - if(openReadOnly) - err = fopen_s(&file, name, "rb"); - else { - err = fopen_s(&file, name, "rb+"); - - if(err == 0) - readOnly = false; - else - err = fopen_s(&file, name, "rb"); - } - - if(err != 0) { - debug("Could not open file " + String((const char *) name)); - } - -#else // defined(_MSC_VER) && (_MSC_VER >= 1400) - -# ifdef _WIN32 - - if(wcslen((const wchar_t *) fileName) > 0) { - if(openReadOnly) - file = _wfopen(name, L"rb"); - else { - file = _wfopen(name, L"rb+"); - - if(file) - readOnly = false; - else - file = _wfopen(name, L"rb"); - } - - if(file) - return; - } - -# endif // defined(_WIN32) - - if(openReadOnly) - file = fopen(name, "rb"); - else { - file = fopen(name, "rb+"); - - if(file) - readOnly = false; - else - file = fopen(name, "rb"); - } + if(file) + readOnly = false; + else + file = openFile(name, true); if(!file) { debug("Could not open file " + String((const char *) name)); } - -#endif // defined(_MSC_VER) && (_MSC_VER >= 1400) - } //////////////////////////////////////////////////////////////////////////////// From 33d0be110b8efca6231279139693c54bdd3465ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Lalinsk=C3=BD?= Date: Mon, 3 Sep 2012 19:55:55 +0200 Subject: [PATCH 12/67] Fix ambiguous reference to uint in tests --- tests/test_apetag.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_apetag.cpp b/tests/test_apetag.cpp index 6cda5e52..0c63f0b6 100644 --- a/tests/test_apetag.cpp +++ b/tests/test_apetag.cpp @@ -77,7 +77,7 @@ public: APE::Item item3 = APE::Item("TRACKNUMBER", "29"); tag.setItem("TRACKNUMBER", item3); properties = tag.properties(); - CPPUNIT_ASSERT_EQUAL(uint(2), properties["TRACKNUMBER"].size()); + CPPUNIT_ASSERT_EQUAL(TagLib::uint(2), properties["TRACKNUMBER"].size()); CPPUNIT_ASSERT_EQUAL(String("17"), properties["TRACKNUMBER"][0]); CPPUNIT_ASSERT_EQUAL(String("29"), properties["TRACKNUMBER"][1]); @@ -93,7 +93,7 @@ public: APE::Tag tag; PropertyMap unsuccessful = tag.setProperties(properties); - CPPUNIT_ASSERT_EQUAL(uint(2), unsuccessful.size()); + CPPUNIT_ASSERT_EQUAL(TagLib::uint(2), unsuccessful.size()); CPPUNIT_ASSERT(unsuccessful.contains("A")); CPPUNIT_ASSERT(unsuccessful.contains("MP+")); } From 74b94613a06b5230bd0cad64d9e10bcaf5a1b6a9 Mon Sep 17 00:00:00 2001 From: Tsuda Kageyu Date: Wed, 5 Sep 2012 19:46:52 +0900 Subject: [PATCH 13/67] Bug fix in String::substr() --- taglib/toolkit/tstring.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/taglib/toolkit/tstring.cpp b/taglib/toolkit/tstring.cpp index 49d0eb9c..292f3533 100644 --- a/taglib/toolkit/tstring.cpp +++ b/taglib/toolkit/tstring.cpp @@ -345,9 +345,6 @@ bool String::startsWith(const String &s) const String String::substr(uint position, uint n) const { - if(n > position + d->data.size()) - n = d->data.size() - position; - String s; s.d->data = d->data.substr(position, n); return s; From 719187794e830d8d24acab4622212dc27f5c0afb Mon Sep 17 00:00:00 2001 From: Rupert Daniel Date: Wed, 5 Sep 2012 16:37:46 +0100 Subject: [PATCH 14/67] Implementation of the ID3v2.4 OWNE frame. --- taglib/mpeg/id3v2/frames/ownershipframe.cpp | 159 ++++++++++++++++++++ taglib/mpeg/id3v2/frames/ownershipframe.h | 152 +++++++++++++++++++ taglib/mpeg/id3v2/id3v2framefactory.cpp | 9 ++ taglib/mpeg/id3v2/id3v2tag.cpp | 4 + taglib/mpeg/id3v2/id3v2tag.h | 1 + tests/test_id3v2.cpp | 85 +++++------ 6 files changed, 360 insertions(+), 50 deletions(-) create mode 100644 taglib/mpeg/id3v2/frames/ownershipframe.cpp create mode 100644 taglib/mpeg/id3v2/frames/ownershipframe.h diff --git a/taglib/mpeg/id3v2/frames/ownershipframe.cpp b/taglib/mpeg/id3v2/frames/ownershipframe.cpp new file mode 100644 index 00000000..da8410b5 --- /dev/null +++ b/taglib/mpeg/id3v2/frames/ownershipframe.cpp @@ -0,0 +1,159 @@ +/*************************************************************************** + copyright : (C) 2012 by Rupert Daniel + email : rupert@cancelmonday.com + ***************************************************************************/ + +/*************************************************************************** + * This library is free software; you can redistribute it and/or modify * + * it under the terms of the GNU Lesser General Public License version * + * 2.1 as published by the Free Software Foundation. * + * * + * This library is distributed in the hope that it will be useful, but * + * WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this library; if not, write to the Free Software * + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * + * 02110-1301 USA * + * * + * Alternatively, this file is available under the Mozilla Public * + * License Version 1.1. You may obtain a copy of the License at * + * http://www.mozilla.org/MPL/ * + ***************************************************************************/ + +#include + +#include "ownershipframe.h" + +using namespace TagLib; +using namespace ID3v2; + +class OwnershipFrame::OwnershipFramePrivate +{ +public: + String currencyCode; + String pricePaid; + String datePurchased; + String seller; + String::Type textEncoding; +}; + +//////////////////////////////////////////////////////////////////////////////// +// public members +//////////////////////////////////////////////////////////////////////////////// + +OwnershipFrame::OwnershipFrame(String::Type encoding) : Frame("OWNE") +{ + d = new OwnershipFramePrivate; + d->textEncoding = encoding; +} + +OwnershipFrame::OwnershipFrame(const ByteVector &data) : Frame(data) +{ + d = new OwnershipFramePrivate; + setData(data); +} + +OwnershipFrame::~OwnershipFrame() +{ + delete d; +} + +String OwnershipFrame::toString() const +{ + return "pricePaid=" + d->pricePaid + " datePurchased=" + d->datePurchased + " seller=" + d->seller; +} + +String OwnershipFrame::pricePaid() const +{ + return d->pricePaid; +} + +void OwnershipFrame::setPricePaid(const String &s) +{ + d->pricePaid = s; +} + +String OwnershipFrame::datePurchased() const +{ + return d->datePurchased; +} + +void OwnershipFrame::setDatePurchased(const String &s) +{ + d->datePurchased = s; +} + +String OwnershipFrame::seller() const +{ + return d->seller; +} + +void OwnershipFrame::setSeller(const String &s) +{ + d->seller = s; +} + +String::Type OwnershipFrame::textEncoding() const +{ + return d->textEncoding; +} + +void OwnershipFrame::setTextEncoding(String::Type encoding) +{ + d->textEncoding = encoding; +} + +//////////////////////////////////////////////////////////////////////////////// +// protected members +//////////////////////////////////////////////////////////////////////////////// + +void OwnershipFrame::parseFields(const ByteVector &data) +{ + int pos = 0; + + // Get the text encoding + d->textEncoding = String::Type(data[0]); + pos += 1; + + // Read the price paid this is a null terminate string + d->pricePaid = readStringField(data, String::Latin1, &pos); + + // If we don't have at least 8 bytes left then don't parse the rest of the + // data + if (data.size() - pos < 8) { + return; + } + + // Read the date purchased YYYYMMDD + d->datePurchased = String(data.mid(pos, 8)); + pos += 8; + + // Read the seller + d->seller = String(data.mid(pos)); +} + +ByteVector OwnershipFrame::renderFields() const +{ + ByteVector v; + + v.append(char(d->textEncoding)); + v.append(d->pricePaid.data(String::Latin1)); + v.append(textDelimiter(String::Latin1)); + v.append(d->datePurchased.data(String::Latin1)); + v.append(d->seller.data(d->textEncoding)); + + return v; +} + +//////////////////////////////////////////////////////////////////////////////// +// private members +//////////////////////////////////////////////////////////////////////////////// + +OwnershipFrame::OwnershipFrame(const ByteVector &data, Header *h) : Frame(h) +{ + d = new OwnershipFramePrivate; + parseFields(fieldData(data)); +} diff --git a/taglib/mpeg/id3v2/frames/ownershipframe.h b/taglib/mpeg/id3v2/frames/ownershipframe.h new file mode 100644 index 00000000..b73832ba --- /dev/null +++ b/taglib/mpeg/id3v2/frames/ownershipframe.h @@ -0,0 +1,152 @@ +/*************************************************************************** + copyright : (C) 2012 by Rupert Daniel + email : rupert@cancelmonday.com + ***************************************************************************/ + +/*************************************************************************** + * This library is free software; you can redistribute it and/or modify * + * it under the terms of the GNU Lesser General Public License version * + * 2.1 as published by the Free Software Foundation. * + * * + * This library is distributed in the hope that it will be useful, but * + * WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this library; if not, write to the Free Software * + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * + * 02110-1301 USA * + * * + * Alternatively, this file is available under the Mozilla Public * + * License Version 1.1. You may obtain a copy of the License at * + * http://www.mozilla.org/MPL/ * + ***************************************************************************/ + +#ifndef TAGLIB_OWNERSHIPFRAME_H +#define TAGLIB_OWNERSHIPFRAME_H + +#include "id3v2frame.h" +#include "taglib_export.h" + +namespace TagLib { + + namespace ID3v2 { + + //! An implementation of ID3v2 "ownership" + + /*! + * This implements the ID3v2 ownership (OWNE frame). It concists of + * an price paid, a date purchased (YYYYMMDD) and the name of the seller. + */ + + class TAGLIB_EXPORT OwnershipFrame : public Frame + { + friend class FrameFactory; + + public: + /*! + * Construct an empty ownership frame. + */ + explicit OwnershipFrame(String::Type encoding = String::Latin1); + + /*! + * Construct a ownership based on the data in \a data. + */ + explicit OwnershipFrame(const ByteVector &data); + + /*! + * Destroys this OwnershipFrame instance. + */ + virtual ~OwnershipFrame(); + + /*! + * Returns the text of this popularimeter. + * + * \see text() + */ + virtual String toString() const; + + /*! + * Returns the date purchased. + * + * \see setDatePurchased() + */ + String datePurchased() const; + + /*! + * Set the date purchased. + * + * \see datePurchased() + */ + void setDatePurchased(const String &datePurchased); + + + /*! + * Returns the price paid. + * + * \see setPricePaid() + */ + String pricePaid() const; + + /*! + * Set the price paid. + * + * \see pricePaid() + */ + void setPricePaid(const String &pricePaid); + + /*! + * Returns the seller. + * + * \see setSeller() + */ + String seller() const; + + /*! + * Set the seller. + * + * \see seller() + */ + void setSeller(const String &seller); + + /*! + * Returns the text encoding that will be used in rendering this frame. + * This defaults to the type that was either specified in the constructor + * or read from the frame when parsed. + * + * \see setTextEncoding() + * \see render() + */ + String::Type textEncoding() const; + + /*! + * Sets the text encoding to be used when rendering this frame to + * \a encoding. + * + * \see textEncoding() + * \see render() + */ + void setTextEncoding(String::Type encoding); + + protected: + // Reimplementations. + + virtual void parseFields(const ByteVector &data); + virtual ByteVector renderFields() const; + + private: + /*! + * The constructor used by the FrameFactory. + */ + OwnershipFrame(const ByteVector &data, Header *h); + OwnershipFrame(const OwnershipFrame &); + OwnershipFrame &operator=(const OwnershipFrame &); + + class OwnershipFramePrivate; + OwnershipFramePrivate *d; + }; + + } +} +#endif diff --git a/taglib/mpeg/id3v2/id3v2framefactory.cpp b/taglib/mpeg/id3v2/id3v2framefactory.cpp index bcf69e1a..7e2e98a8 100644 --- a/taglib/mpeg/id3v2/id3v2framefactory.cpp +++ b/taglib/mpeg/id3v2/id3v2framefactory.cpp @@ -44,6 +44,7 @@ #include "frames/unsynchronizedlyricsframe.h" #include "frames/popularimeterframe.h" #include "frames/privateframe.h" +#include "frames/ownershipframe.h" using namespace TagLib; using namespace ID3v2; @@ -238,6 +239,14 @@ Frame *FrameFactory::createFrame(const ByteVector &origData, Header *tagHeader) if(frameID == "PRIV") return new PrivateFrame(data, header); + + // Ownership (frames 4.22) + + if(frameID == "OWNE") { + OwnershipFrame *f = new OwnershipFrame(data, header); + d->setTextEncoding(f); + return f; + } return new UnknownFrame(data, header); } diff --git a/taglib/mpeg/id3v2/id3v2tag.cpp b/taglib/mpeg/id3v2/id3v2tag.cpp index ffb0189c..54e63920 100644 --- a/taglib/mpeg/id3v2/id3v2tag.cpp +++ b/taglib/mpeg/id3v2/id3v2tag.cpp @@ -81,6 +81,10 @@ const ID3v2::Latin1StringHandler *ID3v2::Tag::TagPrivate::stringHandler = &defau // StringHandler implementation //////////////////////////////////////////////////////////////////////////////// +Latin1StringHandler::Latin1StringHandler() +{ +} + Latin1StringHandler::~Latin1StringHandler() { } diff --git a/taglib/mpeg/id3v2/id3v2tag.h b/taglib/mpeg/id3v2/id3v2tag.h index e6d6d4fd..5fd5c1f1 100644 --- a/taglib/mpeg/id3v2/id3v2tag.h +++ b/taglib/mpeg/id3v2/id3v2tag.h @@ -77,6 +77,7 @@ namespace TagLib { class TAGLIB_EXPORT Latin1StringHandler { public: + Latin1StringHandler(); virtual ~Latin1StringHandler(); /*! diff --git a/tests/test_id3v2.cpp b/tests/test_id3v2.cpp index 29758aa4..243f9753 100644 --- a/tests/test_id3v2.cpp +++ b/tests/test_id3v2.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include "utils.h" @@ -61,6 +62,8 @@ class TestID3v2 : public CppUnit::TestFixture CPPUNIT_TEST(testRenderUrlLinkFrame); CPPUNIT_TEST(testParseUserUrlLinkFrame); CPPUNIT_TEST(testRenderUserUrlLinkFrame); + CPPUNIT_TEST(testParseOwnershipFrame); + CPPUNIT_TEST(testRenderOwnershipFrame); CPPUNIT_TEST(testSaveUTF16Comment); CPPUNIT_TEST(testUpdateGenre23_1); CPPUNIT_TEST(testUpdateGenre23_2); @@ -69,11 +72,8 @@ class TestID3v2 : public CppUnit::TestFixture CPPUNIT_TEST(testDowngradeTo23); // CPPUNIT_TEST(testUpdateFullDate22); TODO TYE+TDA should be upgraded to TDRC together CPPUNIT_TEST(testCompressedFrameWithBrokenLength); - CPPUNIT_TEST(testW000); CPPUNIT_TEST(testPropertyInterface); CPPUNIT_TEST(testPropertyInterface2); - CPPUNIT_TEST(testDeleteFrame); - CPPUNIT_TEST(testSaveAndStripID3v1ShouldNotAddFrameFromID3v1ToId3v2); CPPUNIT_TEST_SUITE_END(); public: @@ -386,6 +386,38 @@ public: "http://example.com", 33), // URL f.render()); } + + void testParseOwnershipFrame() + { + ID3v2::OwnershipFrame f( + ByteVector("OWNE" // Frame ID + "\x00\x00\x00\x19" // Frame size + "\x00\x00" // Frame flags + "\x00" // Text encoding + "GBP1.99\x00" // Price paid + "20120905" // Date of purchase + "Beatport", 35)); // Seller + CPPUNIT_ASSERT_EQUAL(String("GBP1.99"), f.pricePaid()); + CPPUNIT_ASSERT_EQUAL(String("20120905"), f.datePurchased()); + CPPUNIT_ASSERT_EQUAL(String("Beatport"), f.seller()); + } + + void testRenderOwnershipFrame() + { + ID3v2::OwnershipFrame f; + f.setPricePaid("GBP1.99"); + f.setDatePurchased("20120905"); + f.setSeller("Beatport"); + CPPUNIT_ASSERT_EQUAL( + ByteVector("OWNE" // Frame ID + "\x00\x00\x00\x19" // Frame size + "\x00\x00" // Frame flags + "\x00" // Text encoding + "GBP1.99\x00" // Price paid + "20120905" // Date of purchase + "Beatport", 35), // URL + f.render()); + } void testItunes24FrameSize() { @@ -554,16 +586,6 @@ public: CPPUNIT_ASSERT_EQUAL(TagLib::uint(86414), frame->picture().size()); } - void testW000() - { - MPEG::File f(TEST_FILE_PATH_C("w000.mp3"), false); - CPPUNIT_ASSERT(f.ID3v2Tag()->frameListMap().contains("W000")); - ID3v2::UrlLinkFrame *frame = - dynamic_cast(f.ID3v2Tag()->frameListMap()["W000"].front()); - CPPUNIT_ASSERT(frame); - CPPUNIT_ASSERT_EQUAL(String("lukas.lalinsky@example.com____"), frame->url()); - } - void testPropertyInterface() { ScopedFileCopy copy("rare_frames", ".mp3"); @@ -641,43 +663,6 @@ public: CPPUNIT_ASSERT(tag.frameList("TIPL").isEmpty()); } - void testDeleteFrame() - { - ScopedFileCopy copy("rare_frames", ".mp3"); - string newname = copy.fileName(); - MPEG::File f(newname.c_str()); - ID3v2::Tag *t = f.ID3v2Tag(); - ID3v2::Frame *frame = t->frameList("TCON")[0]; - CPPUNIT_ASSERT_EQUAL(1u, t->frameList("TCON").size()); - t->removeFrame(frame, true); - f.save(MPEG::File::ID3v2); - - MPEG::File f2(newname.c_str()); - t = f2.ID3v2Tag(); - CPPUNIT_ASSERT(t->frameList("TCON").isEmpty()); - } - void testSaveAndStripID3v1ShouldNotAddFrameFromID3v1ToId3v2() - { - ScopedFileCopy copy("xing", ".mp3"); - string newname = copy.fileName(); - - { - MPEG::File foo(newname.c_str()); - foo.tag()->setArtist("Artist"); - foo.save(MPEG::File::ID3v1 | MPEG::File::ID3v2); - } - - { - MPEG::File bar(newname.c_str()); - bar.ID3v2Tag()->removeFrames("TPE1"); - // Should strip ID3v1 here and not add old values to ID3v2 again - bar.save(MPEG::File::ID3v2, true); - } - - MPEG::File f(newname.c_str()); - CPPUNIT_ASSERT(!f.ID3v2Tag()->frameListMap().contains("TPE1")); - } - }; CPPUNIT_TEST_SUITE_REGISTRATION(TestID3v2); From f194a55c0ffb8aa6d2240f1fe52f34bea165552f Mon Sep 17 00:00:00 2001 From: Rupert Daniel Date: Thu, 6 Sep 2012 12:11:20 +0100 Subject: [PATCH 15/67] Updated OWNE implementaion with minor changes after pull review --- taglib/mpeg/id3v2/frames/ownershipframe.cpp | 9 ++-- taglib/mpeg/id3v2/frames/ownershipframe.h | 5 +- tests/test_id3v2.cpp | 51 +++++++++++++++++++++ 3 files changed, 59 insertions(+), 6 deletions(-) diff --git a/taglib/mpeg/id3v2/frames/ownershipframe.cpp b/taglib/mpeg/id3v2/frames/ownershipframe.cpp index da8410b5..9451c4c4 100644 --- a/taglib/mpeg/id3v2/frames/ownershipframe.cpp +++ b/taglib/mpeg/id3v2/frames/ownershipframe.cpp @@ -26,6 +26,7 @@ #include #include "ownershipframe.h" +#include using namespace TagLib; using namespace ID3v2; @@ -33,7 +34,6 @@ using namespace ID3v2; class OwnershipFrame::OwnershipFramePrivate { public: - String currencyCode; String pricePaid; String datePurchased; String seller; @@ -123,7 +123,7 @@ void OwnershipFrame::parseFields(const ByteVector &data) // If we don't have at least 8 bytes left then don't parse the rest of the // data - if (data.size() - pos < 8) { + if(data.size() - pos < 8) { return; } @@ -132,7 +132,10 @@ void OwnershipFrame::parseFields(const ByteVector &data) pos += 8; // Read the seller - d->seller = String(data.mid(pos)); + if(d->textEncoding == String::Latin1) + d->seller = Tag::latin1StringHandler()->parse(data.mid(pos)); + else + d->seller = String(data.mid(pos), d->textEncoding); } ByteVector OwnershipFrame::renderFields() const diff --git a/taglib/mpeg/id3v2/frames/ownershipframe.h b/taglib/mpeg/id3v2/frames/ownershipframe.h index b73832ba..34fc9129 100644 --- a/taglib/mpeg/id3v2/frames/ownershipframe.h +++ b/taglib/mpeg/id3v2/frames/ownershipframe.h @@ -36,8 +36,8 @@ namespace TagLib { //! An implementation of ID3v2 "ownership" /*! - * This implements the ID3v2 ownership (OWNE frame). It concists of - * an price paid, a date purchased (YYYYMMDD) and the name of the seller. + * This implements the ID3v2 ownership (OWNE frame). It consists of + * a price paid, a date purchased (YYYYMMDD) and the name of the seller. */ class TAGLIB_EXPORT OwnershipFrame : public Frame @@ -81,7 +81,6 @@ namespace TagLib { */ void setDatePurchased(const String &datePurchased); - /*! * Returns the price paid. * diff --git a/tests/test_id3v2.cpp b/tests/test_id3v2.cpp index 243f9753..d3fbfee9 100644 --- a/tests/test_id3v2.cpp +++ b/tests/test_id3v2.cpp @@ -72,8 +72,11 @@ class TestID3v2 : public CppUnit::TestFixture CPPUNIT_TEST(testDowngradeTo23); // CPPUNIT_TEST(testUpdateFullDate22); TODO TYE+TDA should be upgraded to TDRC together CPPUNIT_TEST(testCompressedFrameWithBrokenLength); + CPPUNIT_TEST(testW000); CPPUNIT_TEST(testPropertyInterface); CPPUNIT_TEST(testPropertyInterface2); + CPPUNIT_TEST(testDeleteFrame); + CPPUNIT_TEST(testSaveAndStripID3v1ShouldNotAddFrameFromID3v1ToId3v2); CPPUNIT_TEST_SUITE_END(); public: @@ -585,6 +588,16 @@ public: CPPUNIT_ASSERT_EQUAL(String(""), frame->description()); CPPUNIT_ASSERT_EQUAL(TagLib::uint(86414), frame->picture().size()); } + + void testW000() + { + MPEG::File f(TEST_FILE_PATH_C("w000.mp3"), false); + CPPUNIT_ASSERT(f.ID3v2Tag()->frameListMap().contains("W000")); + ID3v2::UrlLinkFrame *frame = + dynamic_cast(f.ID3v2Tag()->frameListMap()["W000"].front()); + CPPUNIT_ASSERT(frame); + CPPUNIT_ASSERT_EQUAL(String("lukas.lalinsky@example.com____"), frame->url()); + } void testPropertyInterface() { @@ -663,6 +676,44 @@ public: CPPUNIT_ASSERT(tag.frameList("TIPL").isEmpty()); } + void testDeleteFrame() + { + ScopedFileCopy copy("rare_frames", ".mp3"); + string newname = copy.fileName(); + MPEG::File f(newname.c_str()); + ID3v2::Tag *t = f.ID3v2Tag(); + ID3v2::Frame *frame = t->frameList("TCON")[0]; + CPPUNIT_ASSERT_EQUAL(1u, t->frameList("TCON").size()); + t->removeFrame(frame, true); + f.save(MPEG::File::ID3v2); + + MPEG::File f2(newname.c_str()); + t = f2.ID3v2Tag(); + CPPUNIT_ASSERT(t->frameList("TCON").isEmpty()); + } + + void testSaveAndStripID3v1ShouldNotAddFrameFromID3v1ToId3v2() + { + ScopedFileCopy copy("xing", ".mp3"); + string newname = copy.fileName(); + + { + MPEG::File foo(newname.c_str()); + foo.tag()->setArtist("Artist"); + foo.save(MPEG::File::ID3v1 | MPEG::File::ID3v2); + } + + { + MPEG::File bar(newname.c_str()); + bar.ID3v2Tag()->removeFrames("TPE1"); + // Should strip ID3v1 here and not add old values to ID3v2 again + bar.save(MPEG::File::ID3v2, true); + } + + MPEG::File f(newname.c_str()); + CPPUNIT_ASSERT(!f.ID3v2Tag()->frameListMap().contains("TPE1")); + } + }; CPPUNIT_TEST_SUITE_REGISTRATION(TestID3v2); From e37f6ed7525b62befd71bb1b692ff712198a984d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Lalinsk=C3=BD?= Date: Thu, 6 Sep 2012 19:30:45 +0200 Subject: [PATCH 16/67] Update CMakeLists.txt --- taglib/CMakeLists.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/taglib/CMakeLists.txt b/taglib/CMakeLists.txt index ca62f3a4..61bcb49e 100644 --- a/taglib/CMakeLists.txt +++ b/taglib/CMakeLists.txt @@ -65,6 +65,7 @@ set(tag_HDRS mpeg/id3v2/frames/attachedpictureframe.h mpeg/id3v2/frames/commentsframe.h mpeg/id3v2/frames/generalencapsulatedobjectframe.h + mpeg/id3v2/frames/ownershipframe.h mpeg/id3v2/frames/popularimeterframe.h mpeg/id3v2/frames/privateframe.h mpeg/id3v2/frames/relativevolumeframe.h @@ -151,6 +152,7 @@ set(frames_SRCS mpeg/id3v2/frames/attachedpictureframe.cpp mpeg/id3v2/frames/commentsframe.cpp mpeg/id3v2/frames/generalencapsulatedobjectframe.cpp + mpeg/id3v2/frames/ownershipframe.cpp mpeg/id3v2/frames/popularimeterframe.cpp mpeg/id3v2/frames/privateframe.cpp mpeg/id3v2/frames/relativevolumeframe.cpp From f11b206fe8643048c935e4cc86c1ec930e519809 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Lalinsk=C3=BD?= Date: Thu, 6 Sep 2012 19:43:52 +0200 Subject: [PATCH 17/67] Do not delete the IOStream object in TagLib::File --- taglib/ape/apefile.h | 3 +++ taglib/asf/asffile.h | 6 ++++++ taglib/flac/flacfile.h | 3 +++ taglib/it/itfile.h | 3 +++ taglib/mod/modfile.h | 3 +++ taglib/mp4/mp4file.h | 3 +++ taglib/mpc/mpcfile.h | 3 +++ taglib/mpeg/mpegfile.h | 3 +++ taglib/ogg/flac/oggflacfile.h | 3 +++ taglib/ogg/oggfile.h | 3 +++ taglib/ogg/speex/speexfile.h | 3 +++ taglib/ogg/vorbis/vorbisfile.h | 3 +++ taglib/riff/aiff/aifffile.h | 3 +++ taglib/riff/wav/wavfile.h | 3 +++ taglib/s3m/s3mfile.h | 3 +++ taglib/toolkit/tfile.cpp | 12 +++++++----- taglib/toolkit/tfile.h | 3 +++ taglib/trueaudio/trueaudiofile.h | 6 ++++++ taglib/wavpack/wavpackfile.h | 3 +++ taglib/xm/xmfile.h | 3 +++ 20 files changed, 70 insertions(+), 5 deletions(-) diff --git a/taglib/ape/apefile.h b/taglib/ape/apefile.h index 0bdbd422..8b187f6a 100644 --- a/taglib/ape/apefile.h +++ b/taglib/ape/apefile.h @@ -95,6 +95,9 @@ namespace TagLib { * Contructs an WavPack file from \a file. If \a readProperties is true the * file's audio properties will also be read using \a propertiesStyle. If * false, \a propertiesStyle is ignored. + * + * \note TagLib will *not* take ownership of the stream, the caller is + * responsible for deleting it after the File object. */ File(IOStream *stream, bool readProperties = true, Properties::ReadStyle propertiesStyle = Properties::Average); diff --git a/taglib/asf/asffile.h b/taglib/asf/asffile.h index f0acd728..3a7eef56 100644 --- a/taglib/asf/asffile.h +++ b/taglib/asf/asffile.h @@ -54,6 +54,9 @@ namespace TagLib { * * \note In the current implementation, both \a readProperties and * \a propertiesStyle are ignored. + * + * \note TagLib will *not* take ownership of the stream, the caller is + * responsible for deleting it after the File object. */ File(FileName file, bool readProperties = true, Properties::ReadStyle propertiesStyle = Properties::Average); @@ -64,6 +67,9 @@ namespace TagLib { * * \note In the current implementation, both \a readProperties and * \a propertiesStyle are ignored. + * + * \note TagLib will *not* take ownership of the stream, the caller is + * responsible for deleting it after the File object. */ File(IOStream *stream, bool readProperties = true, Properties::ReadStyle propertiesStyle = Properties::Average); diff --git a/taglib/flac/flacfile.h b/taglib/flac/flacfile.h index 31dfebd7..716d4478 100644 --- a/taglib/flac/flacfile.h +++ b/taglib/flac/flacfile.h @@ -97,6 +97,9 @@ namespace TagLib { * * If this file contains and ID3v2 tag the frames will be created using * \a frameFactory. + * + * \note TagLib will *not* take ownership of the stream, the caller is + * responsible for deleting it after the File object. */ // BIC: merge with the above constructor File(IOStream *stream, ID3v2::FrameFactory *frameFactory, diff --git a/taglib/it/itfile.h b/taglib/it/itfile.h index 9c507742..5584b7cd 100644 --- a/taglib/it/itfile.h +++ b/taglib/it/itfile.h @@ -48,6 +48,9 @@ namespace TagLib { * Contructs a Impulse Tracker file from \a stream. If \a readProperties * is true the file's audio properties will also be read using * \a propertiesStyle. If false, \a propertiesStyle is ignored. + * + * \note TagLib will *not* take ownership of the stream, the caller is + * responsible for deleting it after the File object. */ File(IOStream *stram, bool readProperties = true, AudioProperties::ReadStyle propertiesStyle = diff --git a/taglib/mod/modfile.h b/taglib/mod/modfile.h index 9e79659c..ad1e43b8 100644 --- a/taglib/mod/modfile.h +++ b/taglib/mod/modfile.h @@ -49,6 +49,9 @@ namespace TagLib { * Contructs a Protracker file from \a stream. If \a readProperties * is true the file's audio properties will also be read using * \a propertiesStyle. If false, \a propertiesStyle is ignored. + * + * \note TagLib will *not* take ownership of the stream, the caller is + * responsible for deleting it after the File object. */ File(IOStream *stream, bool readProperties = true, AudioProperties::ReadStyle propertiesStyle = diff --git a/taglib/mp4/mp4file.h b/taglib/mp4/mp4file.h index c3613f76..2ed3bea5 100644 --- a/taglib/mp4/mp4file.h +++ b/taglib/mp4/mp4file.h @@ -65,6 +65,9 @@ namespace TagLib { * * \note In the current implementation, both \a readProperties and * \a propertiesStyle are ignored. + * + * \note TagLib will *not* take ownership of the stream, the caller is + * responsible for deleting it after the File object. */ File(IOStream *stream, bool readProperties = true, Properties::ReadStyle audioPropertiesStyle = Properties::Average); diff --git a/taglib/mpc/mpcfile.h b/taglib/mpc/mpcfile.h index 61ac6d67..167b768e 100644 --- a/taglib/mpc/mpcfile.h +++ b/taglib/mpc/mpcfile.h @@ -95,6 +95,9 @@ namespace TagLib { * Contructs an MPC file from \a file. If \a readProperties is true the * file's audio properties will also be read using \a propertiesStyle. If * false, \a propertiesStyle is ignored. + * + * \note TagLib will *not* take ownership of the stream, the caller is + * responsible for deleting it after the File object. */ File(IOStream *stream, bool readProperties = true, Properties::ReadStyle propertiesStyle = Properties::Average); diff --git a/taglib/mpeg/mpegfile.h b/taglib/mpeg/mpegfile.h index 185fced6..9967a991 100644 --- a/taglib/mpeg/mpegfile.h +++ b/taglib/mpeg/mpegfile.h @@ -99,6 +99,9 @@ namespace TagLib { * file's audio properties will also be read using \a propertiesStyle. If * false, \a propertiesStyle is ignored. The frames will be created using * \a frameFactory. + * + * \note TagLib will *not* take ownership of the stream, the caller is + * responsible for deleting it after the File object. */ // BIC: merge with the above constructor File(IOStream *stream, ID3v2::FrameFactory *frameFactory, diff --git a/taglib/ogg/flac/oggflacfile.h b/taglib/ogg/flac/oggflacfile.h index d4373795..8558cfdf 100644 --- a/taglib/ogg/flac/oggflacfile.h +++ b/taglib/ogg/flac/oggflacfile.h @@ -75,6 +75,9 @@ namespace TagLib { * Contructs an Ogg/FLAC file from \a file. If \a readProperties is true * the file's audio properties will also be read using \a propertiesStyle. * If false, \a propertiesStyle is ignored. + * + * \note TagLib will *not* take ownership of the stream, the caller is + * responsible for deleting it after the File object. */ File(IOStream *stream, bool readProperties = true, Properties::ReadStyle propertiesStyle = Properties::Average); diff --git a/taglib/ogg/oggfile.h b/taglib/ogg/oggfile.h index 1ecf2b9b..b36daecb 100644 --- a/taglib/ogg/oggfile.h +++ b/taglib/ogg/oggfile.h @@ -100,6 +100,9 @@ namespace TagLib { * \note This constructor is protected since Ogg::File shouldn't be * instantiated directly but rather should be used through the codec * specific subclasses. + * + * \note TagLib will *not* take ownership of the stream, the caller is + * responsible for deleting it after the File object. */ File(IOStream *stream); diff --git a/taglib/ogg/speex/speexfile.h b/taglib/ogg/speex/speexfile.h index c14cf2aa..dfe51ec4 100644 --- a/taglib/ogg/speex/speexfile.h +++ b/taglib/ogg/speex/speexfile.h @@ -67,6 +67,9 @@ namespace TagLib { * Contructs a Speex file from \a file. If \a readProperties is true the * file's audio properties will also be read using \a propertiesStyle. If * false, \a propertiesStyle is ignored. + * + * \note TagLib will *not* take ownership of the stream, the caller is + * responsible for deleting it after the File object. */ File(IOStream *stream, bool readProperties = true, Properties::ReadStyle propertiesStyle = Properties::Average); diff --git a/taglib/ogg/vorbis/vorbisfile.h b/taglib/ogg/vorbis/vorbisfile.h index 15c29d99..6e4d4fc4 100644 --- a/taglib/ogg/vorbis/vorbisfile.h +++ b/taglib/ogg/vorbis/vorbisfile.h @@ -74,6 +74,9 @@ namespace TagLib { * Contructs a Vorbis file from \a file. If \a readProperties is true the * file's audio properties will also be read using \a propertiesStyle. If * false, \a propertiesStyle is ignored. + * + * \note TagLib will *not* take ownership of the stream, the caller is + * responsible for deleting it after the File object. */ File(IOStream *stream, bool readProperties = true, Properties::ReadStyle propertiesStyle = Properties::Average); diff --git a/taglib/riff/aiff/aifffile.h b/taglib/riff/aiff/aifffile.h index a50c8ecb..e1284db0 100644 --- a/taglib/riff/aiff/aifffile.h +++ b/taglib/riff/aiff/aifffile.h @@ -69,6 +69,9 @@ namespace TagLib { * Contructs an AIFF file from \a file. If \a readProperties is true the * file's audio properties will also be read using \a propertiesStyle. If * false, \a propertiesStyle is ignored. + * + * \note TagLib will *not* take ownership of the stream, the caller is + * responsible for deleting it after the File object. */ File(IOStream *stream, bool readProperties = true, Properties::ReadStyle propertiesStyle = Properties::Average); diff --git a/taglib/riff/wav/wavfile.h b/taglib/riff/wav/wavfile.h index 861f3f77..1c470870 100644 --- a/taglib/riff/wav/wavfile.h +++ b/taglib/riff/wav/wavfile.h @@ -69,6 +69,9 @@ namespace TagLib { * Contructs an WAV file from \a file. If \a readProperties is true the * file's audio properties will also be read using \a propertiesStyle. If * false, \a propertiesStyle is ignored. + * + * \note TagLib will *not* take ownership of the stream, the caller is + * responsible for deleting it after the File object. */ File(IOStream *stream, bool readProperties = true, Properties::ReadStyle propertiesStyle = Properties::Average); diff --git a/taglib/s3m/s3mfile.h b/taglib/s3m/s3mfile.h index 0605b2bf..c862108e 100644 --- a/taglib/s3m/s3mfile.h +++ b/taglib/s3m/s3mfile.h @@ -48,6 +48,9 @@ namespace TagLib { * Contructs a ScreamTracker III file from \a stream. If \a readProperties * is true the file's audio properties will also be read using * \a propertiesStyle. If false, \a propertiesStyle is ignored. + * + * \note TagLib will *not* take ownership of the stream, the caller is + * responsible for deleting it after the File object. */ File(IOStream *stream, bool readProperties = true, AudioProperties::ReadStyle propertiesStyle = diff --git a/taglib/toolkit/tfile.cpp b/taglib/toolkit/tfile.cpp index 30cc9fbc..d0a6116f 100644 --- a/taglib/toolkit/tfile.cpp +++ b/taglib/toolkit/tfile.cpp @@ -74,15 +74,17 @@ using namespace TagLib; class File::FilePrivate { public: - FilePrivate(IOStream *stream); + FilePrivate(IOStream *stream, bool owner); IOStream *stream; + bool streamOwner; bool valid; static const uint bufferSize = 1024; }; -File::FilePrivate::FilePrivate(IOStream *stream) : +File::FilePrivate::FilePrivate(IOStream *stream, bool owner) : stream(stream), + streamOwner(owner), valid(true) { } @@ -94,17 +96,17 @@ File::FilePrivate::FilePrivate(IOStream *stream) : File::File(FileName fileName) { IOStream *stream = new FileStream(fileName); - d = new FilePrivate(stream); + d = new FilePrivate(stream, true); } File::File(IOStream *stream) { - d = new FilePrivate(stream); + d = new FilePrivate(stream, false); } File::~File() { - if(d->stream) + if(d->stream && d->streamOwner) delete d->stream; delete d; } diff --git a/taglib/toolkit/tfile.h b/taglib/toolkit/tfile.h index 7df774a0..7e6f2b93 100644 --- a/taglib/toolkit/tfile.h +++ b/taglib/toolkit/tfile.h @@ -260,6 +260,9 @@ namespace TagLib { /*! * Construct a File object and use the \a stream instance. * + * \note TagLib will *not* take ownership of the stream, the caller is + * responsible for deleting it after the File object. + * * \note Constructor is protected since this class should only be * instantiated through subclasses. */ diff --git a/taglib/trueaudio/trueaudiofile.h b/taglib/trueaudio/trueaudiofile.h index 9b0378f7..e3e1fe62 100644 --- a/taglib/trueaudio/trueaudiofile.h +++ b/taglib/trueaudio/trueaudiofile.h @@ -100,6 +100,9 @@ namespace TagLib { * Contructs an TrueAudio file from \a file. If \a readProperties is true the * file's audio properties will also be read using \a propertiesStyle. If * false, \a propertiesStyle is ignored. + * + * \note TagLib will *not* take ownership of the stream, the caller is + * responsible for deleting it after the File object. */ File(IOStream *stream, bool readProperties = true, Properties::ReadStyle propertiesStyle = Properties::Average); @@ -109,6 +112,9 @@ namespace TagLib { * file's audio properties will also be read using \a propertiesStyle. If * false, \a propertiesStyle is ignored. The frames will be created using * \a frameFactory. + * + * \note TagLib will *not* take ownership of the stream, the caller is + * responsible for deleting it after the File object. */ File(IOStream *stream, ID3v2::FrameFactory *frameFactory, bool readProperties = true, diff --git a/taglib/wavpack/wavpackfile.h b/taglib/wavpack/wavpackfile.h index 02bac023..5bbbc65a 100644 --- a/taglib/wavpack/wavpackfile.h +++ b/taglib/wavpack/wavpackfile.h @@ -91,6 +91,9 @@ namespace TagLib { * Contructs an WavPack file from \a file. If \a readProperties is true the * file's audio properties will also be read using \a propertiesStyle. If * false, \a propertiesStyle is ignored. + * + * \note TagLib will *not* take ownership of the stream, the caller is + * responsible for deleting it after the File object. */ File(IOStream *stream, bool readProperties = true, Properties::ReadStyle propertiesStyle = Properties::Average); diff --git a/taglib/xm/xmfile.h b/taglib/xm/xmfile.h index 1b07010b..9d1bb71c 100644 --- a/taglib/xm/xmfile.h +++ b/taglib/xm/xmfile.h @@ -48,6 +48,9 @@ namespace TagLib { * Contructs a Extended Module file from \a stream. If \a readProperties * is true the file's audio properties will also be read using * \a propertiesStyle. If false, \a propertiesStyle is ignored. + * + * \note TagLib will *not* take ownership of the stream, the caller is + * responsible for deleting it after the File object. */ File(IOStream *stream, bool readProperties = true, AudioProperties::ReadStyle propertiesStyle = From 082a36147b6b453fbfc5c4cd20eb262e5f65ed28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Lalinsk=C3=BD?= Date: Thu, 6 Sep 2012 19:57:10 +0200 Subject: [PATCH 18/67] Add change log, update SOVERSION --- CMakeLists.txt | 4 ++-- NEWS | 17 ++++++++++++++++- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index f4baae46..20a1141b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -56,9 +56,9 @@ set(TAGLIB_LIB_VERSION_STRING "${TAGLIB_LIB_MAJOR_VERSION}.${TAGLIB_LIB_MINOR_VE # 2. If any interfaces have been added, removed, or changed since the last update, increment current, and set revision to 0. # 3. If any interfaces have been added since the last public release, then increment age. # 4. If any interfaces have been removed since the last public release, then set age to 0. -set(TAGLIB_SOVERSION_CURRENT 12) +set(TAGLIB_SOVERSION_CURRENT 13) set(TAGLIB_SOVERSION_REVISION 0) -set(TAGLIB_SOVERSION_AGE 11) +set(TAGLIB_SOVERSION_AGE 12) math(EXPR TAGLIB_SOVERSION_MAJOR "${TAGLIB_SOVERSION_CURRENT} - ${TAGLIB_SOVERSION_AGE}") math(EXPR TAGLIB_SOVERSION_MINOR "${TAGLIB_SOVERSION_AGE}") diff --git a/NEWS b/NEWS index 6c407a48..fa750f69 100644 --- a/NEWS +++ b/NEWS @@ -1,6 +1,21 @@ -TagLib 1.8 BETA (Jul 14, 2012) +TagLib 1.8 (Sep 6, 2012) ============================== +1.8: + + * Added support for OWNE ID3 frames. + * Changed key validation in the new PropertyMap API. + * ID3v1::Tag::setStringHandler will no londer delete the previous handler, + the caller is responsible for this. + * File objects will also no longer delete the passed IOStream objects. It + should be done in the caller code after the File object is no longer + used. + * Added ID3v2::Tag::setLatin1StringHandler for custom handling of + latin1-encoded text in ID3v2 frames. + * Fixed validation of ID3v2 frame IDs (IDs with '0' were ignored). + +1.8 BETA: + * New API for accessing tags by name. * New abstract I/O stream layer to allow custom I/O handlers. * Support for writing ID3v2.3 tags. From 942ec58de59a8e670dcdc82b4f6c2034c9ada12c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Lalinsk=C3=BD?= Date: Thu, 6 Sep 2012 20:03:08 +0200 Subject: [PATCH 19/67] Add tests for String::substr --- tests/test_string.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/test_string.cpp b/tests/test_string.cpp index b6ff972e..79b32c3a 100644 --- a/tests/test_string.cpp +++ b/tests/test_string.cpp @@ -41,6 +41,7 @@ class TestString : public CppUnit::TestFixture CPPUNIT_TEST(testAppendCharDetach); CPPUNIT_TEST(testAppendStringDetach); CPPUNIT_TEST(testToInt); + CPPUNIT_TEST(testSubstr); CPPUNIT_TEST_SUITE_END(); public: @@ -193,6 +194,13 @@ public: CPPUNIT_ASSERT_EQUAL(String("-123aa").toInt(), -123); } + void testSubstr() + { + CPPUNIT_ASSERT_EQUAL(String("01"), String("0123456").substr(0, 2)); + CPPUNIT_ASSERT_EQUAL(String("12"), String("0123456").substr(1, 2)); + CPPUNIT_ASSERT_EQUAL(String("123456"), String("0123456").substr(1, 200)); + } + }; CPPUNIT_TEST_SUITE_REGISTRATION(TestString); From 9c8c215c30aa109737bdd6d94afd9be40055015b Mon Sep 17 00:00:00 2001 From: Tsuda Kageyu Date: Sun, 26 Aug 2012 10:12:40 +0900 Subject: [PATCH 20/67] Support INFO tags of RIFF wave files. --- taglib/riff/rifffile.cpp | 77 +++++++---- taglib/riff/rifffile.h | 27 +++- taglib/riff/wav/infotag.cpp | 250 ++++++++++++++++++++++++++++++++++++ taglib/riff/wav/infotag.h | 179 ++++++++++++++++++++++++++ taglib/riff/wav/wavfile.cpp | 116 +++++++++++++---- taglib/riff/wav/wavfile.h | 27 +++- tests/test_fileref.cpp | 8 +- tests/test_info.cpp | 45 +++++++ 8 files changed, 682 insertions(+), 47 deletions(-) create mode 100644 taglib/riff/wav/infotag.cpp create mode 100644 taglib/riff/wav/infotag.h create mode 100644 tests/test_info.cpp diff --git a/taglib/riff/rifffile.cpp b/taglib/riff/rifffile.cpp index df3b3663..22fd3184 100644 --- a/taglib/riff/rifffile.cpp +++ b/taglib/riff/rifffile.cpp @@ -138,34 +138,44 @@ ByteVector RIFF::File::chunkData(uint i) return readBlock(d->chunks[i].size); } -void RIFF::File::setChunkData(const ByteVector &name, const ByteVector &data) +void RIFF::File::setChunkData(uint i, const ByteVector &data) +{ + // First we update the global size + + d->size += ((data.size() + 1) & ~1) - (d->chunks[i].size + d->chunks[i].padding); + insert(ByteVector::fromUInt(d->size, d->endianness == BigEndian), 4, 4); + + // Now update the specific chunk + + writeChunk(chunkName(i), data, d->chunks[i].offset - 8, d->chunks[i].size + d->chunks[i].padding + 8); + + d->chunks[i].size = data.size(); + d->chunks[i].padding = (data.size() & 0x01) ? 1 : 0; + + // Now update the internal offsets + + for(i++; i < d->chunks.size(); i++) + d->chunks[i].offset = d->chunks[i-1].offset + 8 + d->chunks[i-1].size + d->chunks[i-1].padding; +} + +void RIFF::File::setChunkData(const ByteVector &name, const ByteVector &data, bool alwaysCreate) { if(d->chunks.size() == 0) { debug("RIFF::File::setChunkData - No valid chunks found."); return; } - for(uint i = 0; i < d->chunks.size(); i++) { - if(d->chunks[i].name == name) { + if(alwaysCreate && name != "LIST") { + debug("RIFF::File::setChunkData - alwaysCreate should be used for only \"LIST\" chunks."); + return; + } - // First we update the global size - - d->size += ((data.size() + 1) & ~1) - (d->chunks[i].size + d->chunks[i].padding); - insert(ByteVector::fromUInt(d->size, d->endianness == BigEndian), 4, 4); - - // Now update the specific chunk - - writeChunk(name, data, d->chunks[i].offset - 8, d->chunks[i].size + d->chunks[i].padding + 8); - - d->chunks[i].size = data.size(); - d->chunks[i].padding = (data.size() & 0x01) ? 1 : 0; - - // Now update the internal offsets - - for(i++; i < d->chunks.size(); i++) - d->chunks[i].offset = d->chunks[i-1].offset + 8 + d->chunks[i-1].size + d->chunks[i-1].padding; - - return; + if(!alwaysCreate) { + for(uint i = 0; i < d->chunks.size(); i++) { + if(d->chunks[i].name == name) { + setChunkData(i, data); + return; + } } } @@ -181,7 +191,8 @@ void RIFF::File::setChunkData(const ByteVector &name, const ByteVector &data) // Now add the chunk to the file - writeChunk(name, data, offset, std::max(ulong(0), length() - offset), (offset & 1) ? 1 : 0); + long len = length(); + writeChunk(name, data, offset, std::max(0, length() - offset), (offset & 1) ? 1 : 0); // And update our internal structure @@ -199,6 +210,28 @@ void RIFF::File::setChunkData(const ByteVector &name, const ByteVector &data) d->chunks.push_back(chunk); } +void RIFF::File::removeChunk(uint i) +{ + if(i >= d->chunks.size()) + return; + + removeBlock(d->chunks[i].offset - 8, d->chunks[i].size + 8); + d->chunks.erase(d->chunks.begin() + i); +} + +void RIFF::File::removeChunk(const ByteVector &name) +{ + std::vector newChunks; + for(size_t i = 0; i < d->chunks.size(); ++i) { + if(d->chunks[i].name == name) + removeBlock(d->chunks[i].offset - 8, d->chunks[i].size + 8); + else + newChunks.push_back(d->chunks[i]); + } + + d->chunks.swap(newChunks); +} + //////////////////////////////////////////////////////////////////////////////// // private members //////////////////////////////////////////////////////////////////////////////// diff --git a/taglib/riff/rifffile.h b/taglib/riff/rifffile.h index e418dbb6..274549ae 100644 --- a/taglib/riff/rifffile.h +++ b/taglib/riff/rifffile.h @@ -95,14 +95,39 @@ namespace TagLib { */ ByteVector chunkData(uint i); + /*! + * Sets the data for the the specified chunk to \a data. + * + * \warning This will update the file immediately. + */ + void setChunkData(uint i, const ByteVector &data); + /*! * Sets the data for the chunk \a name to \a data. If a chunk with the * given name already exists it will be overwritten, otherwise it will be * created after the existing chunks. * + * \note If \a alwaysCreate is true, a new chunk is created regardless of + * existence of chunk \a name. It should be used for only "LIST" chunks. + * * \warning This will update the file immediately. */ - void setChunkData(const ByteVector &name, const ByteVector &data); + void setChunkData(const ByteVector &name, const ByteVector &data, bool alwaysCreate = false); + + /*! + * Removes the specified chunk. + * + * \warning This will update the file immediately. + */ + void removeChunk(uint i); + + /*! + * Removes the chunk \a name. + * + * \warning This will update the file immediately. + * \warning This removes all the chunks with the given name. + */ + void removeChunk(const ByteVector &name); private: File(const File &); diff --git a/taglib/riff/wav/infotag.cpp b/taglib/riff/wav/infotag.cpp new file mode 100644 index 00000000..e3587775 --- /dev/null +++ b/taglib/riff/wav/infotag.cpp @@ -0,0 +1,250 @@ +/*************************************************************************** + copyright : (C) 2002 - 2008 by Scott Wheeler + email : wheeler@kde.org + ***************************************************************************/ + +/*************************************************************************** + * This library is free software; you can redistribute it and/or modify * + * it under the terms of the GNU Lesser General Public License version * + * 2.1 as published by the Free Software Foundation. * + * * + * This library is distributed in the hope that it will be useful, but * + * WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this library; if not, write to the Free Software * + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * + * 02110-1301 USA * + * * + * Alternatively, this file is available under the Mozilla Public * + * License Version 1.1. You may obtain a copy of the License at * + * http://www.mozilla.org/MPL/ * + ***************************************************************************/ + +#include +#include + +#include "infotag.h" + +using namespace TagLib; +using namespace RIFF::Info; + +namespace { + static bool isValidChunkID(const ByteVector &name) + { + if(name.size() != 4) + return false; + + for(int i = 0; i < 4; i++) { + if(name[i] < 32 || name[i] > 127) + return false; + } + + return true; + } +} + +class RIFF::Info::Tag::TagPrivate +{ +public: + TagPrivate() + {} + + FieldListMap fieldListMap; + + static const StringHandler *stringHandler; +}; + + + +StringHandler::StringHandler() +{ +} + +StringHandler::~StringHandler() +{ +} + +String RIFF::Info::StringHandler::parse(const ByteVector &data) const +{ + return String(data, String::UTF8); +} + +ByteVector RIFF::Info::StringHandler::render(const String &s) const +{ + return s.data(String::UTF8); +} + + + +static const StringHandler defaultStringHandler; +const RIFF::Info::StringHandler *RIFF::Info::Tag::TagPrivate::stringHandler = &defaultStringHandler; + +RIFF::Info::Tag::Tag(const ByteVector &data) : TagLib::Tag() +{ + d = new TagPrivate; + parse(data); +} + +RIFF::Info::Tag::Tag() : TagLib::Tag() +{ + d = new TagPrivate; +} + +RIFF::Info::Tag::~Tag() +{ +} + +String RIFF::Info::Tag::title() const +{ + return fieldText("INAM"); +} + +String RIFF::Info::Tag::artist() const +{ + return fieldText("IART"); +} + +String RIFF::Info::Tag::album() const +{ + return fieldText("IPRD"); +} + +String RIFF::Info::Tag::comment() const +{ + return fieldText("ICMT"); +} + +String RIFF::Info::Tag::genre() const +{ + return fieldText("IGNR"); +} + +TagLib::uint RIFF::Info::Tag::year() const +{ + return fieldText("ICRD").substr(0, 4).toInt(); +} + +TagLib::uint RIFF::Info::Tag::track() const +{ + return fieldText("IPRT").toInt(); +} + +void RIFF::Info::Tag::setTitle(const String &s) +{ + setFieldText("INAM", s); +} + +void RIFF::Info::Tag::setArtist(const String &s) +{ + setFieldText("IART", s); +} + +void RIFF::Info::Tag::setAlbum(const String &s) +{ + setFieldText("IPRD", s); +} + +void RIFF::Info::Tag::setComment(const String &s) +{ + setFieldText("ICMT", s); +} + +void RIFF::Info::Tag::setGenre(const String &s) +{ + setFieldText("IGNR", s); +} + +void RIFF::Info::Tag::setYear(uint i) +{ + if(i != 0) + setFieldText("ICRD", String::number(i)); + else + d->fieldListMap.erase("ICRD"); +} + +void RIFF::Info::Tag::setTrack(uint i) +{ + if(i != 0) + setFieldText("IPRT", String::number(i)); + else + d->fieldListMap.erase("IPRT"); +} + +bool RIFF::Info::Tag::isEmpty() const +{ + return d->fieldListMap.isEmpty(); +} + +String RIFF::Info::Tag::fieldText(const ByteVector &id) const +{ + if(d->fieldListMap.contains(id)) + return String(d->fieldListMap[id]); + else + return String(); +} + +void RIFF::Info::Tag::setFieldText(const ByteVector &id, const String &s) +{ + // id must be four-byte long pure ascii string. + if(!isValidChunkID(id)) + return; + + if(!s.isEmpty()) + d->fieldListMap[id] = s; + else + removeField(id); +} + +void RIFF::Info::Tag::removeField(const ByteVector &id) +{ + if(d->fieldListMap.contains(id)) + d->fieldListMap.erase(id); +} + +ByteVector RIFF::Info::Tag::render() const +{ + ByteVector data("INFO"); + + FieldListMap::ConstIterator it = d->fieldListMap.begin(); + for(; it != d->fieldListMap.end(); ++it) { + ByteVector text = TagPrivate::stringHandler->render(it->second); + if(text.isEmpty()) + continue; + + data.append(it->first); + data.append(ByteVector::fromUInt(text.size() + 1, false)); + data.append(text); + + do { + data.append('\0'); + } while(data.size() & 1); + } + + if(data.size() == 4) + return ByteVector(); + else + return data; +} + +void RIFF::Info::Tag::setStringHandler(const StringHandler *handler) +{ + if(handler) + TagPrivate::stringHandler = handler; + else + TagPrivate::stringHandler = &defaultStringHandler; +} + +void RIFF::Info::Tag::parse(const ByteVector &data) +{ + uint p = 4; + while(p < data.size()) { + uint size = data.mid(p + 4, 4).toUInt(false); + d->fieldListMap[data.mid(p, 4)] = TagPrivate::stringHandler->parse(data.mid(p + 8, size)); + + p += ((size + 1) & ~1) + 8; + } +} + diff --git a/taglib/riff/wav/infotag.h b/taglib/riff/wav/infotag.h new file mode 100644 index 00000000..72414841 --- /dev/null +++ b/taglib/riff/wav/infotag.h @@ -0,0 +1,179 @@ +/*************************************************************************** + copyright : (C) 2002 - 2008 by Scott Wheeler + email : wheeler@kde.org + ***************************************************************************/ + +/*************************************************************************** + * This library is free software; you can redistribute it and/or modify * + * it under the terms of the GNU Lesser General Public License version * + * 2.1 as published by the Free Software Foundation. * + * * + * This library is distributed in the hope that it will be useful, but * + * WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this library; if not, write to the Free Software * + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * + * 02110-1301 USA * + * * + * Alternatively, this file is available under the Mozilla Public * + * License Version 1.1. You may obtain a copy of the License at * + * http://www.mozilla.org/MPL/ * + ***************************************************************************/ + +#ifndef TAGLIB_INFOTAG_H +#define TAGLIB_INFOTAG_H + +#include "tag.h" +#include "tmap.h" +#include "tstring.h" +#include "tstringlist.h" +#include "tbytevector.h" +#include "taglib_export.h" + +namespace TagLib { + + class File; + + //! A RIFF Info tag implementation. + namespace RIFF { + namespace Info { + + typedef Map FieldListMap; + + //! A abstraction for the string to data encoding in Info tags. + + /*! + * RIFF Info tag has no clear definitions about character encodings. + * In practice, local encoding of each system is largely used and UTF-8 is + * popular too. + * + * Here is an option to read and write tags in your preferrd encoding + * by subclassing this class, reimplementing parse() and render() and setting + * your reimplementation as the default with Info::Tag::setStringHandler(). + * + * \see ID3v1::Tag::setStringHandler() + */ + + class TAGLIB_EXPORT StringHandler + { + public: + StringHandler(); + ~StringHandler(); + + /*! + * Decode a string from \a data. The default implementation assumes that + * \a data is an UTF-8 character array. + */ + virtual String parse(const ByteVector &data) const; + + /*! + * Encode a ByteVector with the data from \a s. The default implementation + * assumes that \a s is an UTF-8 string. + */ + virtual ByteVector render(const String &s) const; + }; + + //! The main class in the ID3v2 implementation + + /*! + * This is the main class in the INFO tag implementation. RIFF INFO tag is a + * metadata format found in WAV audio and AVI video files. Though it is a part + * of Microsoft/IBM's RIFF specification, the author could not find the official + * documents about it. So, this implementation is refering to unofficial documents + * online and some applications' behaviors especially Windows Explorer. + */ + class TAGLIB_EXPORT Tag : public TagLib::Tag + { + public: + /*! + * Constructs an empty Info tag. + */ + Tag(); + + /*! + * Constructs an Info tag read from \a data which is contents of "LIST" chunk. + */ + Tag(const ByteVector &data); + + virtual ~Tag(); + + // Reimplementations + + virtual String title() const; + virtual String artist() const; + virtual String album() const; + virtual String comment() const; + virtual String genre() const; + virtual uint year() const; + virtual uint track() const; + + virtual void setTitle(const String &s); + virtual void setArtist(const String &s); + virtual void setAlbum(const String &s); + virtual void setComment(const String &s); + virtual void setGenre(const String &s); + virtual void setYear(uint i); + virtual void setTrack(uint i); + + virtual bool isEmpty() const; + /* + * Gets the value of the field with the ID \a id. + */ + String fieldText(const ByteVector &id) const; + + /* + * Sets the value of the field with the ID \a id to \a s. + * If the field does not exist, it is created. + * If \s is empty, the field is removed. + * + * \note fieldId must be four-byte long pure ascii string. This function + * performs nothing if fieldId is invalid. + */ + void setFieldText(const ByteVector &id, const String &s); + + /* + * Removes the field with the ID \a id. + */ + void removeField(const ByteVector &id); + + /*! + * Render the tag back to binary data, suitable to be written to disk. + * + * \note Returns empty ByteVector is the tag contains no fields. + */ + ByteVector render() const; + + /*! + * Sets the string handler that decides how the text data will be + * converted to and from binary data. + * If the parameter \a handler is null, the previous handler is + * released and default UTF-8 handler is restored. + * + * \note The caller is responsible for deleting the previous handler + * as needed after it is released. + * + * \see StringHandler + */ + static void setStringHandler(const StringHandler *handler); + + protected: + /*! + * Pareses the body of the tag in \a data. + */ + void parse(const ByteVector &data); + + + private: + Tag(const Tag &); + Tag &operator=(const Tag &); + + class TagPrivate; + TagPrivate *d; + }; + }} +} + +#endif diff --git a/taglib/riff/wav/wavfile.cpp b/taglib/riff/wav/wavfile.cpp index 613db4ef..dd7da2c8 100644 --- a/taglib/riff/wav/wavfile.cpp +++ b/taglib/riff/wav/wavfile.cpp @@ -23,36 +23,42 @@ * http://www.mozilla.org/MPL/ * ***************************************************************************/ -#include -#include -#include -#include -#include +#include "tbytevector.h" +#include "tdebug.h" +#include "tstringlist.h" +#include "tpropertymap.h" #include "wavfile.h" +#include "id3v2tag.h" +#include "infotag.h" +#include "tagunion.h" using namespace TagLib; +namespace +{ + enum { ID3v2Index = 0, InfoIndex = 1 }; +} + class RIFF::WAV::File::FilePrivate { public: FilePrivate() : properties(0), - tag(0), tagChunkID("ID3 ") { - } ~FilePrivate() { delete properties; - delete tag; } Properties *properties; - ID3v2::Tag *tag; + ByteVector tagChunkID; + + TagUnion tag; }; //////////////////////////////////////////////////////////////////////////////// @@ -80,28 +86,42 @@ RIFF::WAV::File::~File() delete d; } -ID3v2::Tag *RIFF::WAV::File::tag() const +Tag *RIFF::WAV::File::tag() const { - return d->tag; + return &d->tag; +} + +ID3v2::Tag *RIFF::WAV::File::ID3v2Tag() const +{ + return d->tag.access(ID3v2Index, false); +} + +RIFF::Info::Tag *RIFF::WAV::File::InfoTag() const +{ + return d->tag.access(InfoIndex, false); } PropertyMap RIFF::WAV::File::properties() const { - return d->tag->properties(); + return d->tag.properties(); } PropertyMap RIFF::WAV::File::setProperties(const PropertyMap &properties) { - return d->tag->setProperties(properties); + return d->tag.setProperties(properties); } - RIFF::WAV::Properties *RIFF::WAV::File::audioProperties() const { return d->properties; } bool RIFF::WAV::File::save() +{ + return RIFF::WAV::File::save(AllTags); +} + +bool RIFF::WAV::File::save(TagTypes tags, bool stripOthers, int id3v2Version) { if(readOnly()) { debug("RIFF::WAV::File::save() -- File is read only."); @@ -113,7 +133,25 @@ bool RIFF::WAV::File::save() return false; } - setChunkData(d->tagChunkID, d->tag->render()); + if(stripOthers) + strip(static_cast(AllTags & ~tags)); + + ID3v2::Tag *id3v2tag = d->tag.access(ID3v2Index, false); + if(!id3v2tag->isEmpty()) { + if(tags & ID3v2) + setChunkData(d->tagChunkID, id3v2tag->render(id3v2Version)); + } + + Info::Tag *infotag = d->tag.access(InfoIndex, false); + if(!infotag->isEmpty()) { + if(tags & Info) { + int chunkId = findInfoTagChunk(); + if(chunkId != -1) + setChunkData(chunkId, infotag->render()); + else + setChunkData("LIST", infotag->render(), true); + } + } return true; } @@ -127,19 +165,53 @@ void RIFF::WAV::File::read(bool readProperties, Properties::ReadStyle properties ByteVector formatData; uint streamLength = 0; for(uint i = 0; i < chunkCount(); i++) { - if(chunkName(i) == "ID3 " || chunkName(i) == "id3 ") { + String name = chunkName(i); + if(name == "ID3 " || name == "id3 ") { d->tagChunkID = chunkName(i); - d->tag = new ID3v2::Tag(this, chunkOffset(i)); + d->tag.set(ID3v2Index, new ID3v2::Tag(this, chunkOffset(i))); } - else if(chunkName(i) == "fmt " && readProperties) + else if(name == "fmt " && readProperties) formatData = chunkData(i); - else if(chunkName(i) == "data" && readProperties) + else if(name == "data" && readProperties) streamLength = chunkDataSize(i); + else if(name == "LIST") { + ByteVector data = chunkData(i); + ByteVector type = data.mid(0, 4); + + if(type == "INFO") + d->tag.set(InfoIndex, new RIFF::Info::Tag(data)); + } } + if (!d->tag[ID3v2Index]) + d->tag.set(ID3v2Index, new ID3v2::Tag); + + if (!d->tag[InfoIndex]) + d->tag.set(InfoIndex, new RIFF::Info::Tag); + if(!formatData.isEmpty()) d->properties = new Properties(formatData, streamLength, propertiesStyle); - - if(!d->tag) - d->tag = new ID3v2::Tag; +} + +void RIFF::WAV::File::strip(TagTypes tags) +{ + if(tags & ID3v2) + removeChunk(d->tagChunkID); + + if(tags & Info){ + uint chunkId = findInfoTagChunk(); + if(chunkId != -1) + removeChunk(chunkId); + } +} + +uint RIFF::WAV::File::findInfoTagChunk() +{ + for(uint i = 0; i < chunkCount(); ++i) { + if(chunkName(i) == "LIST" && chunkData(i).mid(0, 4) == "INFO") { + return i; + } + } + + return -1; } diff --git a/taglib/riff/wav/wavfile.h b/taglib/riff/wav/wavfile.h index 1c470870..c0fa122e 100644 --- a/taglib/riff/wav/wavfile.h +++ b/taglib/riff/wav/wavfile.h @@ -28,6 +28,7 @@ #include "rifffile.h" #include "id3v2tag.h" +#include "infotag.h" #include "wavproperties.h" namespace TagLib { @@ -57,6 +58,17 @@ namespace TagLib { class TAGLIB_EXPORT File : public TagLib::RIFF::File { public: + enum TagTypes { + //! Empty set. Matches no tag types. + NoTags = 0x0000, + //! Matches ID3v2 tags. + ID3v2 = 0x0001, + //! Matches Info tags. + Info = 0x0002, + //! Matches all tag types. + AllTags = 0xffff + }; + /*! * Contructs an WAV file from \a file. If \a readProperties is true the * file's audio properties will also be read using \a propertiesStyle. If @@ -84,7 +96,11 @@ namespace TagLib { /*! * Returns the Tag for this file. */ - virtual ID3v2::Tag *tag() const; + virtual Tag *tag() const; + + ID3v2::Tag *ID3v2Tag() const; + + Info::Tag *InfoTag() const; /*! * Implements the unified property interface -- export function. @@ -109,12 +125,21 @@ namespace TagLib { */ virtual bool save(); + bool save(TagTypes tags, bool stripOthers = true, int id3v2Version = 4); + private: File(const File &); File &operator=(const File &); void read(bool readProperties, Properties::ReadStyle propertiesStyle); + void strip(TagTypes tags); + + /*! + * Returns the index of the chunk that its name is "LIST" and list type is "INFO". + */ + uint findInfoTagChunk(); + class FilePrivate; FilePrivate *d; }; diff --git a/tests/test_fileref.cpp b/tests/test_fileref.cpp index 3268fdb4..d0c8a3a5 100644 --- a/tests/test_fileref.cpp +++ b/tests/test_fileref.cpp @@ -29,6 +29,7 @@ class TestFileRef : public CppUnit::TestFixture CPPUNIT_TEST(testMP4_3); CPPUNIT_TEST(testTrueAudio); CPPUNIT_TEST(testAPE); + CPPUNIT_TEST(testWav); CPPUNIT_TEST_SUITE_END(); public: @@ -127,6 +128,11 @@ public: fileRefSave("no-tags", ".3g2"); } + void testWav() + { + fileRefSave("empty", ".wav"); + } + void testOGA_FLAC() { FileRef *f = new FileRef(TEST_FILE_PATH_C("empty_flac.oga")); @@ -143,7 +149,7 @@ public: void testAPE() { - fileRefSave("mac-399.ape", ".ape"); + fileRefSave("mac-399", ".ape"); } }; diff --git a/tests/test_info.cpp b/tests/test_info.cpp new file mode 100644 index 00000000..f76fd67a --- /dev/null +++ b/tests/test_info.cpp @@ -0,0 +1,45 @@ +#include +#include +#include +#include +#include +#include +#include "utils.h" + +using namespace std; +using namespace TagLib; + +class TestInfoTag : public CppUnit::TestFixture +{ + CPPUNIT_TEST_SUITE(TestInfoTag); + CPPUNIT_TEST(testTitle); + CPPUNIT_TEST(testNumericFields); + CPPUNIT_TEST_SUITE_END(); + +public: + void testTitle() + { + RIFF::Info::Tag tag; + + CPPUNIT_ASSERT_EQUAL(String(""), tag.title()); + tag.setTitle("Test title 1"); + CPPUNIT_ASSERT_EQUAL(String("Test title 1"), tag.title()); + } + + void testNumericFields() + { + RIFF::Info::Tag tag; + + CPPUNIT_ASSERT_EQUAL((uint)0, tag.track()); + tag.setTrack(1234); + CPPUNIT_ASSERT_EQUAL((uint)1234, tag.track()); + CPPUNIT_ASSERT_EQUAL(String("1234"), tag.fieldText("IPRT")); + + CPPUNIT_ASSERT_EQUAL((uint)0, tag.year()); + tag.setYear(1234); + CPPUNIT_ASSERT_EQUAL((uint)1234, tag.year()); + CPPUNIT_ASSERT_EQUAL(String("1234"), tag.fieldText("ICRD")); + } +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(TestInfoTag); From 61c8013f2c3b824619fc2a4c25862433528ac5ba Mon Sep 17 00:00:00 2001 From: Tsuda Kageyu Date: Mon, 27 Aug 2012 00:41:09 +0900 Subject: [PATCH 21/67] Added missing comments --- taglib/riff/wav/infotag.cpp | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/taglib/riff/wav/infotag.cpp b/taglib/riff/wav/infotag.cpp index e3587775..df1d2f81 100644 --- a/taglib/riff/wav/infotag.cpp +++ b/taglib/riff/wav/infotag.cpp @@ -57,7 +57,9 @@ public: static const StringHandler *stringHandler; }; - +//////////////////////////////////////////////////////////////////////////////// +// StringHandler implementation +//////////////////////////////////////////////////////////////////////////////// StringHandler::StringHandler() { @@ -77,7 +79,9 @@ ByteVector RIFF::Info::StringHandler::render(const String &s) const return s.data(String::UTF8); } - +//////////////////////////////////////////////////////////////////////////////// +// public members +//////////////////////////////////////////////////////////////////////////////// static const StringHandler defaultStringHandler; const RIFF::Info::StringHandler *RIFF::Info::Tag::TagPrivate::stringHandler = &defaultStringHandler; @@ -237,6 +241,10 @@ void RIFF::Info::Tag::setStringHandler(const StringHandler *handler) TagPrivate::stringHandler = &defaultStringHandler; } +//////////////////////////////////////////////////////////////////////////////// +// protected members +//////////////////////////////////////////////////////////////////////////////// + void RIFF::Info::Tag::parse(const ByteVector &data) { uint p = 4; From 071a1477b54a64578b74db3c9a8ae26e2293b6c0 Mon Sep 17 00:00:00 2001 From: Tsuda Kageyu Date: Mon, 27 Aug 2012 18:29:12 +0900 Subject: [PATCH 22/67] Fixed an API change --- taglib/riff/wav/wavfile.cpp | 4 ++-- taglib/riff/wav/wavfile.h | 13 +++++++++++-- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/taglib/riff/wav/wavfile.cpp b/taglib/riff/wav/wavfile.cpp index dd7da2c8..489adc3b 100644 --- a/taglib/riff/wav/wavfile.cpp +++ b/taglib/riff/wav/wavfile.cpp @@ -86,9 +86,9 @@ RIFF::WAV::File::~File() delete d; } -Tag *RIFF::WAV::File::tag() const +ID3v2::Tag *RIFF::WAV::File::tag() const { - return &d->tag; + return ID3v2Tag(); } ID3v2::Tag *RIFF::WAV::File::ID3v2Tag() const diff --git a/taglib/riff/wav/wavfile.h b/taglib/riff/wav/wavfile.h index c0fa122e..287db25b 100644 --- a/taglib/riff/wav/wavfile.h +++ b/taglib/riff/wav/wavfile.h @@ -94,12 +94,21 @@ namespace TagLib { virtual ~File(); /*! - * Returns the Tag for this file. + * Returns the ID3v2 Tag for this file. + * + * \note This method does not return all the tags for this file for + * backward compatibility. Will be fixed in TagLib 2.0. */ - virtual Tag *tag() const; + ID3v2::Tag *RIFF::WAV::File::tag() const; + /*! + * Returns the ID3v2 Tag for this file. + */ ID3v2::Tag *ID3v2Tag() const; + /*! + * Returns the RIFF INFO Tag for this file. + */ Info::Tag *InfoTag() const; /*! From 43e100fd379c305b5b11f788234500f7df07a1fc Mon Sep 17 00:00:00 2001 From: Tsuda Kageyu Date: Fri, 7 Sep 2012 23:48:06 +0900 Subject: [PATCH 23/67] FileStream improvement in Win32 --- taglib/toolkit/tfilestream.cpp | 167 +++++++++++++++++++++------------ 1 file changed, 107 insertions(+), 60 deletions(-) diff --git a/taglib/toolkit/tfilestream.cpp b/taglib/toolkit/tfilestream.cpp index b1fd9f80..928a2d2d 100644 --- a/taglib/toolkit/tfilestream.cpp +++ b/taglib/toolkit/tfilestream.cpp @@ -35,79 +35,65 @@ # include # include # include -# define ftruncate _chsize #else # include #endif #include -#ifndef R_OK -# define R_OK 4 -#endif -#ifndef W_OK -# define W_OK 2 -#endif - using namespace TagLib; +namespace { #ifdef _WIN32 -typedef FileName FileNameHandle; + // For Windows + + typedef FileName FileNameHandle; + + // Using native file handles instead of file descriptors for reducing the resource consumption. + + HANDLE openFile(const FileName &path, bool readOnly) + { + DWORD access = readOnly ? GENERIC_READ : (GENERIC_READ | GENERIC_WRITE); + + if(wcslen(path) > 0) + return CreateFileW(path, access, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); + else + return CreateFileA(path, access, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); + } + + size_t fread(void *ptr, size_t size, size_t nmemb, HANDLE stream) + { + DWORD read_len; + ReadFile(stream, ptr, size * nmemb, &read_len, NULL); + + return (read_len / size); + } + + size_t fwrite(const void *ptr, size_t size, size_t nmemb, HANDLE stream) + { + DWORD written_len; + WriteFile(stream, ptr, size * nmemb, &written_len, NULL); + + return written_len; + } #else -struct FileNameHandle : public std::string -{ - FileNameHandle(FileName name) : std::string(name) {} - operator FileName () const { return c_str(); } -}; + // For non-Windows -#endif + struct FileNameHandle : public std::string + { + FileNameHandle(FileName name) : std::string(name) {} + operator FileName () const { return c_str(); } + }; -namespace { FILE *openFile(const FileName &path, bool readOnly) { - // Calls a proper variation of fopen() depending on the compiling environment. - -#if defined(_WIN32) - -# if defined(_MSC_VER) && (_MSC_VER >= 1400) - - // Visual C++ 2005 or later. - - FILE *file; - errno_t err; - - if(wcslen(path) > 0) - err = _wfopen_s(&file, path, readOnly ? L"rb" : L"rb+"); - else - err = fopen_s(&file, path, readOnly ? "rb" : "rb+"); - - if(err == 0) - return file; - else - return NULL; - -# else // defined(_MSC_VER) && (_MSC_VER >= 1400) - - // Visual C++.NET 2003 or earlier. - - if(wcslen(path) > 0) - return _wfopen(path, readOnly ? L"rb" : L"rb+"); - else - return fopen(path, readOnly ? "rb" : "rb+"); - -# endif // defined(_MSC_VER) && (_MSC_VER >= 1400) - -#else // defined(_WIN32) - - // Non-Win32 - return fopen(path, readOnly ? "rb" : "rb+"); - -#endif // defined(_WIN32) } + +#endif } class FileStream::FileStreamPrivate @@ -115,8 +101,16 @@ class FileStream::FileStreamPrivate public: FileStreamPrivate(FileName fileName, bool openReadOnly); +#ifdef _WIN32 + + HANDLE file; + +#else + FILE *file; +#endif + FileNameHandle name; bool readOnly; @@ -156,8 +150,18 @@ FileStream::FileStream(FileName file, bool openReadOnly) FileStream::~FileStream() { +#ifdef _WIN32 + + if(d->file) + CloseHandle(d->file); + +#else + if(d->file) fclose(d->file); + +#endif + delete d; } @@ -348,27 +352,67 @@ void FileStream::seek(long offset, Position p) return; } +#ifdef _WIN32 + + DWORD whence; switch(p) { case Beginning: - fseek(d->file, offset, SEEK_SET); + whence = FILE_BEGIN; break; case Current: - fseek(d->file, offset, SEEK_CUR); + whence = FILE_CURRENT; break; case End: - fseek(d->file, offset, SEEK_END); + whence = FILE_END; break; } + + SetFilePointer(d->file, offset, NULL, whence); + +#else + + int whence; + switch(p) { + case Beginning: + whence = SEEK_SET; + break; + case Current: + whence = SEEK_CUR; + break; + case End: + whence = SEEK_END; + break; + } + + fseek(d->file, offset, whence); + +#endif } void FileStream::clear() { +#ifdef _WIN32 + + // NOP + +#else + clearerr(d->file); + +#endif } long FileStream::tell() const { +#ifdef _WIN32 + + return (long)SetFilePointer(d->file, 0, NULL, FILE_CURRENT); + +#else + return ftell(d->file); + +#endif } long FileStream::length() @@ -398,17 +442,20 @@ long FileStream::length() void FileStream::truncate(long length) { +#ifdef _WIN32 -#if defined(_MSC_VER) && (_MSC_VER >= 1400) // VC++2005 or later + long current_pos = tell(); - ftruncate(_fileno(d->file), length); + seek(length); + SetEndOfFile(d->file); + + seek(current_pos); #else ftruncate(fileno(d->file), length); #endif - } TagLib::uint FileStream::bufferSize() From 4582ea3b271723079b3b64ee8cbd1f934e2e6fef Mon Sep 17 00:00:00 2001 From: Tsuda Kageyu Date: Sat, 8 Sep 2012 01:17:15 +0900 Subject: [PATCH 24/67] Fixed CMakeLists.txt --- taglib/CMakeLists.txt | 2 ++ taglib/riff/wav/wavfile.h | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/taglib/CMakeLists.txt b/taglib/CMakeLists.txt index 61bcb49e..ebb9165d 100644 --- a/taglib/CMakeLists.txt +++ b/taglib/CMakeLists.txt @@ -103,6 +103,7 @@ set(tag_HDRS riff/aiff/aiffproperties.h riff/wav/wavfile.h riff/wav/wavproperties.h + riff/wav/infotag.h asf/asffile.h asf/asfproperties.h asf/asftag.h @@ -244,6 +245,7 @@ set(aiff_SRCS set(wav_SRCS riff/wav/wavfile.cpp riff/wav/wavproperties.cpp + riff/wav/infotag.cpp ) set(mod_SRCS diff --git a/taglib/riff/wav/wavfile.h b/taglib/riff/wav/wavfile.h index 287db25b..18ce2336 100644 --- a/taglib/riff/wav/wavfile.h +++ b/taglib/riff/wav/wavfile.h @@ -99,7 +99,7 @@ namespace TagLib { * \note This method does not return all the tags for this file for * backward compatibility. Will be fixed in TagLib 2.0. */ - ID3v2::Tag *RIFF::WAV::File::tag() const; + ID3v2::Tag *tag() const; /*! * Returns the ID3v2 Tag for this file. From e7126db97c8bfadbd84be235ddb5a1e90d450ca0 Mon Sep 17 00:00:00 2001 From: Tsuda Kageyu Date: Sat, 8 Sep 2012 14:13:20 +0900 Subject: [PATCH 25/67] Add MPEG::FILE::Has*Tag functions --- taglib/mpeg/mpegfile.cpp | 15 +++++++++++++++ taglib/mpeg/mpegfile.h | 15 +++++++++++++++ tests/test_mpeg.cpp | 6 ++++++ 3 files changed, 36 insertions(+) diff --git a/taglib/mpeg/mpegfile.cpp b/taglib/mpeg/mpegfile.cpp index 6ebff897..ec094cdc 100644 --- a/taglib/mpeg/mpegfile.cpp +++ b/taglib/mpeg/mpegfile.cpp @@ -437,6 +437,21 @@ long MPEG::File::lastFrameOffset() return previousFrameOffset(ID3v1Tag() ? d->ID3v1Location - 1 : length()); } +bool MPEG::File::hasID3v1Tag() const +{ + return d->hasID3v1; +} + +bool MPEG::File::hasID3v2Tag() const +{ + return d->hasID3v2; +} + +bool MPEG::File::hasAPETag() const +{ + return d->hasAPE; +} + //////////////////////////////////////////////////////////////////////////////// // private members //////////////////////////////////////////////////////////////////////////////// diff --git a/taglib/mpeg/mpegfile.h b/taglib/mpeg/mpegfile.h index 9967a991..f7b98364 100644 --- a/taglib/mpeg/mpegfile.h +++ b/taglib/mpeg/mpegfile.h @@ -301,6 +301,21 @@ namespace TagLib { */ long lastFrameOffset(); + /*! + * Returns whether or not the file on disk contains ID3v1 tag. + */ + bool hasID3v1Tag() const; + + /*! + * Returns whether or not the file on disk contains ID3v2 tag. + */ + bool hasID3v2Tag() const; + + /*! + * Returns whether or not the file on disk contains APE tag. + */ + bool hasAPETag() const; + private: File(const File &); File &operator=(const File &); diff --git a/tests/test_mpeg.cpp b/tests/test_mpeg.cpp index 973803f9..18724728 100644 --- a/tests/test_mpeg.cpp +++ b/tests/test_mpeg.cpp @@ -32,9 +32,12 @@ public: String xxx = ByteVector(254, 'X'); MPEG::File f(newname.c_str()); + CPPUNIT_ASSERT_EQUAL(false, f.hasID3v2Tag()); + f.tag()->setTitle(xxx); f.tag()->setArtist("Artist A"); f.save(MPEG::File::AllTags, true, 4); + CPPUNIT_ASSERT_EQUAL(true, f.hasID3v2Tag()); MPEG::File f2(newname.c_str()); CPPUNIT_ASSERT_EQUAL(TagLib::uint(4), f2.ID3v2Tag()->header()->majorVersion()); @@ -66,9 +69,12 @@ public: String xxx = ByteVector(254, 'X'); MPEG::File f(newname.c_str()); + CPPUNIT_ASSERT_EQUAL(false, f.hasID3v2Tag()); + f.tag()->setTitle(xxx); f.tag()->setArtist("Artist A"); f.save(MPEG::File::AllTags, true, 3); + CPPUNIT_ASSERT_EQUAL(true, f.hasID3v2Tag()); MPEG::File f2(newname.c_str()); CPPUNIT_ASSERT_EQUAL(TagLib::uint(3), f2.ID3v2Tag()->header()->majorVersion()); From 7b80418122a42295ada34863609d82152baab03c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Lalinsk=C3=BD?= Date: Wed, 26 Sep 2012 20:30:53 +0200 Subject: [PATCH 26/67] Fix the version number --- taglib/toolkit/taglib.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/taglib/toolkit/taglib.h b/taglib/toolkit/taglib.h index dda9c83c..d41e419e 100644 --- a/taglib/toolkit/taglib.h +++ b/taglib/toolkit/taglib.h @@ -27,7 +27,7 @@ #define TAGLIB_H #define TAGLIB_MAJOR_VERSION 1 -#define TAGLIB_MINOR_VERSION 7 +#define TAGLIB_MINOR_VERSION 8 #define TAGLIB_PATCH_VERSION 0 #if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ > 1)) From 1086d4476d8c82c13ba142607be0fb6b9839b814 Mon Sep 17 00:00:00 2001 From: Festus Hagen Date: Fri, 28 Sep 2012 21:41:45 -0400 Subject: [PATCH 27/67] Win32 taglib-config.cmd support. --- CMakeLists.txt | 11 +++++++++-- taglib-config.cmd.cmake | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 2 deletions(-) mode change 100644 => 100755 CMakeLists.txt create mode 100755 taglib-config.cmd.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt old mode 100644 new mode 100755 index 20a1141b..1f29e463 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -66,8 +66,15 @@ math(EXPR TAGLIB_SOVERSION_PATCH "${TAGLIB_SOVERSION_REVISION}") include(ConfigureChecks.cmake) -configure_file(${CMAKE_CURRENT_SOURCE_DIR}/taglib-config.cmake ${CMAKE_CURRENT_BINARY_DIR}/taglib-config ) -install(PROGRAMS ${CMAKE_CURRENT_BINARY_DIR}/taglib-config DESTINATION ${BIN_INSTALL_DIR}) +if(NOT WIN32) + configure_file(${CMAKE_CURRENT_SOURCE_DIR}/taglib-config.cmake ${CMAKE_CURRENT_BINARY_DIR}/taglib-config ) + install(PROGRAMS ${CMAKE_CURRENT_BINARY_DIR}/taglib-config DESTINATION ${BIN_INSTALL_DIR}) +endif() + +if(WIN32) + configure_file(${CMAKE_CURRENT_SOURCE_DIR}/taglib-config.cmd.cmake ${CMAKE_CURRENT_BINARY_DIR}/taglib-config.cmd ) + install(PROGRAMS ${CMAKE_CURRENT_BINARY_DIR}/taglib-config.cmd DESTINATION ${BIN_INSTALL_DIR}) +endif() if(NOT WIN32 AND NOT BUILD_FRAMEWORK) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/taglib.pc.cmake ${CMAKE_CURRENT_BINARY_DIR}/taglib.pc ) diff --git a/taglib-config.cmd.cmake b/taglib-config.cmd.cmake new file mode 100755 index 00000000..bef752ed --- /dev/null +++ b/taglib-config.cmd.cmake @@ -0,0 +1,36 @@ +@echo off +goto beginning + * + * It is what it is, you can do with it as you please. + * + * Just don't blame me if it teaches your computer to smoke! + * + * -Enjoy + * fh :)_~ + * +:beginning +if /i "%1#" == "--libs#" goto doit +if /i "%1#" == "--cflags#" goto doit +if /i "%1#" == "--version#" goto doit +if /i "%1#" == "--prefix#" goto doit + +echo "usage: %0 [OPTIONS]" +echo [--libs] +echo [--cflags] +echo [--version] +echo [--prefix] +goto theend + + * + * NOTE: Windows does not assume libraries are prefixed with 'lib'. + * NOTE: If '-llibtag' is the last element, it is easily appended in the users installation/makefile process + * to allow for static, shared or debug builds. + * It would be preferable if the top level CMakeLists.txt provided the library name during config. ?? +:doit +if /i "%1#" == "--libs#" echo -L${LIB_INSTALL_DIR} -llibtag +if /i "%1#" == "--cflags#" echo -I${INCLUDE_INSTALL_DIR}/taglib +if /i "%1#" == "--version#" echo ${TAGLIB_LIB_MAJOR_VERSION}.${TAGLIB_LIB_MINOR_VERSION}.${TAGLIB_LIB_PATCH_VERSION} +if /i "%1#" == "--prefix#" echo ${CMAKE_INSTALL_PREFIX} + +:theend + From 8790e9de0125765ea5f49c7fd4f8fe40da416d7c Mon Sep 17 00:00:00 2001 From: Festus Hagen Date: Fri, 28 Sep 2012 16:32:27 -0400 Subject: [PATCH 28/67] Silence warnings with MinGW 4.6.3. --- taglib/toolkit/taglib.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) mode change 100644 => 100755 taglib/toolkit/taglib.h diff --git a/taglib/toolkit/taglib.h b/taglib/toolkit/taglib.h old mode 100644 new mode 100755 index d41e419e..9ecdd365 --- a/taglib/toolkit/taglib.h +++ b/taglib/toolkit/taglib.h @@ -48,7 +48,9 @@ # include # define TAGLIB_ATOMIC_MAC #elif defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__CYGWIN__) -# define NOMINMAX +# if !defined(NOMINMAX) +# define NOMINMAX +# endif # include # define TAGLIB_ATOMIC_WIN #elif defined (__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__ >= 401) \ From 60a3a4e455ba4f71f50bf691b89b8410a1d56826 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Lalinsk=C3=BD?= Date: Sun, 30 Sep 2012 10:22:10 +0200 Subject: [PATCH 29/67] Various uint fixes We really need to get rid of TagLib::uint... --- taglib/riff/rifffile.cpp | 1 - taglib/riff/wav/wavfile.cpp | 8 ++++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/taglib/riff/rifffile.cpp b/taglib/riff/rifffile.cpp index 22fd3184..a38e1c40 100644 --- a/taglib/riff/rifffile.cpp +++ b/taglib/riff/rifffile.cpp @@ -191,7 +191,6 @@ void RIFF::File::setChunkData(const ByteVector &name, const ByteVector &data, bo // Now add the chunk to the file - long len = length(); writeChunk(name, data, offset, std::max(0, length() - offset), (offset & 1) ? 1 : 0); // And update our internal structure diff --git a/taglib/riff/wav/wavfile.cpp b/taglib/riff/wav/wavfile.cpp index 489adc3b..0ba40665 100644 --- a/taglib/riff/wav/wavfile.cpp +++ b/taglib/riff/wav/wavfile.cpp @@ -199,13 +199,13 @@ void RIFF::WAV::File::strip(TagTypes tags) removeChunk(d->tagChunkID); if(tags & Info){ - uint chunkId = findInfoTagChunk(); - if(chunkId != -1) + TagLib::uint chunkId = findInfoTagChunk(); + if(chunkId != TagLib::uint(-1)) removeChunk(chunkId); } } -uint RIFF::WAV::File::findInfoTagChunk() +TagLib::uint RIFF::WAV::File::findInfoTagChunk() { for(uint i = 0; i < chunkCount(); ++i) { if(chunkName(i) == "LIST" && chunkData(i).mid(0, 4) == "INFO") { @@ -213,5 +213,5 @@ uint RIFF::WAV::File::findInfoTagChunk() } } - return -1; + return TagLib::uint(-1); } From 9f597bab1b3b765bf51df639f467e4a72188dc0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Lalinsk=C3=BD?= Date: Sun, 30 Sep 2012 10:50:19 +0200 Subject: [PATCH 30/67] Change some variables to follow the TagLib naming convention --- taglib/toolkit/tfilestream.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/taglib/toolkit/tfilestream.cpp b/taglib/toolkit/tfilestream.cpp index 928a2d2d..16efb579 100644 --- a/taglib/toolkit/tfilestream.cpp +++ b/taglib/toolkit/tfilestream.cpp @@ -64,18 +64,18 @@ namespace { size_t fread(void *ptr, size_t size, size_t nmemb, HANDLE stream) { - DWORD read_len; - ReadFile(stream, ptr, size * nmemb, &read_len, NULL); + DWORD readLen; + ReadFile(stream, ptr, size * nmemb, &readLen, NULL); - return (read_len / size); + return (readLen / size); } size_t fwrite(const void *ptr, size_t size, size_t nmemb, HANDLE stream) { - DWORD written_len; - WriteFile(stream, ptr, size * nmemb, &written_len, NULL); + DWORD writtenLen; + WriteFile(stream, ptr, size * nmemb, &writtenLen, NULL); - return written_len; + return writtenLen; } #else @@ -444,12 +444,12 @@ void FileStream::truncate(long length) { #ifdef _WIN32 - long current_pos = tell(); + long currentPos = tell(); seek(length); SetEndOfFile(d->file); - seek(current_pos); + seek(currentPos); #else From 2d7414733eaa3263868c74abfa6cff38a8afe8d3 Mon Sep 17 00:00:00 2001 From: Urs Fleisch Date: Sun, 30 Sep 2012 15:42:16 +0200 Subject: [PATCH 31/67] Crash when saving xm files (fixes #68) --- taglib/xm/xmfile.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/taglib/xm/xmfile.cpp b/taglib/xm/xmfile.cpp index 272e5fe0..c311a06e 100644 --- a/taglib/xm/xmfile.cpp +++ b/taglib/xm/xmfile.cpp @@ -443,7 +443,7 @@ bool XM::File::save() return false; uint len = std::min(22UL, instrumentHeaderSize - 4U); - if(i > lines.size()) + if(i >= lines.size()) writeString(String::null, len); else writeString(lines[i], len); From 46e613dcca9a020c8199600cfc7b85c4ce2f256d Mon Sep 17 00:00:00 2001 From: Tsuda Kageyu Date: Thu, 4 Oct 2012 19:23:10 +0900 Subject: [PATCH 32/67] Add reading/writing ID3v1 genre in number --- taglib/mpeg/id3v1/id3v1tag.cpp | 14 ++++++++++++-- taglib/mpeg/id3v1/id3v1tag.h | 23 +++++++++++++++++++---- 2 files changed, 31 insertions(+), 6 deletions(-) diff --git a/taglib/mpeg/id3v1/id3v1tag.cpp b/taglib/mpeg/id3v1/id3v1tag.cpp index e9eb47bf..9fc8cfd7 100644 --- a/taglib/mpeg/id3v1/id3v1tag.cpp +++ b/taglib/mpeg/id3v1/id3v1tag.cpp @@ -182,16 +182,26 @@ void ID3v1::Tag::setGenre(const String &s) d->genre = ID3v1::genreIndex(s); } -void ID3v1::Tag::setYear(uint i) +void ID3v1::Tag::setYear(TagLib::uint i) { d->year = i > 0 ? String::number(i) : String::null; } -void ID3v1::Tag::setTrack(uint i) +void ID3v1::Tag::setTrack(TagLib::uint i) { d->track = i < 256 ? i : 0; } +TagLib::uint ID3v1::Tag::genreNumber() const +{ + return d->genre; +} + +void ID3v1::Tag::setGenreNumber(TagLib::uint i) +{ + d->genre = i < 256 ? i : 255; +} + void ID3v1::Tag::setStringHandler(const StringHandler *handler) { if (handler) diff --git a/taglib/mpeg/id3v1/id3v1tag.h b/taglib/mpeg/id3v1/id3v1tag.h index 9332d4dc..7b26d023 100644 --- a/taglib/mpeg/id3v1/id3v1tag.h +++ b/taglib/mpeg/id3v1/id3v1tag.h @@ -140,16 +140,31 @@ namespace TagLib { virtual String album() const; virtual String comment() const; virtual String genre() const; - virtual uint year() const; - virtual uint track() const; + virtual TagLib::uint year() const; + virtual TagLib::uint track() const; virtual void setTitle(const String &s); virtual void setArtist(const String &s); virtual void setAlbum(const String &s); virtual void setComment(const String &s); virtual void setGenre(const String &s); - virtual void setYear(uint i); - virtual void setTrack(uint i); + virtual void setYear(TagLib::uint i); + virtual void setTrack(TagLib::uint i); + + /*! + * Returns the genre in number. + * + * /note Normally 255 indicates that this tag contains no genre. + */ + TagLib::uint genreNumber() const; + + /*! + * Sets the genre in number to \a i. + * + * /note Valid value is from 0 up to 255. Normally 255 indicates that + * this tag contains no genre. + */ + void setGenreNumber(TagLib::uint i); /*! * Sets the string handler that decides how the ID3v1 data will be From ad9ffc62e6fac5c47f46eb96b39c614e32742eb5 Mon Sep 17 00:00:00 2001 From: Arnaud Bienner Date: Sun, 7 Oct 2012 03:04:02 +0200 Subject: [PATCH 33/67] Consider FLAC file as being invalid if a 0 length block is found --- taglib/flac/flacfile.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/taglib/flac/flacfile.cpp b/taglib/flac/flacfile.cpp index a02770a8..775fed90 100644 --- a/taglib/flac/flacfile.cpp +++ b/taglib/flac/flacfile.cpp @@ -425,7 +425,7 @@ void FLAC::File::scan() length = header.mid(1, 3).toUInt(); ByteVector data = readBlock(length); - if(data.size() != length) { + if(data.size() != length || length == 0) { debug("FLAC::File::scan() -- FLAC stream corrupted"); setValid(false); return; From b7a15092d8471a78f4053ba96dd59938c4e36af5 Mon Sep 17 00:00:00 2001 From: Arnaud Bienner Date: Sun, 7 Oct 2012 03:07:53 +0200 Subject: [PATCH 34/67] Faster FLAC::FilePrivate destructor --- taglib/flac/flacfile.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/taglib/flac/flacfile.cpp b/taglib/flac/flacfile.cpp index 775fed90..c85d9590 100644 --- a/taglib/flac/flacfile.cpp +++ b/taglib/flac/flacfile.cpp @@ -70,7 +70,8 @@ public: ~FilePrivate() { - for(uint i = 0; i < blocks.size(); i++) { + uint size = blocks.size(); + for(uint i = 0; i < size; i++) { delete blocks[i]; } delete properties; From 72f9a96cceba0e22a672760961de3a87e291a602 Mon Sep 17 00:00:00 2001 From: Julien Ramseier Date: Sun, 7 Oct 2012 15:41:40 +0200 Subject: [PATCH 35/67] Fix missing CppUnit include directive --- tests/CMakeLists.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 38ba4d25..f51e7bfa 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -62,6 +62,8 @@ SET(test_runner_SRCS test_mpc.cpp ) +INCLUDE_DIRECTORIES(${CPPUNIT_INCLUDE_DIR}) + ADD_EXECUTABLE(test_runner ${test_runner_SRCS}) TARGET_LINK_LIBRARIES(test_runner tag ${CPPUNIT_LIBRARIES}) From c8994ede3fdc8569a59b3482acbc797186e72d00 Mon Sep 17 00:00:00 2001 From: rsjtdrjgfuzkfg Date: Mon, 8 Oct 2012 19:18:20 +0200 Subject: [PATCH 36/67] Restrict url frame generation to W??? Frames to prevent known non-text non-url frameIDs to be handled as url when containing only one String. No longer describe the default comment as "COMMENT". --- taglib/mpeg/id3v2/id3v2frame.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/taglib/mpeg/id3v2/id3v2frame.cpp b/taglib/mpeg/id3v2/id3v2frame.cpp index 3a5463f5..e6d01bdb 100644 --- a/taglib/mpeg/id3v2/id3v2frame.cpp +++ b/taglib/mpeg/id3v2/id3v2frame.cpp @@ -119,7 +119,7 @@ Frame *Frame::createTextualFrame(const String &key, const StringList &values) // TextIdentificationFrame *frame = new TextIdentificationFrame(frameID, String::UTF8); frame->setText(values); return frame; - } else if(values.size() == 1){ // URL frame (not WXXX); support only one value + } else if((frameID[0] == 'W') && (values.size() == 1)){ // URL frame (not WXXX); support only one value UrlLinkFrame* frame = new UrlLinkFrame(frameID); frame->setUrl(values.front()); return frame; @@ -143,7 +143,9 @@ Frame *Frame::createTextualFrame(const String &key, const StringList &values) // // -COMMENT: depending on the number of values, use COMM or TXXX (with description=COMMENT) if((key == "COMMENT" || key.startsWith(commentPrefix)) && values.size() == 1){ CommentsFrame *frame = new CommentsFrame(String::UTF8); - frame->setDescription(key == "COMMENT" ? key : key.substr(commentPrefix.size())); + if (key != "COMMENT"){ + frame->setDescription(key.substr(commentPrefix.size())); + } frame->setText(values.front()); return frame; } From a9df3e48f7b59bc27e115e5b0ad057a642b6fac3 Mon Sep 17 00:00:00 2001 From: Tsuda Kageyu Date: Wed, 10 Oct 2012 21:17:05 +0900 Subject: [PATCH 37/67] Fix a bug in updating MP4 tags --- taglib/mp4/mp4tag.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/taglib/mp4/mp4tag.cpp b/taglib/mp4/mp4tag.cpp index fc75fb45..f929ef64 100644 --- a/taglib/mp4/mp4tag.cpp +++ b/taglib/mp4/mp4tag.cpp @@ -570,7 +570,7 @@ MP4::Tag::updateOffsets(long delta, long offset) atom->offset += delta; } d->file->seek(atom->offset + 9); - ByteVector data = d->file->readBlock(atom->offset - 9); + ByteVector data = d->file->readBlock(atom->length - 9); unsigned int flags = (ByteVector(1, '\0') + data.mid(0, 3)).toUInt(); if(flags & 1) { long long o = data.mid(7, 8).toLongLong(); From 1e660dda717d1c9680aca6247e48027435a69f6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Lalinsk=C3=BD?= Date: Wed, 10 Oct 2012 17:09:03 +0200 Subject: [PATCH 38/67] Partial changelog for 1.9 --- NEWS | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/NEWS b/NEWS index fa750f69..548e9baf 100644 --- a/NEWS +++ b/NEWS @@ -1,5 +1,17 @@ +TagLib 1.9 (In Development) +========================== + + * Added support for INFO tags in WAV files. + * Changed FileStream to use Windows file API. + * Included taglib-config.cmd script for Windows. + * New ID3v1::Tag methods for working directly with genre numbers. + * New MPEG::File methods for checking which tags are saved in the file. + * Better parsing of corrupted FLAC files. + * Fixed saving of PropertyMap comments without description into ID3v2 tags. + * Fixed crash when parsing certain XM files. + TagLib 1.8 (Sep 6, 2012) -============================== +======================== 1.8: From d15c8453ac75d9070008b9170ae114ea31a4b586 Mon Sep 17 00:00:00 2001 From: gonemad Date: Thu, 11 Oct 2012 21:20:03 -0400 Subject: [PATCH 39/67] Added check if file is open before attempting to read tags --- taglib/ape/apefile.cpp | 6 ++++-- taglib/asf/asffile.cpp | 6 ++++-- taglib/flac/flacfile.cpp | 9 ++++++--- taglib/it/itfile.cpp | 6 ++++-- taglib/mod/modfile.cpp | 6 ++++-- taglib/mp4/mp4file.cpp | 6 ++++-- taglib/mpc/mpcfile.cpp | 6 ++++-- taglib/ogg/flac/oggflacfile.cpp | 6 ++++-- taglib/ogg/speex/speexfile.cpp | 6 ++++-- taglib/ogg/vorbis/vorbisfile.cpp | 6 ++++-- taglib/s3m/s3mfile.cpp | 6 ++++-- taglib/wavpack/wavpackfile.cpp | 6 ++++-- taglib/xm/xmfile.cpp | 6 ++++-- 13 files changed, 54 insertions(+), 27 deletions(-) diff --git a/taglib/ape/apefile.cpp b/taglib/ape/apefile.cpp index cb652242..bf6491fc 100644 --- a/taglib/ape/apefile.cpp +++ b/taglib/ape/apefile.cpp @@ -90,14 +90,16 @@ APE::File::File(FileName file, bool readProperties, Properties::ReadStyle propertiesStyle) : TagLib::File(file) { d = new FilePrivate; - read(readProperties, propertiesStyle); + if(isOpen()) + read(readProperties, propertiesStyle); } APE::File::File(IOStream *stream, bool readProperties, Properties::ReadStyle propertiesStyle) : TagLib::File(stream) { d = new FilePrivate; - read(readProperties, propertiesStyle); + if(isOpen()) + read(readProperties, propertiesStyle); } APE::File::~File() diff --git a/taglib/asf/asffile.cpp b/taglib/asf/asffile.cpp index 455631f8..6a3155a0 100644 --- a/taglib/asf/asffile.cpp +++ b/taglib/asf/asffile.cpp @@ -372,14 +372,16 @@ ASF::File::File(FileName file, bool readProperties, Properties::ReadStyle proper : TagLib::File(file) { d = new FilePrivate; - read(readProperties, propertiesStyle); + if(isOpen()) + read(readProperties, propertiesStyle); } ASF::File::File(IOStream *stream, bool readProperties, Properties::ReadStyle propertiesStyle) : TagLib::File(stream) { d = new FilePrivate; - read(readProperties, propertiesStyle); + if(isOpen()) + read(readProperties, propertiesStyle); } ASF::File::~File() diff --git a/taglib/flac/flacfile.cpp b/taglib/flac/flacfile.cpp index c85d9590..291c42d0 100644 --- a/taglib/flac/flacfile.cpp +++ b/taglib/flac/flacfile.cpp @@ -109,7 +109,8 @@ FLAC::File::File(FileName file, bool readProperties, TagLib::File(file) { d = new FilePrivate; - read(readProperties, propertiesStyle); + if(isOpen()) + read(readProperties, propertiesStyle); } FLAC::File::File(FileName file, ID3v2::FrameFactory *frameFactory, @@ -118,7 +119,8 @@ FLAC::File::File(FileName file, ID3v2::FrameFactory *frameFactory, { d = new FilePrivate; d->ID3v2FrameFactory = frameFactory; - read(readProperties, propertiesStyle); + if(isOpen()) + read(readProperties, propertiesStyle); } FLAC::File::File(IOStream *stream, ID3v2::FrameFactory *frameFactory, @@ -127,7 +129,8 @@ FLAC::File::File(IOStream *stream, ID3v2::FrameFactory *frameFactory, { d = new FilePrivate; d->ID3v2FrameFactory = frameFactory; - read(readProperties, propertiesStyle); + if(isOpen()) + read(readProperties, propertiesStyle); } FLAC::File::~File() diff --git a/taglib/it/itfile.cpp b/taglib/it/itfile.cpp index 4e049518..4807b9a2 100644 --- a/taglib/it/itfile.cpp +++ b/taglib/it/itfile.cpp @@ -45,7 +45,8 @@ IT::File::File(FileName file, bool readProperties, Mod::FileBase(file), d(new FilePrivate(propertiesStyle)) { - read(readProperties); + if(isOpen()) + read(readProperties); } IT::File::File(IOStream *stream, bool readProperties, @@ -53,7 +54,8 @@ IT::File::File(IOStream *stream, bool readProperties, Mod::FileBase(stream), d(new FilePrivate(propertiesStyle)) { - read(readProperties); + if(isOpen()) + read(readProperties); } IT::File::~File() diff --git a/taglib/mod/modfile.cpp b/taglib/mod/modfile.cpp index 25fc8715..8700ca74 100644 --- a/taglib/mod/modfile.cpp +++ b/taglib/mod/modfile.cpp @@ -45,7 +45,8 @@ Mod::File::File(FileName file, bool readProperties, Mod::FileBase(file), d(new FilePrivate(propertiesStyle)) { - read(readProperties); + if(isOpen()) + read(readProperties); } Mod::File::File(IOStream *stream, bool readProperties, @@ -53,7 +54,8 @@ Mod::File::File(IOStream *stream, bool readProperties, Mod::FileBase(stream), d(new FilePrivate(propertiesStyle)) { - read(readProperties); + if(isOpen()) + read(readProperties); } Mod::File::~File() diff --git a/taglib/mp4/mp4file.cpp b/taglib/mp4/mp4file.cpp index 02185575..6f7e48ee 100644 --- a/taglib/mp4/mp4file.cpp +++ b/taglib/mp4/mp4file.cpp @@ -67,14 +67,16 @@ MP4::File::File(FileName file, bool readProperties, AudioProperties::ReadStyle a : TagLib::File(file) { d = new FilePrivate; - read(readProperties, audioPropertiesStyle); + if(isOpen()) + read(readProperties, audioPropertiesStyle); } MP4::File::File(IOStream *stream, bool readProperties, AudioProperties::ReadStyle audioPropertiesStyle) : TagLib::File(stream) { d = new FilePrivate; - read(readProperties, audioPropertiesStyle); + if(isOpen()) + read(readProperties, audioPropertiesStyle); } MP4::File::~File() diff --git a/taglib/mpc/mpcfile.cpp b/taglib/mpc/mpcfile.cpp index 519a0467..7734eae0 100644 --- a/taglib/mpc/mpcfile.cpp +++ b/taglib/mpc/mpcfile.cpp @@ -94,14 +94,16 @@ MPC::File::File(FileName file, bool readProperties, Properties::ReadStyle propertiesStyle) : TagLib::File(file) { d = new FilePrivate; - read(readProperties, propertiesStyle); + if(isOpen()) + read(readProperties, propertiesStyle); } MPC::File::File(IOStream *stream, bool readProperties, Properties::ReadStyle propertiesStyle) : TagLib::File(stream) { d = new FilePrivate; - read(readProperties, propertiesStyle); + if(isOpen()) + read(readProperties, propertiesStyle); } MPC::File::~File() diff --git a/taglib/ogg/flac/oggflacfile.cpp b/taglib/ogg/flac/oggflacfile.cpp index 9d9c303d..e523f276 100644 --- a/taglib/ogg/flac/oggflacfile.cpp +++ b/taglib/ogg/flac/oggflacfile.cpp @@ -72,14 +72,16 @@ Ogg::FLAC::File::File(FileName file, bool readProperties, Properties::ReadStyle propertiesStyle) : Ogg::File(file) { d = new FilePrivate; - read(readProperties, propertiesStyle); + if(isOpen()) + read(readProperties, propertiesStyle); } Ogg::FLAC::File::File(IOStream *stream, bool readProperties, Properties::ReadStyle propertiesStyle) : Ogg::File(stream) { d = new FilePrivate; - read(readProperties, propertiesStyle); + if(isOpen()) + read(readProperties, propertiesStyle); } Ogg::FLAC::File::~File() diff --git a/taglib/ogg/speex/speexfile.cpp b/taglib/ogg/speex/speexfile.cpp index 3a4940a2..58f67566 100644 --- a/taglib/ogg/speex/speexfile.cpp +++ b/taglib/ogg/speex/speexfile.cpp @@ -62,14 +62,16 @@ Speex::File::File(FileName file, bool readProperties, Properties::ReadStyle propertiesStyle) : Ogg::File(file) { d = new FilePrivate; - read(readProperties, propertiesStyle); + if(isOpen()) + read(readProperties, propertiesStyle); } Speex::File::File(IOStream *stream, bool readProperties, Properties::ReadStyle propertiesStyle) : Ogg::File(stream) { d = new FilePrivate; - read(readProperties, propertiesStyle); + if(isOpen()) + read(readProperties, propertiesStyle); } Speex::File::~File() diff --git a/taglib/ogg/vorbis/vorbisfile.cpp b/taglib/ogg/vorbis/vorbisfile.cpp index e2eed9e2..a6753f5f 100644 --- a/taglib/ogg/vorbis/vorbisfile.cpp +++ b/taglib/ogg/vorbis/vorbisfile.cpp @@ -67,14 +67,16 @@ Vorbis::File::File(FileName file, bool readProperties, Properties::ReadStyle propertiesStyle) : Ogg::File(file) { d = new FilePrivate; - read(readProperties, propertiesStyle); + if(isOpen()) + read(readProperties, propertiesStyle); } Vorbis::File::File(IOStream *stream, bool readProperties, Properties::ReadStyle propertiesStyle) : Ogg::File(stream) { d = new FilePrivate; - read(readProperties, propertiesStyle); + if(isOpen()) + read(readProperties, propertiesStyle); } Vorbis::File::~File() diff --git a/taglib/s3m/s3mfile.cpp b/taglib/s3m/s3mfile.cpp index 7ffdf910..1afe3628 100644 --- a/taglib/s3m/s3mfile.cpp +++ b/taglib/s3m/s3mfile.cpp @@ -47,7 +47,8 @@ S3M::File::File(FileName file, bool readProperties, Mod::FileBase(file), d(new FilePrivate(propertiesStyle)) { - read(readProperties); + if(isOpen()) + read(readProperties); } S3M::File::File(IOStream *stream, bool readProperties, @@ -55,7 +56,8 @@ S3M::File::File(IOStream *stream, bool readProperties, Mod::FileBase(stream), d(new FilePrivate(propertiesStyle)) { - read(readProperties); + if(isOpen()) + read(readProperties); } S3M::File::~File() diff --git a/taglib/wavpack/wavpackfile.cpp b/taglib/wavpack/wavpackfile.cpp index 49f7923e..ea5a8f85 100644 --- a/taglib/wavpack/wavpackfile.cpp +++ b/taglib/wavpack/wavpackfile.cpp @@ -86,14 +86,16 @@ WavPack::File::File(FileName file, bool readProperties, Properties::ReadStyle propertiesStyle) : TagLib::File(file) { d = new FilePrivate; - read(readProperties, propertiesStyle); + if(isOpen()) + read(readProperties, propertiesStyle); } WavPack::File::File(IOStream *stream, bool readProperties, Properties::ReadStyle propertiesStyle) : TagLib::File(stream) { d = new FilePrivate; - read(readProperties, propertiesStyle); + if(isOpen()) + read(readProperties, propertiesStyle); } WavPack::File::~File() diff --git a/taglib/xm/xmfile.cpp b/taglib/xm/xmfile.cpp index c311a06e..a0890c88 100644 --- a/taglib/xm/xmfile.cpp +++ b/taglib/xm/xmfile.cpp @@ -359,7 +359,8 @@ XM::File::File(FileName file, bool readProperties, Mod::FileBase(file), d(new FilePrivate(propertiesStyle)) { - read(readProperties); + if(isOpen()) + read(readProperties); } XM::File::File(IOStream *stream, bool readProperties, @@ -367,7 +368,8 @@ XM::File::File(IOStream *stream, bool readProperties, Mod::FileBase(stream), d(new FilePrivate(propertiesStyle)) { - read(readProperties); + if(isOpen()) + read(readProperties); } XM::File::~File() From 6b9ef6421fb145db9a902c25747bde9217ea2741 Mon Sep 17 00:00:00 2001 From: gonemad Date: Thu, 11 Oct 2012 22:10:19 -0400 Subject: [PATCH 40/67] Fixed indents --- taglib/ape/apefile.cpp | 4 ++-- taglib/asf/asffile.cpp | 4 ++-- taglib/flac/flacfile.cpp | 6 +++--- taglib/it/itfile.cpp | 8 ++++---- taglib/mod/modfile.cpp | 8 ++++---- taglib/mp4/mp4file.cpp | 4 ++-- taglib/mpc/mpcfile.cpp | 4 ++-- taglib/ogg/flac/oggflacfile.cpp | 4 ++-- taglib/ogg/speex/speexfile.cpp | 4 ++-- taglib/ogg/vorbis/vorbisfile.cpp | 4 ++-- taglib/s3m/s3mfile.cpp | 8 ++++---- taglib/wavpack/wavpackfile.cpp | 4 ++-- taglib/xm/xmfile.cpp | 8 ++++---- 13 files changed, 35 insertions(+), 35 deletions(-) diff --git a/taglib/ape/apefile.cpp b/taglib/ape/apefile.cpp index bf6491fc..2572a2e9 100644 --- a/taglib/ape/apefile.cpp +++ b/taglib/ape/apefile.cpp @@ -91,7 +91,7 @@ APE::File::File(FileName file, bool readProperties, { d = new FilePrivate; if(isOpen()) - read(readProperties, propertiesStyle); + read(readProperties, propertiesStyle); } APE::File::File(IOStream *stream, bool readProperties, @@ -99,7 +99,7 @@ APE::File::File(IOStream *stream, bool readProperties, { d = new FilePrivate; if(isOpen()) - read(readProperties, propertiesStyle); + read(readProperties, propertiesStyle); } APE::File::~File() diff --git a/taglib/asf/asffile.cpp b/taglib/asf/asffile.cpp index 6a3155a0..96b8706f 100644 --- a/taglib/asf/asffile.cpp +++ b/taglib/asf/asffile.cpp @@ -373,7 +373,7 @@ ASF::File::File(FileName file, bool readProperties, Properties::ReadStyle proper { d = new FilePrivate; if(isOpen()) - read(readProperties, propertiesStyle); + read(readProperties, propertiesStyle); } ASF::File::File(IOStream *stream, bool readProperties, Properties::ReadStyle propertiesStyle) @@ -381,7 +381,7 @@ ASF::File::File(IOStream *stream, bool readProperties, Properties::ReadStyle pro { d = new FilePrivate; if(isOpen()) - read(readProperties, propertiesStyle); + read(readProperties, propertiesStyle); } ASF::File::~File() diff --git a/taglib/flac/flacfile.cpp b/taglib/flac/flacfile.cpp index 291c42d0..0bfe84c3 100644 --- a/taglib/flac/flacfile.cpp +++ b/taglib/flac/flacfile.cpp @@ -110,7 +110,7 @@ FLAC::File::File(FileName file, bool readProperties, { d = new FilePrivate; if(isOpen()) - read(readProperties, propertiesStyle); + read(readProperties, propertiesStyle); } FLAC::File::File(FileName file, ID3v2::FrameFactory *frameFactory, @@ -120,7 +120,7 @@ FLAC::File::File(FileName file, ID3v2::FrameFactory *frameFactory, d = new FilePrivate; d->ID3v2FrameFactory = frameFactory; if(isOpen()) - read(readProperties, propertiesStyle); + read(readProperties, propertiesStyle); } FLAC::File::File(IOStream *stream, ID3v2::FrameFactory *frameFactory, @@ -130,7 +130,7 @@ FLAC::File::File(IOStream *stream, ID3v2::FrameFactory *frameFactory, d = new FilePrivate; d->ID3v2FrameFactory = frameFactory; if(isOpen()) - read(readProperties, propertiesStyle); + read(readProperties, propertiesStyle); } FLAC::File::~File() diff --git a/taglib/it/itfile.cpp b/taglib/it/itfile.cpp index 4807b9a2..ad5cf0b8 100644 --- a/taglib/it/itfile.cpp +++ b/taglib/it/itfile.cpp @@ -45,8 +45,8 @@ IT::File::File(FileName file, bool readProperties, Mod::FileBase(file), d(new FilePrivate(propertiesStyle)) { - if(isOpen()) - read(readProperties); + if(isOpen()) + read(readProperties); } IT::File::File(IOStream *stream, bool readProperties, @@ -54,8 +54,8 @@ IT::File::File(IOStream *stream, bool readProperties, Mod::FileBase(stream), d(new FilePrivate(propertiesStyle)) { - if(isOpen()) - read(readProperties); + if(isOpen()) + read(readProperties); } IT::File::~File() diff --git a/taglib/mod/modfile.cpp b/taglib/mod/modfile.cpp index 8700ca74..ce974c16 100644 --- a/taglib/mod/modfile.cpp +++ b/taglib/mod/modfile.cpp @@ -45,8 +45,8 @@ Mod::File::File(FileName file, bool readProperties, Mod::FileBase(file), d(new FilePrivate(propertiesStyle)) { - if(isOpen()) - read(readProperties); + if(isOpen()) + read(readProperties); } Mod::File::File(IOStream *stream, bool readProperties, @@ -54,8 +54,8 @@ Mod::File::File(IOStream *stream, bool readProperties, Mod::FileBase(stream), d(new FilePrivate(propertiesStyle)) { - if(isOpen()) - read(readProperties); + if(isOpen()) + read(readProperties); } Mod::File::~File() diff --git a/taglib/mp4/mp4file.cpp b/taglib/mp4/mp4file.cpp index 6f7e48ee..a9543211 100644 --- a/taglib/mp4/mp4file.cpp +++ b/taglib/mp4/mp4file.cpp @@ -68,7 +68,7 @@ MP4::File::File(FileName file, bool readProperties, AudioProperties::ReadStyle a { d = new FilePrivate; if(isOpen()) - read(readProperties, audioPropertiesStyle); + read(readProperties, audioPropertiesStyle); } MP4::File::File(IOStream *stream, bool readProperties, AudioProperties::ReadStyle audioPropertiesStyle) @@ -76,7 +76,7 @@ MP4::File::File(IOStream *stream, bool readProperties, AudioProperties::ReadStyl { d = new FilePrivate; if(isOpen()) - read(readProperties, audioPropertiesStyle); + read(readProperties, audioPropertiesStyle); } MP4::File::~File() diff --git a/taglib/mpc/mpcfile.cpp b/taglib/mpc/mpcfile.cpp index 7734eae0..18f533f8 100644 --- a/taglib/mpc/mpcfile.cpp +++ b/taglib/mpc/mpcfile.cpp @@ -95,7 +95,7 @@ MPC::File::File(FileName file, bool readProperties, { d = new FilePrivate; if(isOpen()) - read(readProperties, propertiesStyle); + read(readProperties, propertiesStyle); } MPC::File::File(IOStream *stream, bool readProperties, @@ -103,7 +103,7 @@ MPC::File::File(IOStream *stream, bool readProperties, { d = new FilePrivate; if(isOpen()) - read(readProperties, propertiesStyle); + read(readProperties, propertiesStyle); } MPC::File::~File() diff --git a/taglib/ogg/flac/oggflacfile.cpp b/taglib/ogg/flac/oggflacfile.cpp index e523f276..b73c5f57 100644 --- a/taglib/ogg/flac/oggflacfile.cpp +++ b/taglib/ogg/flac/oggflacfile.cpp @@ -73,7 +73,7 @@ Ogg::FLAC::File::File(FileName file, bool readProperties, { d = new FilePrivate; if(isOpen()) - read(readProperties, propertiesStyle); + read(readProperties, propertiesStyle); } Ogg::FLAC::File::File(IOStream *stream, bool readProperties, @@ -81,7 +81,7 @@ Ogg::FLAC::File::File(IOStream *stream, bool readProperties, { d = new FilePrivate; if(isOpen()) - read(readProperties, propertiesStyle); + read(readProperties, propertiesStyle); } Ogg::FLAC::File::~File() diff --git a/taglib/ogg/speex/speexfile.cpp b/taglib/ogg/speex/speexfile.cpp index 58f67566..8ac86e69 100644 --- a/taglib/ogg/speex/speexfile.cpp +++ b/taglib/ogg/speex/speexfile.cpp @@ -63,7 +63,7 @@ Speex::File::File(FileName file, bool readProperties, { d = new FilePrivate; if(isOpen()) - read(readProperties, propertiesStyle); + read(readProperties, propertiesStyle); } Speex::File::File(IOStream *stream, bool readProperties, @@ -71,7 +71,7 @@ Speex::File::File(IOStream *stream, bool readProperties, { d = new FilePrivate; if(isOpen()) - read(readProperties, propertiesStyle); + read(readProperties, propertiesStyle); } Speex::File::~File() diff --git a/taglib/ogg/vorbis/vorbisfile.cpp b/taglib/ogg/vorbis/vorbisfile.cpp index a6753f5f..82984536 100644 --- a/taglib/ogg/vorbis/vorbisfile.cpp +++ b/taglib/ogg/vorbis/vorbisfile.cpp @@ -68,7 +68,7 @@ Vorbis::File::File(FileName file, bool readProperties, { d = new FilePrivate; if(isOpen()) - read(readProperties, propertiesStyle); + read(readProperties, propertiesStyle); } Vorbis::File::File(IOStream *stream, bool readProperties, @@ -76,7 +76,7 @@ Vorbis::File::File(IOStream *stream, bool readProperties, { d = new FilePrivate; if(isOpen()) - read(readProperties, propertiesStyle); + read(readProperties, propertiesStyle); } Vorbis::File::~File() diff --git a/taglib/s3m/s3mfile.cpp b/taglib/s3m/s3mfile.cpp index 1afe3628..371340a5 100644 --- a/taglib/s3m/s3mfile.cpp +++ b/taglib/s3m/s3mfile.cpp @@ -47,8 +47,8 @@ S3M::File::File(FileName file, bool readProperties, Mod::FileBase(file), d(new FilePrivate(propertiesStyle)) { - if(isOpen()) - read(readProperties); + if(isOpen()) + read(readProperties); } S3M::File::File(IOStream *stream, bool readProperties, @@ -56,8 +56,8 @@ S3M::File::File(IOStream *stream, bool readProperties, Mod::FileBase(stream), d(new FilePrivate(propertiesStyle)) { - if(isOpen()) - read(readProperties); + if(isOpen()) + read(readProperties); } S3M::File::~File() diff --git a/taglib/wavpack/wavpackfile.cpp b/taglib/wavpack/wavpackfile.cpp index ea5a8f85..2d1f8cd9 100644 --- a/taglib/wavpack/wavpackfile.cpp +++ b/taglib/wavpack/wavpackfile.cpp @@ -87,7 +87,7 @@ WavPack::File::File(FileName file, bool readProperties, { d = new FilePrivate; if(isOpen()) - read(readProperties, propertiesStyle); + read(readProperties, propertiesStyle); } WavPack::File::File(IOStream *stream, bool readProperties, @@ -95,7 +95,7 @@ WavPack::File::File(IOStream *stream, bool readProperties, { d = new FilePrivate; if(isOpen()) - read(readProperties, propertiesStyle); + read(readProperties, propertiesStyle); } WavPack::File::~File() diff --git a/taglib/xm/xmfile.cpp b/taglib/xm/xmfile.cpp index a0890c88..d8373a78 100644 --- a/taglib/xm/xmfile.cpp +++ b/taglib/xm/xmfile.cpp @@ -359,8 +359,8 @@ XM::File::File(FileName file, bool readProperties, Mod::FileBase(file), d(new FilePrivate(propertiesStyle)) { - if(isOpen()) - read(readProperties); + if(isOpen()) + read(readProperties); } XM::File::File(IOStream *stream, bool readProperties, @@ -368,8 +368,8 @@ XM::File::File(IOStream *stream, bool readProperties, Mod::FileBase(stream), d(new FilePrivate(propertiesStyle)) { - if(isOpen()) - read(readProperties); + if(isOpen()) + read(readProperties); } XM::File::~File() From 5e7b1da632247be528e1a1b95590480c8f15d1d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Lalinsk=C3=BD?= Date: Sat, 13 Oct 2012 08:55:23 +0200 Subject: [PATCH 41/67] Add support for Ogg Opus --- NEWS | 1 + taglib/CMakeLists.txt | 10 +- taglib/fileref.cpp | 3 + taglib/ogg/opus/opusfile.cpp | 128 ++++++++++++++ taglib/ogg/opus/opusfile.h | 110 ++++++++++++ taglib/ogg/opus/opusproperties.cpp | 161 ++++++++++++++++++ taglib/ogg/opus/opusproperties.h | 96 +++++++++++ taglib/toolkit/tfile.cpp | 7 + tests/CMakeLists.txt | 3 + .../data/correctness_gain_silent_output.opus | Bin 0 -> 35506 bytes tests/test_opus.cpp | 61 +++++++ 11 files changed, 579 insertions(+), 1 deletion(-) create mode 100644 taglib/ogg/opus/opusfile.cpp create mode 100644 taglib/ogg/opus/opusfile.h create mode 100644 taglib/ogg/opus/opusproperties.cpp create mode 100644 taglib/ogg/opus/opusproperties.h create mode 100644 tests/data/correctness_gain_silent_output.opus create mode 100644 tests/test_opus.cpp diff --git a/NEWS b/NEWS index 548e9baf..9432865a 100644 --- a/NEWS +++ b/NEWS @@ -1,6 +1,7 @@ TagLib 1.9 (In Development) ========================== + * Added support for the Ogg Opus file format. * Added support for INFO tags in WAV files. * Changed FileStream to use Windows file API. * Included taglib-config.cmd script for Windows. diff --git a/taglib/CMakeLists.txt b/taglib/CMakeLists.txt index ebb9165d..72712ca2 100644 --- a/taglib/CMakeLists.txt +++ b/taglib/CMakeLists.txt @@ -10,6 +10,7 @@ include_directories( ${CMAKE_CURRENT_SOURCE_DIR}/mp4 ${CMAKE_CURRENT_SOURCE_DIR}/ogg/vorbis ${CMAKE_CURRENT_SOURCE_DIR}/ogg/speex + ${CMAKE_CURRENT_SOURCE_DIR}/ogg/opus ${CMAKE_CURRENT_SOURCE_DIR}/mpeg/id3v2 ${CMAKE_CURRENT_SOURCE_DIR}/mpeg/id3v2/frames ${CMAKE_CURRENT_SOURCE_DIR}/mpeg/id3v1 @@ -83,6 +84,8 @@ set(tag_HDRS ogg/flac/oggflacfile.h ogg/speex/speexfile.h ogg/speex/speexproperties.h + ogg/opus/opusfile.h + ogg/opus/opusproperties.h flac/flacfile.h flac/flacpicture.h flac/flacproperties.h @@ -220,6 +223,11 @@ set(speex_SRCS ogg/speex/speexproperties.cpp ) +set(opus_SRCS + ogg/opus/opusfile.cpp + ogg/opus/opusproperties.cpp +) + set(trueaudio_SRCS trueaudio/trueaudiofile.cpp trueaudio/trueaudioproperties.cpp @@ -288,7 +296,7 @@ set(tag_LIB_SRCS ${mpeg_SRCS} ${id3v1_SRCS} ${id3v2_SRCS} ${frames_SRCS} ${ogg_SRCS} ${vorbis_SRCS} ${oggflacs_SRCS} ${mpc_SRCS} ${ape_SRCS} ${toolkit_SRCS} ${flacs_SRCS} ${wavpack_SRCS} ${speex_SRCS} ${trueaudio_SRCS} ${riff_SRCS} ${aiff_SRCS} ${wav_SRCS} - ${asf_SRCS} ${mp4_SRCS} ${mod_SRCS} ${s3m_SRCS} ${it_SRCS} ${xm_SRCS} + ${asf_SRCS} ${mp4_SRCS} ${mod_SRCS} ${s3m_SRCS} ${it_SRCS} ${xm_SRCS} ${opus_SRCS} tag.cpp tagunion.cpp fileref.cpp diff --git a/taglib/fileref.cpp b/taglib/fileref.cpp index 5c42ed4c..859f3155 100644 --- a/taglib/fileref.cpp +++ b/taglib/fileref.cpp @@ -45,6 +45,7 @@ #include "mp4file.h" #include "wavpackfile.h" #include "speexfile.h" +#include "opusfile.h" #include "trueaudiofile.h" #include "aifffile.h" #include "wavfile.h" @@ -252,6 +253,8 @@ File *FileRef::create(FileName fileName, bool readAudioProperties, return new WavPack::File(fileName, readAudioProperties, audioPropertiesStyle); if(ext == "SPX") return new Ogg::Speex::File(fileName, readAudioProperties, audioPropertiesStyle); + if(ext == "OPUS") + return new Ogg::Opus::File(fileName, readAudioProperties, audioPropertiesStyle); if(ext == "TTA") return new TrueAudio::File(fileName, readAudioProperties, audioPropertiesStyle); if(ext == "M4A" || ext == "M4R" || ext == "M4B" || ext == "M4P" || ext == "MP4" || ext == "3G2") diff --git a/taglib/ogg/opus/opusfile.cpp b/taglib/ogg/opus/opusfile.cpp new file mode 100644 index 00000000..836f89c9 --- /dev/null +++ b/taglib/ogg/opus/opusfile.cpp @@ -0,0 +1,128 @@ +/*************************************************************************** + copyright : (C) 2012 by Lukáš Lalinský + email : lalinsky@gmail.com + + copyright : (C) 2002 - 2008 by Scott Wheeler + email : wheeler@kde.org + (original Vorbis implementation) + ***************************************************************************/ + +/*************************************************************************** + * This library is free software; you can redistribute it and/or modify * + * it under the terms of the GNU Lesser General Public License version * + * 2.1 as published by the Free Software Foundation. * + * * + * This library is distributed in the hope that it will be useful, but * + * WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this library; if not, write to the Free Software * + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * + * 02110-1301 USA * + * * + * Alternatively, this file is available under the Mozilla Public * + * License Version 1.1. You may obtain a copy of the License at * + * http://www.mozilla.org/MPL/ * + ***************************************************************************/ + +#include + +#include +#include + +#include "opusfile.h" + +using namespace TagLib; +using namespace TagLib::Ogg; + +class Opus::File::FilePrivate +{ +public: + FilePrivate() : + comment(0), + properties(0) {} + + ~FilePrivate() + { + delete comment; + delete properties; + } + + Ogg::XiphComment *comment; + Properties *properties; +}; + +//////////////////////////////////////////////////////////////////////////////// +// public members +//////////////////////////////////////////////////////////////////////////////// + +Opus::File::File(FileName file, bool readProperties, + Properties::ReadStyle propertiesStyle) : Ogg::File(file) +{ + d = new FilePrivate; + read(readProperties, propertiesStyle); +} + +Opus::File::File(IOStream *stream, bool readProperties, + Properties::ReadStyle propertiesStyle) : Ogg::File(stream) +{ + d = new FilePrivate; + read(readProperties, propertiesStyle); +} + +Opus::File::~File() +{ + delete d; +} + +Ogg::XiphComment *Opus::File::tag() const +{ + return d->comment; +} + +Opus::Properties *Opus::File::audioProperties() const +{ + return d->properties; +} + +bool Opus::File::save() +{ + if(!d->comment) + d->comment = new Ogg::XiphComment; + + setPacket(1, ByteVector("OpusTags", 8) + d->comment->render(false)); + + return Ogg::File::save(); +} + +//////////////////////////////////////////////////////////////////////////////// +// private members +//////////////////////////////////////////////////////////////////////////////// + +void Opus::File::read(bool readProperties, Properties::ReadStyle propertiesStyle) +{ + ByteVector opusHeaderData = packet(0); + + if(!opusHeaderData.startsWith("OpusHead")) { + setValid(false); + debug("Opus::File::read() -- invalid Opus identification header"); + return; + } + + ByteVector commentHeaderData = packet(1); + + if(!commentHeaderData.startsWith("OpusTags")) { + setValid(false); + debug("Opus::File::read() -- invalid Opus tags header"); + return; + } + + debug("starts with OpusTags"); + + d->comment = new Ogg::XiphComment(commentHeaderData.mid(8)); + + if(readProperties) + d->properties = new Properties(this, propertiesStyle); +} diff --git a/taglib/ogg/opus/opusfile.h b/taglib/ogg/opus/opusfile.h new file mode 100644 index 00000000..73375af8 --- /dev/null +++ b/taglib/ogg/opus/opusfile.h @@ -0,0 +1,110 @@ +/*************************************************************************** + copyright : (C) 2012 by Lukáš Lalinský + email : lalinsky@gmail.com + + copyright : (C) 2002 - 2008 by Scott Wheeler + email : wheeler@kde.org + (original Vorbis implementation) +***************************************************************************/ + +/*************************************************************************** + * This library is free software; you can redistribute it and/or modify * + * it under the terms of the GNU Lesser General Public License version * + * 2.1 as published by the Free Software Foundation. * + * * + * This library is distributed in the hope that it will be useful, but * + * WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this library; if not, write to the Free Software * + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * + * 02110-1301 USA * + * * + * Alternatively, this file is available under the Mozilla Public * + * License Version 1.1. You may obtain a copy of the License at * + * http://www.mozilla.org/MPL/ * + ***************************************************************************/ + +#ifndef TAGLIB_OPUSFILE_H +#define TAGLIB_OPUSFILE_H + +#include "oggfile.h" +#include "xiphcomment.h" + +#include "opusproperties.h" + +namespace TagLib { + + namespace Ogg { + + //! A namespace containing classes for Opus metadata + + namespace Opus { + + //! An implementation of Ogg::File with Opus specific methods + + /*! + * This is the central class in the Ogg Opus metadata processing collection + * of classes. It's built upon Ogg::File which handles processing of the Ogg + * logical bitstream and breaking it down into pages which are handled by + * the codec implementations, in this case Opus specifically. + */ + + class TAGLIB_EXPORT File : public Ogg::File + { + public: + /*! + * Contructs a Opus file from \a file. If \a readProperties is true the + * file's audio properties will also be read using \a propertiesStyle. If + * false, \a propertiesStyle is ignored. + */ + File(FileName file, bool readProperties = true, + Properties::ReadStyle propertiesStyle = Properties::Average); + + /*! + * Contructs a Opus file from \a file. If \a readProperties is true the + * file's audio properties will also be read using \a propertiesStyle. If + * false, \a propertiesStyle is ignored. + * + * \note TagLib will *not* take ownership of the stream, the caller is + * responsible for deleting it after the File object. + */ + File(IOStream *stream, bool readProperties = true, + Properties::ReadStyle propertiesStyle = Properties::Average); + + /*! + * Destroys this instance of the File. + */ + virtual ~File(); + + /*! + * Returns the XiphComment for this file. XiphComment implements the tag + * interface, so this serves as the reimplementation of + * TagLib::File::tag(). + */ + virtual Ogg::XiphComment *tag() const; + + /*! + * Returns the Opus::Properties for this file. If no audio properties + * were read then this will return a null pointer. + */ + virtual Properties *audioProperties() const; + + virtual bool save(); + + private: + File(const File &); + File &operator=(const File &); + + void read(bool readProperties, Properties::ReadStyle propertiesStyle); + + class FilePrivate; + FilePrivate *d; + }; + } + } +} + +#endif diff --git a/taglib/ogg/opus/opusproperties.cpp b/taglib/ogg/opus/opusproperties.cpp new file mode 100644 index 00000000..70679d4c --- /dev/null +++ b/taglib/ogg/opus/opusproperties.cpp @@ -0,0 +1,161 @@ +/*************************************************************************** + copyright : (C) 2012 by Lukáš Lalinský + email : lalinsky@gmail.com + + copyright : (C) 2002 - 2008 by Scott Wheeler + email : wheeler@kde.org + (original Vorbis implementation) + ***************************************************************************/ + +/*************************************************************************** + * This library is free software; you can redistribute it and/or modify * + * it under the terms of the GNU Lesser General Public License version * + * 2.1 as published by the Free Software Foundation. * + * * + * This library is distributed in the hope that it will be useful, but * + * WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this library; if not, write to the Free Software * + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * + * 02110-1301 USA * + * * + * Alternatively, this file is available under the Mozilla Public * + * License Version 1.1. You may obtain a copy of the License at * + * http://www.mozilla.org/MPL/ * + ***************************************************************************/ + +#include +#include + +#include + +#include "opusproperties.h" +#include "opusfile.h" + +using namespace TagLib; +using namespace TagLib::Ogg; + +class Opus::Properties::PropertiesPrivate +{ +public: + PropertiesPrivate(File *f, ReadStyle s) : + file(f), + style(s), + length(0), + inputSampleRate(0), + channels(0), + opusVersion(0) {} + + File *file; + ReadStyle style; + int length; + int inputSampleRate; + int channels; + int opusVersion; +}; + +//////////////////////////////////////////////////////////////////////////////// +// public members +//////////////////////////////////////////////////////////////////////////////// + +Opus::Properties::Properties(File *file, ReadStyle style) : AudioProperties(style) +{ + d = new PropertiesPrivate(file, style); + read(); +} + +Opus::Properties::~Properties() +{ + delete d; +} + +int Opus::Properties::length() const +{ + return d->length; +} + +int Opus::Properties::bitrate() const +{ + return 0; +} + +int Opus::Properties::sampleRate() const +{ + // Opus can decode any stream at a sample rate of 8, 12, 16, 24, or 48 kHz, + // so there is no single sample rate. Let's assume it's the highest + // possible. + return 48000; +} + +int Opus::Properties::channels() const +{ + return d->channels; +} + +int Opus::Properties::inputSampleRate() const +{ + return d->inputSampleRate; +} + +int Opus::Properties::opusVersion() const +{ + return d->opusVersion; +} + +//////////////////////////////////////////////////////////////////////////////// +// private members +//////////////////////////////////////////////////////////////////////////////// + +void Opus::Properties::read() +{ + // Get the identification header from the Ogg implementation. + + // http://tools.ietf.org/html/draft-terriberry-oggopus-01#section-5.1 + + ByteVector data = d->file->packet(0); + + // *Magic Signature* + int pos = 8; + + // *Version* (8 bits, unsigned) + d->opusVersion = uchar(data.at(pos)); + pos += 1; + + // *Output Channel Count* 'C' (8 bits, unsigned) + d->channels = uchar(data.at(pos)); + pos += 1; + + // *Pre-skip* (16 bits, unsigned, little endian) + ushort preSkip = data.mid(pos, 2).toUShort(false); + pos += 2; + + // *Input Sample Rate* (32 bits, unsigned, little endian) + d->inputSampleRate = data.mid(pos, 4).toUInt(false); + pos += 4; + + // *Output Gain* (16 bits, signed, little endian) + pos += 2; + + // *Channel Mapping Family* (8 bits, unsigned) + pos += 1; + + const Ogg::PageHeader *first = d->file->firstPageHeader(); + const Ogg::PageHeader *last = d->file->lastPageHeader(); + + if(first && last) { + long long start = first->absoluteGranularPosition(); + long long end = last->absoluteGranularPosition(); + + if(start >= 0 && end >= 0) + d->length = (int) ((end - start - preSkip) / 48000); + else { + debug("Opus::Properties::read() -- The PCM values for the start or " + "end of this file was incorrect."); + } + } + else + debug("Opus::Properties::read() -- Could not find valid first and last Ogg pages."); +} diff --git a/taglib/ogg/opus/opusproperties.h b/taglib/ogg/opus/opusproperties.h new file mode 100644 index 00000000..946f1675 --- /dev/null +++ b/taglib/ogg/opus/opusproperties.h @@ -0,0 +1,96 @@ +/*************************************************************************** + copyright : (C) 2012 by Lukáš Lalinský + email : lalinsky@gmail.com + + copyright : (C) 2002 - 2008 by Scott Wheeler + email : wheeler@kde.org + (original Vorbis implementation) +***************************************************************************/ + +/*************************************************************************** + * This library is free software; you can redistribute it and/or modify * + * it under the terms of the GNU Lesser General Public License version * + * 2.1 as published by the Free Software Foundation. * + * * + * This library is distributed in the hope that it will be useful, but * + * WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * + * Lesser General Public License for more details. * + * * + * You should have received a copy of the GNU Lesser General Public * + * License along with this library; if not, write to the Free Software * + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * + * 02110-1301 USA * + * * + * Alternatively, this file is available under the Mozilla Public * + * License Version 1.1. You may obtain a copy of the License at * + * http://www.mozilla.org/MPL/ * + ***************************************************************************/ + +#ifndef TAGLIB_OPUSPROPERTIES_H +#define TAGLIB_OPUSPROPERTIES_H + +#include "audioproperties.h" + +namespace TagLib { + + namespace Ogg { + + namespace Opus { + + class File; + + //! An implementation of audio property reading for Ogg Opus + + /*! + * This reads the data from an Ogg Opus stream found in the AudioProperties + * API. + */ + + class TAGLIB_EXPORT Properties : public AudioProperties + { + public: + /*! + * Create an instance of Opus::Properties with the data read from the + * Opus::File \a file. + */ + Properties(File *file, ReadStyle style = Average); + + /*! + * Destroys this Opus::Properties instance. + */ + virtual ~Properties(); + + // Reimplementations. + + virtual int length() const; + virtual int bitrate() const; + virtual int sampleRate() const; + virtual int channels() const; + + /*! + * The Opus codec supports decoding at multiple sample rates, there is no + * single sample rate of the encoded stream. This returns the sample rate + * of the original audio stream. + */ + int inputSampleRate() const; + + /*! + * Returns the Opus version, currently "0" (as specified by the spec). + */ + int opusVersion() const; + + private: + Properties(const Properties &); + Properties &operator=(const Properties &); + + void read(); + + class PropertiesPrivate; + PropertiesPrivate *d; + }; + } + } +} + +#endif diff --git a/taglib/toolkit/tfile.cpp b/taglib/toolkit/tfile.cpp index d0a6116f..8d7ccdc9 100644 --- a/taglib/toolkit/tfile.cpp +++ b/taglib/toolkit/tfile.cpp @@ -60,6 +60,7 @@ #include "mp4file.h" #include "wavpackfile.h" #include "speexfile.h" +#include "opusfile.h" #include "trueaudiofile.h" #include "aifffile.h" #include "wavfile.h" @@ -135,6 +136,8 @@ PropertyMap File::properties() const return dynamic_cast(this)->properties(); if(dynamic_cast(this)) return dynamic_cast(this)->properties(); + if(dynamic_cast(this)) + return dynamic_cast(this)->properties(); if(dynamic_cast(this)) return dynamic_cast(this)->properties(); if(dynamic_cast(this)) @@ -174,6 +177,8 @@ void File::removeUnsupportedProperties(const StringList &properties) dynamic_cast(this)->removeUnsupportedProperties(properties); else if(dynamic_cast(this)) dynamic_cast(this)->removeUnsupportedProperties(properties); + else if(dynamic_cast(this)) + dynamic_cast(this)->removeUnsupportedProperties(properties); else if(dynamic_cast(this)) dynamic_cast(this)->removeUnsupportedProperties(properties); else if(dynamic_cast(this)) @@ -210,6 +215,8 @@ PropertyMap File::setProperties(const PropertyMap &properties) return dynamic_cast(this)->setProperties(properties); else if(dynamic_cast(this)) return dynamic_cast(this)->setProperties(properties); + else if(dynamic_cast(this)) + return dynamic_cast(this)->setProperties(properties); else if(dynamic_cast(this)) return dynamic_cast(this)->setProperties(properties); else if(dynamic_cast(this)) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index f51e7bfa..4e428af8 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -16,6 +16,8 @@ INCLUDE_DIRECTORIES( ${CMAKE_CURRENT_SOURCE_DIR}/../taglib/ogg ${CMAKE_CURRENT_SOURCE_DIR}/../taglib/ogg/vorbis ${CMAKE_CURRENT_SOURCE_DIR}/../taglib/ogg/flac + ${CMAKE_CURRENT_SOURCE_DIR}/../taglib/ogg/speex + ${CMAKE_CURRENT_SOURCE_DIR}/../taglib/ogg/opus ${CMAKE_CURRENT_SOURCE_DIR}/../taglib/flac ${CMAKE_CURRENT_SOURCE_DIR}/../taglib/wavpack ${CMAKE_CURRENT_SOURCE_DIR}/../taglib/mod @@ -60,6 +62,7 @@ SET(test_runner_SRCS test_it.cpp test_xm.cpp test_mpc.cpp + test_opus.cpp ) INCLUDE_DIRECTORIES(${CPPUNIT_INCLUDE_DIR}) diff --git a/tests/data/correctness_gain_silent_output.opus b/tests/data/correctness_gain_silent_output.opus new file mode 100644 index 0000000000000000000000000000000000000000..00972c42f64f285610c17ab548ac0bfa6960bdff GIT binary patch literal 35506 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 literal 0 HcmV?d00001 diff --git a/tests/test_opus.cpp b/tests/test_opus.cpp new file mode 100644 index 00000000..35ee3b3d --- /dev/null +++ b/tests/test_opus.cpp @@ -0,0 +1,61 @@ +#include +#include +#include +#include +#include +#include +#include "utils.h" + +using namespace std; +using namespace TagLib; + +class TestOpus : public CppUnit::TestFixture +{ + CPPUNIT_TEST_SUITE(TestOpus); + CPPUNIT_TEST(testProperties); + CPPUNIT_TEST(testReadComments); + CPPUNIT_TEST(testWriteComments); + CPPUNIT_TEST_SUITE_END(); + +public: + + void testProperties() + { + Ogg::Opus::File f(TEST_FILE_PATH_C("correctness_gain_silent_output.opus")); + CPPUNIT_ASSERT_EQUAL(7, f.audioProperties()->length()); + CPPUNIT_ASSERT_EQUAL(0, f.audioProperties()->bitrate()); + CPPUNIT_ASSERT_EQUAL(1, f.audioProperties()->channels()); + CPPUNIT_ASSERT_EQUAL(48000, f.audioProperties()->sampleRate()); + CPPUNIT_ASSERT_EQUAL(48000, ((Ogg::Opus::Properties *)f.audioProperties())->inputSampleRate()); + } + + void testReadComments() + { + Ogg::Opus::File f(TEST_FILE_PATH_C("correctness_gain_silent_output.opus")); + CPPUNIT_ASSERT_EQUAL(StringList("Xiph.Org Opus testvectormaker"), f.tag()->fieldListMap()["ENCODER"]); + CPPUNIT_ASSERT(f.tag()->fieldListMap().contains("TESTDESCRIPTION")); + CPPUNIT_ASSERT(!f.tag()->fieldListMap().contains("ARTIST")); + CPPUNIT_ASSERT_EQUAL(String("libopus 0.9.11-66-g64c2dd7"), f.tag()->vendorID()); + } + + void testWriteComments() + { + ScopedFileCopy copy("correctness_gain_silent_output", ".opus"); + string filename = copy.fileName(); + + Ogg::Opus::File *f = new Ogg::Opus::File(filename.c_str()); + f->tag()->setArtist("Your Tester"); + f->save(); + delete f; + + f = new Ogg::Opus::File(filename.c_str()); + CPPUNIT_ASSERT_EQUAL(StringList("Xiph.Org Opus testvectormaker"), f->tag()->fieldListMap()["ENCODER"]); + CPPUNIT_ASSERT(f->tag()->fieldListMap().contains("TESTDESCRIPTION")); + CPPUNIT_ASSERT_EQUAL(StringList("Your Tester"), f->tag()->fieldListMap()["ARTIST"]); + CPPUNIT_ASSERT_EQUAL(String("libopus 0.9.11-66-g64c2dd7"), f->tag()->vendorID()); + delete f; + } + +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(TestOpus); From ca543039a570ed1f42f161d89aabcc79e07f2ee1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Lalinsk=C3=BD?= Date: Sat, 13 Oct 2012 09:06:09 +0200 Subject: [PATCH 42/67] Include Opus in docs --- taglib/toolkit/taglib.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/taglib/toolkit/taglib.h b/taglib/toolkit/taglib.h index 9ecdd365..e941ca78 100755 --- a/taglib/toolkit/taglib.h +++ b/taglib/toolkit/taglib.h @@ -145,7 +145,7 @@ namespace TagLib { * - A clean, high level, C++ API to handling audio meta data. * - Format specific APIs for advanced API users. * - ID3v1, ID3v2, APE, FLAC, Xiph, iTunes-style MP4 and WMA tag formats. - * - MP3, MPC, FLAC, MP4, ASF, AIFF, WAV, TrueAudio, WavPack, Ogg FLAC, Ogg Vorbis and Speex file formats. + * - MP3, MPC, FLAC, MP4, ASF, AIFF, WAV, TrueAudio, WavPack, Ogg FLAC, Ogg Vorbis, Speex and Opus file formats. * - Basic audio file properties such as length, sample rate, etc. * - Long term binary and source compatibility. * - Extensible design, notably the ability to add other formats or extend current formats as a library user. From e568e1019df6b8dc782c9fab34b8eaed1ee0bf13 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Lalinsk=C3=BD?= Date: Sat, 13 Oct 2012 09:11:20 +0200 Subject: [PATCH 43/67] Remove useless debug print --- taglib/ogg/opus/opusfile.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/taglib/ogg/opus/opusfile.cpp b/taglib/ogg/opus/opusfile.cpp index 836f89c9..ecbc218d 100644 --- a/taglib/ogg/opus/opusfile.cpp +++ b/taglib/ogg/opus/opusfile.cpp @@ -119,8 +119,6 @@ void Opus::File::read(bool readProperties, Properties::ReadStyle propertiesStyle return; } - debug("starts with OpusTags"); - d->comment = new Ogg::XiphComment(commentHeaderData.mid(8)); if(readProperties) From 7a884af0ef5220dd87e7c48aa382b154491d564c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Lalinsk=C3=BD?= Date: Sat, 13 Oct 2012 13:33:36 +0200 Subject: [PATCH 44/67] Experimenting with Travis CI --- .travis.yml | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 00000000..2131ed30 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,6 @@ +language: cpp +compiler: + - gcc +install: apt-get install libcppunit-dev zlib1g-dev +script: cmake && make + From 074f6db6d8078590f1a9d5282c671f4fd7fbb0e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Lalinsk=C3=BD?= Date: Sat, 13 Oct 2012 13:34:44 +0200 Subject: [PATCH 45/67] Needs sudo --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 2131ed30..ff3c0e64 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,6 @@ language: cpp compiler: - gcc -install: apt-get install libcppunit-dev zlib1g-dev +install: sudo apt-get install libcppunit-dev zlib1g-dev script: cmake && make From 2d7686b5fa41909e15eaee75ede7ff1e599418d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Lalinsk=C3=BD?= Date: Sat, 13 Oct 2012 13:36:33 +0200 Subject: [PATCH 46/67] Actually run the tests --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index ff3c0e64..1be87b2a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,5 +2,5 @@ language: cpp compiler: - gcc install: sudo apt-get install libcppunit-dev zlib1g-dev -script: cmake && make +script: cmake . && make && make check From 044da877e629ea180d734ba86339564dc0886a7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Lalinsk=C3=BD?= Date: Sat, 13 Oct 2012 13:38:35 +0200 Subject: [PATCH 47/67] Make sure we build the tests --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 1be87b2a..34379033 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,5 +2,5 @@ language: cpp compiler: - gcc install: sudo apt-get install libcppunit-dev zlib1g-dev -script: cmake . && make && make check +script: cmake -DBUILD_TESTS=ON . && make && make check From c42bdeab43dc1a0fa1c15ddf025e76932ba26f62 Mon Sep 17 00:00:00 2001 From: Jonathan Marshall Date: Sun, 4 Nov 2012 19:52:10 +1300 Subject: [PATCH 48/67] fixes noop frame id size check in ID3v2::FrameFactory::createFrame --- taglib/mpeg/id3v2/id3v2framefactory.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/taglib/mpeg/id3v2/id3v2framefactory.cpp b/taglib/mpeg/id3v2/id3v2framefactory.cpp index 7e2e98a8..bba2985c 100644 --- a/taglib/mpeg/id3v2/id3v2framefactory.cpp +++ b/taglib/mpeg/id3v2/id3v2framefactory.cpp @@ -99,7 +99,7 @@ Frame *FrameFactory::createFrame(const ByteVector &origData, Header *tagHeader) // A quick sanity check -- make sure that the frameID is 4 uppercase Latin1 // characters. Also make sure that there is data in the frame. - if(!frameID.size() == (version < 3 ? 3 : 4) || + if(frameID.size() != (version < 3 ? 3 : 4) || header->frameSize() <= uint(header->dataLengthIndicator() ? 4 : 0) || header->frameSize() > data.size()) { From be6187e8937e330eb23cb390fda804536af6f834 Mon Sep 17 00:00:00 2001 From: Jonathan Marshall Date: Sun, 4 Nov 2012 19:53:55 +1300 Subject: [PATCH 49/67] adds new iTunes hack for v2.2 frames stored in a v2.3 tag (iTunes v8.1.1.10 for example) --- taglib/mpeg/id3v2/id3v2framefactory.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/taglib/mpeg/id3v2/id3v2framefactory.cpp b/taglib/mpeg/id3v2/id3v2framefactory.cpp index bba2985c..c7d74214 100644 --- a/taglib/mpeg/id3v2/id3v2framefactory.cpp +++ b/taglib/mpeg/id3v2/id3v2framefactory.cpp @@ -107,6 +107,17 @@ Frame *FrameFactory::createFrame(const ByteVector &origData, Header *tagHeader) return 0; } +#ifndef NO_ITUNES_HACKS + if(version == 3 && frameID.size() == 4 && frameID[3] == '\0') { + // iTunes v2.3 tags store v2.2 frames - convert now + frameID = frameID.mid(0, 3); + header->setFrameID(frameID); + header->setVersion(2); + updateFrame(header); + header->setVersion(3); + } +#endif + for(ByteVector::ConstIterator it = frameID.begin(); it != frameID.end(); it++) { if( (*it < 'A' || *it > 'Z') && (*it < '0' || *it > '9') ) { delete header; From 56fbe7e14d05ea4d9291f33b2404b2b864d9b2dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Lalinsk=C3=BD?= Date: Sat, 10 Nov 2012 20:51:17 +0100 Subject: [PATCH 50/67] Make travis run tests also with clang --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 34379033..ba237166 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,7 @@ language: cpp compiler: - gcc + - clang install: sudo apt-get install libcppunit-dev zlib1g-dev script: cmake -DBUILD_TESTS=ON . && make && make check From 9e788bb8c2a195a7bbb300321dd4d721c58eb74d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Lalinsk=C3=BD?= Date: Sun, 11 Nov 2012 14:26:10 +0100 Subject: [PATCH 51/67] Fix tests when compiled with clang on Linux --- tests/test_apetag.cpp | 2 +- tests/test_asf.cpp | 2 +- tests/test_bytevector.cpp | 2 +- tests/test_bytevectorlist.cpp | 2 +- tests/test_bytevectorstream.cpp | 2 +- tests/test_fileref.cpp | 2 +- tests/test_flac.cpp | 2 +- tests/test_flacpicture.cpp | 2 +- tests/test_flacunknownmetadatablock.cpp | 2 +- tests/test_id3v1.cpp | 2 +- tests/test_id3v2.cpp | 2 +- tests/test_it.cpp | 2 +- tests/test_mod.cpp | 2 +- tests/test_mp4.cpp | 2 +- tests/test_mp4coverart.cpp | 2 +- tests/test_mp4item.cpp | 2 +- tests/test_mpeg.cpp | 3 ++- tests/test_ogg.cpp | 8 ++++---- tests/test_oggflac.cpp | 2 +- tests/test_opus.cpp | 2 +- tests/test_riff.cpp | 2 +- tests/test_s3m.cpp | 2 +- tests/test_string.cpp | 2 +- tests/test_synchdata.cpp | 2 +- tests/test_xiphcomment.cpp | 2 +- tests/test_xm.cpp | 2 +- 26 files changed, 30 insertions(+), 29 deletions(-) diff --git a/tests/test_apetag.cpp b/tests/test_apetag.cpp index 0c63f0b6..c93b2a7e 100644 --- a/tests/test_apetag.cpp +++ b/tests/test_apetag.cpp @@ -1,4 +1,3 @@ -#include #include #include #include @@ -7,6 +6,7 @@ #include #include #include +#include #include "utils.h" using namespace std; diff --git a/tests/test_asf.cpp b/tests/test_asf.cpp index 51981342..0cd1f3d7 100644 --- a/tests/test_asf.cpp +++ b/tests/test_asf.cpp @@ -1,10 +1,10 @@ -#include #include #include #include #include #include #include +#include #include "utils.h" using namespace std; diff --git a/tests/test_bytevector.cpp b/tests/test_bytevector.cpp index 2ac78b34..dd433456 100644 --- a/tests/test_bytevector.cpp +++ b/tests/test_bytevector.cpp @@ -22,9 +22,9 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include #include #include +#include using namespace std; using namespace TagLib; diff --git a/tests/test_bytevectorlist.cpp b/tests/test_bytevectorlist.cpp index f090fcdc..7e4a9fd7 100644 --- a/tests/test_bytevectorlist.cpp +++ b/tests/test_bytevectorlist.cpp @@ -1,6 +1,6 @@ -#include #include #include +#include using namespace std; using namespace TagLib; diff --git a/tests/test_bytevectorstream.cpp b/tests/test_bytevectorstream.cpp index b5114679..b0fec90b 100644 --- a/tests/test_bytevectorstream.cpp +++ b/tests/test_bytevectorstream.cpp @@ -1,5 +1,5 @@ -#include #include +#include using namespace std; using namespace TagLib; diff --git a/tests/test_fileref.cpp b/tests/test_fileref.cpp index d0c8a3a5..5500ae7f 100644 --- a/tests/test_fileref.cpp +++ b/tests/test_fileref.cpp @@ -1,10 +1,10 @@ -#include #include #include #include #include #include #include +#include #include "utils.h" #ifdef HAVE_CONFIG_H #include "config.h" diff --git a/tests/test_flac.cpp b/tests/test_flac.cpp index 992a02b8..3d31f33c 100644 --- a/tests/test_flac.cpp +++ b/tests/test_flac.cpp @@ -1,4 +1,3 @@ -#include #include #include #include @@ -7,6 +6,7 @@ #include #include #include +#include #include "utils.h" using namespace std; diff --git a/tests/test_flacpicture.cpp b/tests/test_flacpicture.cpp index 5ed13951..0ba9452f 100644 --- a/tests/test_flacpicture.cpp +++ b/tests/test_flacpicture.cpp @@ -1,4 +1,3 @@ -#include #include #include #include @@ -6,6 +5,7 @@ #include #include #include +#include #include "utils.h" using namespace std; diff --git a/tests/test_flacunknownmetadatablock.cpp b/tests/test_flacunknownmetadatablock.cpp index 2a99a1ee..881078dd 100644 --- a/tests/test_flacunknownmetadatablock.cpp +++ b/tests/test_flacunknownmetadatablock.cpp @@ -1,10 +1,10 @@ -#include #include #include #include #include #include #include +#include #include "utils.h" using namespace std; diff --git a/tests/test_id3v1.cpp b/tests/test_id3v1.cpp index 308225c2..1d0a4974 100644 --- a/tests/test_id3v1.cpp +++ b/tests/test_id3v1.cpp @@ -1,7 +1,7 @@ -#include #include #include #include +#include using namespace std; using namespace TagLib; diff --git a/tests/test_id3v2.cpp b/tests/test_id3v2.cpp index d3fbfee9..99f77101 100644 --- a/tests/test_id3v2.cpp +++ b/tests/test_id3v2.cpp @@ -1,4 +1,3 @@ -#include #include #include // so evil :( @@ -18,6 +17,7 @@ #include #include #include +#include #include "utils.h" using namespace std; diff --git a/tests/test_it.cpp b/tests/test_it.cpp index 53d8ea0e..be5680ec 100644 --- a/tests/test_it.cpp +++ b/tests/test_it.cpp @@ -19,9 +19,9 @@ * MA 02110-1301 USA * ***************************************************************************/ -#include #include #include +#include #include "utils.h" using namespace std; diff --git a/tests/test_mod.cpp b/tests/test_mod.cpp index 05657c13..0a233c97 100644 --- a/tests/test_mod.cpp +++ b/tests/test_mod.cpp @@ -19,9 +19,9 @@ * MA 02110-1301 USA * ***************************************************************************/ -#include #include #include +#include #include "utils.h" using namespace std; diff --git a/tests/test_mp4.cpp b/tests/test_mp4.cpp index 14a4df8e..d6830a61 100644 --- a/tests/test_mp4.cpp +++ b/tests/test_mp4.cpp @@ -1,4 +1,3 @@ -#include #include #include #include @@ -6,6 +5,7 @@ #include #include #include +#include #include "utils.h" using namespace std; diff --git a/tests/test_mp4coverart.cpp b/tests/test_mp4coverart.cpp index 75c6ad03..812a2e5d 100644 --- a/tests/test_mp4coverart.cpp +++ b/tests/test_mp4coverart.cpp @@ -1,8 +1,8 @@ -#include #include #include #include #include +#include #include "utils.h" using namespace std; diff --git a/tests/test_mp4item.cpp b/tests/test_mp4item.cpp index 68ba0c8d..37d3f719 100644 --- a/tests/test_mp4item.cpp +++ b/tests/test_mp4item.cpp @@ -1,9 +1,9 @@ -#include #include #include #include #include #include +#include #include "utils.h" using namespace std; diff --git a/tests/test_mpeg.cpp b/tests/test_mpeg.cpp index 18724728..ad6acc49 100644 --- a/tests/test_mpeg.cpp +++ b/tests/test_mpeg.cpp @@ -1,8 +1,9 @@ -#include #include #include +#include #include #include +#include #include "utils.h" using namespace std; diff --git a/tests/test_ogg.cpp b/tests/test_ogg.cpp index fb7a262a..c89586aa 100644 --- a/tests/test_ogg.cpp +++ b/tests/test_ogg.cpp @@ -1,4 +1,3 @@ -#include #include #include #include @@ -8,6 +7,7 @@ #include #include #include +#include #include "utils.h" using namespace std; @@ -88,12 +88,12 @@ public: CPPUNIT_ASSERT_EQUAL(TagLib::uint(2), tags["UNUSUALTAG"].size()); CPPUNIT_ASSERT_EQUAL(String("usual value"), tags["UNUSUALTAG"][0]); CPPUNIT_ASSERT_EQUAL(String("another value"), tags["UNUSUALTAG"][1]); - CPPUNIT_ASSERT_EQUAL(String(L"öäüoΣø"), tags["UNICODETAG"][0]); + CPPUNIT_ASSERT_EQUAL(String("öäüoΣø", String::UTF8), tags["UNICODETAG"][0]); - tags["UNICODETAG"][0] = L"νεω ναλυε"; + tags["UNICODETAG"][0] = String("νεω ναλυε", String::UTF8); tags.erase("UNUSUALTAG"); f->tag()->setProperties(tags); - CPPUNIT_ASSERT_EQUAL(String(L"νεω ναλυε"), f->tag()->properties()["UNICODETAG"][0]); + CPPUNIT_ASSERT_EQUAL(String("νεω ναλυε", String::UTF8), f->tag()->properties()["UNICODETAG"][0]); CPPUNIT_ASSERT_EQUAL(false, f->tag()->properties().contains("UNUSUALTAG")); delete f; diff --git a/tests/test_oggflac.cpp b/tests/test_oggflac.cpp index c1837175..1cdb24b0 100644 --- a/tests/test_oggflac.cpp +++ b/tests/test_oggflac.cpp @@ -1,4 +1,3 @@ -#include #include #include #include @@ -6,6 +5,7 @@ #include #include #include +#include #include "utils.h" using namespace std; diff --git a/tests/test_opus.cpp b/tests/test_opus.cpp index 35ee3b3d..769d3985 100644 --- a/tests/test_opus.cpp +++ b/tests/test_opus.cpp @@ -1,9 +1,9 @@ -#include #include #include #include #include #include +#include #include "utils.h" using namespace std; diff --git a/tests/test_riff.cpp b/tests/test_riff.cpp index 43b2f8bb..8e120d06 100644 --- a/tests/test_riff.cpp +++ b/tests/test_riff.cpp @@ -1,9 +1,9 @@ -#include #include #include #include #include #include +#include #include "utils.h" using namespace std; diff --git a/tests/test_s3m.cpp b/tests/test_s3m.cpp index a6ecc084..24a4c6e4 100644 --- a/tests/test_s3m.cpp +++ b/tests/test_s3m.cpp @@ -19,8 +19,8 @@ * MA 02110-1301 USA * ***************************************************************************/ -#include #include +#include #include "utils.h" using namespace std; diff --git a/tests/test_string.cpp b/tests/test_string.cpp index 79b32c3a..1e37d7a2 100644 --- a/tests/test_string.cpp +++ b/tests/test_string.cpp @@ -22,9 +22,9 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include #include #include +#include using namespace std; using namespace TagLib; diff --git a/tests/test_synchdata.cpp b/tests/test_synchdata.cpp index 1113ee43..2e3021ea 100644 --- a/tests/test_synchdata.cpp +++ b/tests/test_synchdata.cpp @@ -22,8 +22,8 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include #include +#include using namespace std; using namespace TagLib; diff --git a/tests/test_xiphcomment.cpp b/tests/test_xiphcomment.cpp index a98aea61..6526229b 100644 --- a/tests/test_xiphcomment.cpp +++ b/tests/test_xiphcomment.cpp @@ -1,9 +1,9 @@ -#include #include #include #include #include #include +#include #include "utils.h" using namespace std; diff --git a/tests/test_xm.cpp b/tests/test_xm.cpp index 70b3967e..28086533 100644 --- a/tests/test_xm.cpp +++ b/tests/test_xm.cpp @@ -19,8 +19,8 @@ * MA 02110-1301 USA * ***************************************************************************/ -#include #include +#include #include "utils.h" using namespace std; From 72745846f486dbc3348b0e992358cf61cef0ddf0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Lalinsk=C3=BD?= Date: Sun, 11 Nov 2012 14:49:31 +0100 Subject: [PATCH 52/67] Update NEWS --- NEWS | 1 + 1 file changed, 1 insertion(+) diff --git a/NEWS b/NEWS index 9432865a..24d396cd 100644 --- a/NEWS +++ b/NEWS @@ -10,6 +10,7 @@ TagLib 1.9 (In Development) * Better parsing of corrupted FLAC files. * Fixed saving of PropertyMap comments without description into ID3v2 tags. * Fixed crash when parsing certain XM files. + * Fixed compilation of unit test with clang. TagLib 1.8 (Sep 6, 2012) ======================== From 3a5aeb457324f9dcac2c0051e94b0d748af63f65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Lalinsk=C3=BD?= Date: Sun, 11 Nov 2012 16:04:08 +0100 Subject: [PATCH 53/67] Parse covr atoms with type 0 (fixes issue #84) --- taglib/mp4/mp4coverart.h | 9 +++++---- taglib/mp4/mp4tag.cpp | 5 ++++- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/taglib/mp4/mp4coverart.h b/taglib/mp4/mp4coverart.h index 804ab785..64115b45 100644 --- a/taglib/mp4/mp4coverart.h +++ b/taglib/mp4/mp4coverart.h @@ -42,10 +42,11 @@ namespace TagLib { * This describes the image type. */ enum Format { - JPEG = TypeJPEG, - PNG = TypePNG, - BMP = TypeBMP, - GIF = TypeGIF + JPEG = TypeJPEG, + PNG = TypePNG, + BMP = TypeBMP, + GIF = TypeGIF, + Unknown = TypeImplicit, }; CoverArt(Format format, const ByteVector &data); diff --git a/taglib/mp4/mp4tag.cpp b/taglib/mp4/mp4tag.cpp index f929ef64..afecf98a 100644 --- a/taglib/mp4/mp4tag.cpp +++ b/taglib/mp4/mp4tag.cpp @@ -283,10 +283,13 @@ MP4::Tag::parseCovr(MP4::Atom *atom, TagLib::File *file) debug("MP4: Unexpected atom \"" + name + "\", expecting \"data\""); break; } - if(flags == TypeJPEG || flags == TypePNG || flags == TypeBMP || flags == TypeGIF) { + if(flags == TypeJPEG || flags == TypePNG || flags == TypeBMP || flags == TypeGIF || flags == TypeImplicit) { value.append(MP4::CoverArt(MP4::CoverArt::Format(flags), data.mid(pos + 16, length - 16))); } + else { + debug("MP4: Unknown covr format " + String::number(flags)); + } pos += length; } if(value.size() > 0) From 45b0279b411958d8fcc1428dc38084f351ae4b80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Lalinsk=C3=BD?= Date: Sun, 11 Nov 2012 16:43:36 +0100 Subject: [PATCH 54/67] Add a tool to inspect audio files, only MP4 is implemented for now --- examples/CMakeLists.txt | 8 +++++ examples/inspect.cpp | 61 ++++++++++++++++++++++++++++++++++++ taglib/audioproperties.cpp | 11 +++++++ taglib/audioproperties.h | 3 ++ taglib/mp4/mp4file.cpp | 13 ++++++++ taglib/mp4/mp4file.h | 5 +++ taglib/mp4/mp4item.cpp | 56 ++++++++++++++++++++++++++++++++- taglib/mp4/mp4item.h | 17 ++++++++++ taglib/mp4/mp4properties.cpp | 31 +++++++++++++++++- taglib/mp4/mp4properties.h | 2 ++ taglib/mp4/mp4tag.cpp | 10 ++++++ taglib/mp4/mp4tag.h | 2 ++ taglib/tag.cpp | 14 +++++++++ taglib/tag.h | 2 ++ taglib/toolkit/tfile.cpp | 14 +++++++++ taglib/toolkit/tfile.h | 5 +++ 16 files changed, 252 insertions(+), 2 deletions(-) create mode 100644 examples/inspect.cpp diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 17d4bd6b..7812d2e9 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -5,6 +5,7 @@ INCLUDE_DIRECTORIES( ${CMAKE_CURRENT_SOURCE_DIR}/../taglib ${CMAKE_CURRENT_SOURCE_DIR}/../taglib/mpeg ${CMAKE_CURRENT_SOURCE_DIR}/../taglib/mpeg/id3v1 ${CMAKE_CURRENT_SOURCE_DIR}/../taglib/mpeg/id3v2 + ${CMAKE_CURRENT_SOURCE_DIR}/../taglib/mp4 ${CMAKE_CURRENT_SOURCE_DIR}/../bindings/c/ ) if(ENABLE_STATIC) @@ -46,5 +47,12 @@ ADD_EXECUTABLE(strip-id3v1 strip-id3v1.cpp) TARGET_LINK_LIBRARIES(strip-id3v1 tag ) +########### next target ############### + +ADD_EXECUTABLE(inspect inspect.cpp) + +TARGET_LINK_LIBRARIES(inspect tag ) + + endif(BUILD_EXAMPLES) diff --git a/examples/inspect.cpp b/examples/inspect.cpp new file mode 100644 index 00000000..90a94dbb --- /dev/null +++ b/examples/inspect.cpp @@ -0,0 +1,61 @@ +/* Copyright (C) 2012 Lukas Lalinsky + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include +#include + +using namespace std; +using namespace TagLib; + +#define MAYBE_PRINT_DESC(_Type) \ + if(dynamic_cast<_Type *>(f.file())) { \ + cout << dynamic_cast<_Type *>(f.file())->toString().to8Bit(true) << endl; \ + found = 1; \ + } + +int main(int argc, char *argv[]) +{ + // process the command line args + + for(int i = 1; i < argc; i++) { + + cout << "******************** \"" << argv[i] << "\"********************" << endl; + + FileRef f(argv[i]); + + bool found = 0; + if(!f.isNull() && f.file()) { + MAYBE_PRINT_DESC(MP4::File); + MAYBE_PRINT_DESC(File); + } + + if(!found) { + cout << "could not find any information about the file" << endl; + } + + } +} diff --git a/taglib/audioproperties.cpp b/taglib/audioproperties.cpp index 298b97da..5b25867b 100644 --- a/taglib/audioproperties.cpp +++ b/taglib/audioproperties.cpp @@ -24,6 +24,7 @@ ***************************************************************************/ #include "audioproperties.h" +#include "tstringlist.h" using namespace TagLib; @@ -49,3 +50,13 @@ AudioProperties::AudioProperties(ReadStyle) { } + +String +AudioProperties::toString() const +{ + StringList desc; + desc.append("Audio"); + desc.append(String::number(length()) + " seconds"); + desc.append(String::number(bitrate()) + " kbps"); + return desc.toString(", "); +} diff --git a/taglib/audioproperties.h b/taglib/audioproperties.h index e9844fa0..12400b9f 100644 --- a/taglib/audioproperties.h +++ b/taglib/audioproperties.h @@ -27,6 +27,7 @@ #define TAGLIB_AUDIOPROPERTIES_H #include "taglib_export.h" +#include "tstring.h" namespace TagLib { @@ -86,6 +87,8 @@ namespace TagLib { */ virtual int channels() const = 0; + String toString() const; + protected: /*! diff --git a/taglib/mp4/mp4file.cpp b/taglib/mp4/mp4file.cpp index a9543211..8d6a99a3 100644 --- a/taglib/mp4/mp4file.cpp +++ b/taglib/mp4/mp4file.cpp @@ -149,3 +149,16 @@ MP4::File::save() return d->tag->save(); } +String +MP4::File::toString() const +{ + StringList desc; + if(d->properties) { + desc.append(d->properties->toString()); + } + if(d->tag) { + desc.append(d->tag->toString()); + } + return desc.toString("\n"); +} + diff --git a/taglib/mp4/mp4file.h b/taglib/mp4/mp4file.h index 2ed3bea5..d8468490 100644 --- a/taglib/mp4/mp4file.h +++ b/taglib/mp4/mp4file.h @@ -100,6 +100,11 @@ namespace TagLib { */ bool save(); + /*! + * Description of the file. + */ + String toString() const; + private: void read(bool readProperties, Properties::ReadStyle audioPropertiesStyle); diff --git a/taglib/mp4/mp4item.cpp b/taglib/mp4/mp4item.cpp index af2cc65c..4ed11a27 100644 --- a/taglib/mp4/mp4item.cpp +++ b/taglib/mp4/mp4item.cpp @@ -26,6 +26,7 @@ #ifdef HAVE_CONFIG_H #include #endif +#include #include #include @@ -36,10 +37,11 @@ using namespace TagLib; class MP4::Item::ItemPrivate : public RefCounter { public: - ItemPrivate() : RefCounter(), valid(true), atomDataType(TypeUndefined) {} + ItemPrivate() : RefCounter(), valid(true), atomDataType(MP4::TypeUndefined), type(MP4::Item::TypeUndefined) {} bool valid; AtomDataType atomDataType; + ItemType type; union { bool m_bool; int m_int; @@ -86,30 +88,35 @@ MP4::Item::Item(bool value) { d = new ItemPrivate; d->m_bool = value; + d->type = TypeBool; } MP4::Item::Item(int value) { d = new ItemPrivate; d->m_int = value; + d->type = TypeInt; } MP4::Item::Item(uchar value) { d = new ItemPrivate; d->m_byte = value; + d->type = TypeByte; } MP4::Item::Item(uint value) { d = new ItemPrivate; d->m_uint = value; + d->type = TypeUInt; } MP4::Item::Item(long long value) { d = new ItemPrivate; d->m_longlong = value; + d->type = TypeLongLong; } MP4::Item::Item(int value1, int value2) @@ -117,24 +124,28 @@ MP4::Item::Item(int value1, int value2) d = new ItemPrivate; d->m_intPair.first = value1; d->m_intPair.second = value2; + d->type = TypeIntPair; } MP4::Item::Item(const ByteVectorList &value) { d = new ItemPrivate; d->m_byteVectorList = value; + d->type = TypeByteVectorList; } MP4::Item::Item(const StringList &value) { d = new ItemPrivate; d->m_stringList = value; + d->type = TypeStringList; } MP4::Item::Item(const MP4::CoverArtList &value) { d = new ItemPrivate; d->m_coverArtList = value; + d->type = TypeCoverArtList; } void MP4::Item::setAtomDataType(MP4::AtomDataType type) @@ -207,3 +218,46 @@ MP4::Item::isValid() const return d->valid; } +String +MP4::Item::toString() const +{ + StringList desc; + char tmp[256]; + switch (d->type) { + case TypeBool: + return d->m_bool ? "true" : "false"; + case TypeInt: + sprintf(tmp, "%d", d->m_int); + return tmp; + case TypeIntPair: + sprintf(tmp, "%d/%d", d->m_intPair.first, d->m_intPair.second); + return tmp; + case TypeByte: + sprintf(tmp, "%d", d->m_byte); + return tmp; + case TypeUInt: + sprintf(tmp, "%u", d->m_uint); + return tmp; + case TypeLongLong: + sprintf(tmp, "%lld", d->m_longlong); + return tmp; + case TypeStringList: + return d->m_stringList.toString(" / "); + case TypeByteVectorList: + for(int i = 0; i < d->m_byteVectorList.size(); i++) { + sprintf(tmp, "[%d bytes of data]", d->m_byteVectorList[i].size()); + desc.append(tmp); + } + return desc.toString(", "); + case TypeCoverArtList: + for(int i = 0; i < d->m_coverArtList.size(); i++) { + sprintf(tmp, "[%d bytes of data]", d->m_coverArtList[i].data().size()); + desc.append(tmp); + } + return desc.toString(", "); + case TypeUndefined: + return "[unknown]"; + } + return String(); +} + diff --git a/taglib/mp4/mp4item.h b/taglib/mp4/mp4item.h index be7aa1a1..d97c9834 100644 --- a/taglib/mp4/mp4item.h +++ b/taglib/mp4/mp4item.h @@ -41,6 +41,19 @@ namespace TagLib { int first, second; }; + enum ItemType { + TypeUndefined = 0, + TypeBool, + TypeInt, + TypeIntPair, + TypeByte, + TypeUInt, + TypeLongLong, + TypeStringList, + TypeByteVectorList, + TypeCoverArtList, + }; + Item(); Item(const Item &item); Item &operator=(const Item &item); @@ -69,8 +82,12 @@ namespace TagLib { ByteVectorList toByteVectorList() const; CoverArtList toCoverArtList() const; + ItemType type() const; + bool isValid() const; + String toString() const; + private: class ItemPrivate; ItemPrivate *d; diff --git a/taglib/mp4/mp4properties.cpp b/taglib/mp4/mp4properties.cpp index d2d04167..35830bb9 100644 --- a/taglib/mp4/mp4properties.cpp +++ b/taglib/mp4/mp4properties.cpp @@ -38,7 +38,13 @@ using namespace TagLib; class MP4::Properties::PropertiesPrivate { public: - PropertiesPrivate() : length(0), bitrate(0), sampleRate(0), channels(0), bitsPerSample(0), encrypted(false) {} + PropertiesPrivate() : length(0), bitrate(0), sampleRate(0), channels(0), bitsPerSample(0), encrypted(false), format(Unknown) {} + + enum Format { + Unknown = 0, + AAC = 1, + ALAC = 2, + }; int length; int bitrate; @@ -46,6 +52,7 @@ public: int channels; int bitsPerSample; bool encrypted; + Format format; }; MP4::Properties::Properties(File *file, MP4::Atoms *atoms, ReadStyle style) @@ -118,6 +125,7 @@ MP4::Properties::Properties(File *file, MP4::Atoms *atoms, ReadStyle style) file->seek(atom->offset); data = file->readBlock(atom->length); if(data.mid(20, 4) == "mp4a") { + d->format = PropertiesPrivate::AAC; d->channels = data.mid(40, 2).toShort(); d->bitsPerSample = data.mid(42, 2).toShort(); d->sampleRate = data.mid(46, 4).toUInt(); @@ -138,6 +146,7 @@ MP4::Properties::Properties(File *file, MP4::Atoms *atoms, ReadStyle style) } } else if (data.mid(20, 4) == "alac") { + d->format = PropertiesPrivate::ALAC; if (atom->length == 88 && data.mid(56, 4) == "alac") { d->bitsPerSample = data.at(69); d->channels = data.at(73); @@ -193,3 +202,23 @@ MP4::Properties::isEncrypted() const return d->encrypted; } +String +MP4::Properties::toString() const +{ + String format; + if(d->format == PropertiesPrivate::AAC) { + format = "AAC"; + } + else if(d->format == PropertiesPrivate::ALAC) { + format = "ALAC"; + } + else { + format = "Unknown"; + } + StringList desc; + desc.append("MPEG-4 audio (" + format + ")"); + desc.append(String::number(length()) + " seconds"); + desc.append(String::number(bitrate()) + " kbps"); + return desc.toString(", "); +} + diff --git a/taglib/mp4/mp4properties.h b/taglib/mp4/mp4properties.h index 7906824d..0f12f934 100644 --- a/taglib/mp4/mp4properties.h +++ b/taglib/mp4/mp4properties.h @@ -50,6 +50,8 @@ namespace TagLib { virtual int bitsPerSample() const; bool isEncrypted() const; + String toString() const; + private: class PropertiesPrivate; PropertiesPrivate *d; diff --git a/taglib/mp4/mp4tag.cpp b/taglib/mp4/mp4tag.cpp index afecf98a..24693fe0 100644 --- a/taglib/mp4/mp4tag.cpp +++ b/taglib/mp4/mp4tag.cpp @@ -759,3 +759,13 @@ MP4::Tag::itemListMap() return d->items; } +String +MP4::Tag::toString() const +{ + StringList desc; + for(MP4::ItemListMap::Iterator i = d->items.begin(); i != d->items.end(); i++) { + desc.append(i->first + "=" + i->second.toString()); + } + return desc.toString("\n"); +} + diff --git a/taglib/mp4/mp4tag.h b/taglib/mp4/mp4tag.h index b5ea6ebb..2270432c 100644 --- a/taglib/mp4/mp4tag.h +++ b/taglib/mp4/mp4tag.h @@ -67,6 +67,8 @@ namespace TagLib { ItemListMap &itemListMap(); + String toString() const; + private: AtomDataList parseData2(Atom *atom, TagLib::File *file, int expectedFlags = -1, bool freeForm = false); TagLib::ByteVectorList parseData(Atom *atom, TagLib::File *file, int expectedFlags = -1, bool freeForm = false); diff --git a/taglib/tag.cpp b/taglib/tag.cpp index 67634081..17dcca5c 100644 --- a/taglib/tag.cpp +++ b/taglib/tag.cpp @@ -178,3 +178,17 @@ void Tag::duplicate(const Tag *source, Tag *target, bool overwrite) // static target->setTrack(source->track()); } } + +String Tag::toString() const +{ + StringList desc; + desc.append("title=" + title()); + desc.append("artist=" + artist()); + desc.append("album=" + album()); + desc.append("comment=" + comment()); + desc.append("genre=" + genre()); + desc.append("year=" + String::number(year())); + desc.append("track=" + String::number(track())); + return desc.toString("\n"); +} + diff --git a/taglib/tag.h b/taglib/tag.h index 76c9a82a..43337a74 100644 --- a/taglib/tag.h +++ b/taglib/tag.h @@ -182,6 +182,8 @@ namespace TagLib { */ static void duplicate(const Tag *source, Tag *target, bool overwrite = true); + String toString() const; + protected: /*! * Construct a Tag. This is protected since tags should only be instantiated diff --git a/taglib/toolkit/tfile.cpp b/taglib/toolkit/tfile.cpp index 8d7ccdc9..3d7c1885 100644 --- a/taglib/toolkit/tfile.cpp +++ b/taglib/toolkit/tfile.cpp @@ -489,6 +489,20 @@ bool File::isWritable(const char *file) } +String File::toString() const +{ + StringList desc; + AudioProperties *properties = audioProperties(); + if(properties) { + desc.append(properties->toString()); + } + Tag *t = tag(); + if(t) { + desc.append(t->toString()); + } + return desc.toString("\n"); +} + //////////////////////////////////////////////////////////////////////////////// // protected members //////////////////////////////////////////////////////////////////////////////// diff --git a/taglib/toolkit/tfile.h b/taglib/toolkit/tfile.h index 7e6f2b93..a7cd2d09 100644 --- a/taglib/toolkit/tfile.h +++ b/taglib/toolkit/tfile.h @@ -247,6 +247,11 @@ namespace TagLib { */ static bool isWritable(const char *name); + /*! + * Description of the file. + */ + String toString() const; + protected: /*! * Construct a File object and opens the \a file. \a file should be a From dbe6be778b5e04c48923d1e9d84b3fe414503595 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Lalinsk=C3=BD?= Date: Sun, 11 Nov 2012 16:44:01 +0100 Subject: [PATCH 55/67] Build also examples --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index ba237166..76e58336 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,5 +3,5 @@ compiler: - gcc - clang install: sudo apt-get install libcppunit-dev zlib1g-dev -script: cmake -DBUILD_TESTS=ON . && make && make check +script: cmake -DBUILD_TESTS=ON -DBUILD_EXAMPLES=ON . && make && make check From c6f7ad3e83e6888c448cac2d1ac242181554623f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Lalinsk=C3=BD?= Date: Mon, 12 Nov 2012 16:14:32 +0100 Subject: [PATCH 56/67] Revert "Add a tool to inspect audio files, only MP4 is implemented for now" This reverts commit 45b0279b411958d8fcc1428dc38084f351ae4b80. --- examples/CMakeLists.txt | 8 ----- examples/inspect.cpp | 61 ------------------------------------ taglib/audioproperties.cpp | 11 ------- taglib/audioproperties.h | 3 -- taglib/mp4/mp4file.cpp | 13 -------- taglib/mp4/mp4file.h | 5 --- taglib/mp4/mp4item.cpp | 56 +-------------------------------- taglib/mp4/mp4item.h | 17 ---------- taglib/mp4/mp4properties.cpp | 31 +----------------- taglib/mp4/mp4properties.h | 2 -- taglib/mp4/mp4tag.cpp | 10 ------ taglib/mp4/mp4tag.h | 2 -- taglib/tag.cpp | 14 --------- taglib/tag.h | 2 -- taglib/toolkit/tfile.cpp | 14 --------- taglib/toolkit/tfile.h | 5 --- 16 files changed, 2 insertions(+), 252 deletions(-) delete mode 100644 examples/inspect.cpp diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 7812d2e9..17d4bd6b 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -5,7 +5,6 @@ INCLUDE_DIRECTORIES( ${CMAKE_CURRENT_SOURCE_DIR}/../taglib ${CMAKE_CURRENT_SOURCE_DIR}/../taglib/mpeg ${CMAKE_CURRENT_SOURCE_DIR}/../taglib/mpeg/id3v1 ${CMAKE_CURRENT_SOURCE_DIR}/../taglib/mpeg/id3v2 - ${CMAKE_CURRENT_SOURCE_DIR}/../taglib/mp4 ${CMAKE_CURRENT_SOURCE_DIR}/../bindings/c/ ) if(ENABLE_STATIC) @@ -47,12 +46,5 @@ ADD_EXECUTABLE(strip-id3v1 strip-id3v1.cpp) TARGET_LINK_LIBRARIES(strip-id3v1 tag ) -########### next target ############### - -ADD_EXECUTABLE(inspect inspect.cpp) - -TARGET_LINK_LIBRARIES(inspect tag ) - - endif(BUILD_EXAMPLES) diff --git a/examples/inspect.cpp b/examples/inspect.cpp deleted file mode 100644 index 90a94dbb..00000000 --- a/examples/inspect.cpp +++ /dev/null @@ -1,61 +0,0 @@ -/* Copyright (C) 2012 Lukas Lalinsky - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. - * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include -#include - -#include -#include - -using namespace std; -using namespace TagLib; - -#define MAYBE_PRINT_DESC(_Type) \ - if(dynamic_cast<_Type *>(f.file())) { \ - cout << dynamic_cast<_Type *>(f.file())->toString().to8Bit(true) << endl; \ - found = 1; \ - } - -int main(int argc, char *argv[]) -{ - // process the command line args - - for(int i = 1; i < argc; i++) { - - cout << "******************** \"" << argv[i] << "\"********************" << endl; - - FileRef f(argv[i]); - - bool found = 0; - if(!f.isNull() && f.file()) { - MAYBE_PRINT_DESC(MP4::File); - MAYBE_PRINT_DESC(File); - } - - if(!found) { - cout << "could not find any information about the file" << endl; - } - - } -} diff --git a/taglib/audioproperties.cpp b/taglib/audioproperties.cpp index 5b25867b..298b97da 100644 --- a/taglib/audioproperties.cpp +++ b/taglib/audioproperties.cpp @@ -24,7 +24,6 @@ ***************************************************************************/ #include "audioproperties.h" -#include "tstringlist.h" using namespace TagLib; @@ -50,13 +49,3 @@ AudioProperties::AudioProperties(ReadStyle) { } - -String -AudioProperties::toString() const -{ - StringList desc; - desc.append("Audio"); - desc.append(String::number(length()) + " seconds"); - desc.append(String::number(bitrate()) + " kbps"); - return desc.toString(", "); -} diff --git a/taglib/audioproperties.h b/taglib/audioproperties.h index 12400b9f..e9844fa0 100644 --- a/taglib/audioproperties.h +++ b/taglib/audioproperties.h @@ -27,7 +27,6 @@ #define TAGLIB_AUDIOPROPERTIES_H #include "taglib_export.h" -#include "tstring.h" namespace TagLib { @@ -87,8 +86,6 @@ namespace TagLib { */ virtual int channels() const = 0; - String toString() const; - protected: /*! diff --git a/taglib/mp4/mp4file.cpp b/taglib/mp4/mp4file.cpp index 8d6a99a3..a9543211 100644 --- a/taglib/mp4/mp4file.cpp +++ b/taglib/mp4/mp4file.cpp @@ -149,16 +149,3 @@ MP4::File::save() return d->tag->save(); } -String -MP4::File::toString() const -{ - StringList desc; - if(d->properties) { - desc.append(d->properties->toString()); - } - if(d->tag) { - desc.append(d->tag->toString()); - } - return desc.toString("\n"); -} - diff --git a/taglib/mp4/mp4file.h b/taglib/mp4/mp4file.h index d8468490..2ed3bea5 100644 --- a/taglib/mp4/mp4file.h +++ b/taglib/mp4/mp4file.h @@ -100,11 +100,6 @@ namespace TagLib { */ bool save(); - /*! - * Description of the file. - */ - String toString() const; - private: void read(bool readProperties, Properties::ReadStyle audioPropertiesStyle); diff --git a/taglib/mp4/mp4item.cpp b/taglib/mp4/mp4item.cpp index 4ed11a27..af2cc65c 100644 --- a/taglib/mp4/mp4item.cpp +++ b/taglib/mp4/mp4item.cpp @@ -26,7 +26,6 @@ #ifdef HAVE_CONFIG_H #include #endif -#include #include #include @@ -37,11 +36,10 @@ using namespace TagLib; class MP4::Item::ItemPrivate : public RefCounter { public: - ItemPrivate() : RefCounter(), valid(true), atomDataType(MP4::TypeUndefined), type(MP4::Item::TypeUndefined) {} + ItemPrivate() : RefCounter(), valid(true), atomDataType(TypeUndefined) {} bool valid; AtomDataType atomDataType; - ItemType type; union { bool m_bool; int m_int; @@ -88,35 +86,30 @@ MP4::Item::Item(bool value) { d = new ItemPrivate; d->m_bool = value; - d->type = TypeBool; } MP4::Item::Item(int value) { d = new ItemPrivate; d->m_int = value; - d->type = TypeInt; } MP4::Item::Item(uchar value) { d = new ItemPrivate; d->m_byte = value; - d->type = TypeByte; } MP4::Item::Item(uint value) { d = new ItemPrivate; d->m_uint = value; - d->type = TypeUInt; } MP4::Item::Item(long long value) { d = new ItemPrivate; d->m_longlong = value; - d->type = TypeLongLong; } MP4::Item::Item(int value1, int value2) @@ -124,28 +117,24 @@ MP4::Item::Item(int value1, int value2) d = new ItemPrivate; d->m_intPair.first = value1; d->m_intPair.second = value2; - d->type = TypeIntPair; } MP4::Item::Item(const ByteVectorList &value) { d = new ItemPrivate; d->m_byteVectorList = value; - d->type = TypeByteVectorList; } MP4::Item::Item(const StringList &value) { d = new ItemPrivate; d->m_stringList = value; - d->type = TypeStringList; } MP4::Item::Item(const MP4::CoverArtList &value) { d = new ItemPrivate; d->m_coverArtList = value; - d->type = TypeCoverArtList; } void MP4::Item::setAtomDataType(MP4::AtomDataType type) @@ -218,46 +207,3 @@ MP4::Item::isValid() const return d->valid; } -String -MP4::Item::toString() const -{ - StringList desc; - char tmp[256]; - switch (d->type) { - case TypeBool: - return d->m_bool ? "true" : "false"; - case TypeInt: - sprintf(tmp, "%d", d->m_int); - return tmp; - case TypeIntPair: - sprintf(tmp, "%d/%d", d->m_intPair.first, d->m_intPair.second); - return tmp; - case TypeByte: - sprintf(tmp, "%d", d->m_byte); - return tmp; - case TypeUInt: - sprintf(tmp, "%u", d->m_uint); - return tmp; - case TypeLongLong: - sprintf(tmp, "%lld", d->m_longlong); - return tmp; - case TypeStringList: - return d->m_stringList.toString(" / "); - case TypeByteVectorList: - for(int i = 0; i < d->m_byteVectorList.size(); i++) { - sprintf(tmp, "[%d bytes of data]", d->m_byteVectorList[i].size()); - desc.append(tmp); - } - return desc.toString(", "); - case TypeCoverArtList: - for(int i = 0; i < d->m_coverArtList.size(); i++) { - sprintf(tmp, "[%d bytes of data]", d->m_coverArtList[i].data().size()); - desc.append(tmp); - } - return desc.toString(", "); - case TypeUndefined: - return "[unknown]"; - } - return String(); -} - diff --git a/taglib/mp4/mp4item.h b/taglib/mp4/mp4item.h index d97c9834..be7aa1a1 100644 --- a/taglib/mp4/mp4item.h +++ b/taglib/mp4/mp4item.h @@ -41,19 +41,6 @@ namespace TagLib { int first, second; }; - enum ItemType { - TypeUndefined = 0, - TypeBool, - TypeInt, - TypeIntPair, - TypeByte, - TypeUInt, - TypeLongLong, - TypeStringList, - TypeByteVectorList, - TypeCoverArtList, - }; - Item(); Item(const Item &item); Item &operator=(const Item &item); @@ -82,12 +69,8 @@ namespace TagLib { ByteVectorList toByteVectorList() const; CoverArtList toCoverArtList() const; - ItemType type() const; - bool isValid() const; - String toString() const; - private: class ItemPrivate; ItemPrivate *d; diff --git a/taglib/mp4/mp4properties.cpp b/taglib/mp4/mp4properties.cpp index 35830bb9..d2d04167 100644 --- a/taglib/mp4/mp4properties.cpp +++ b/taglib/mp4/mp4properties.cpp @@ -38,13 +38,7 @@ using namespace TagLib; class MP4::Properties::PropertiesPrivate { public: - PropertiesPrivate() : length(0), bitrate(0), sampleRate(0), channels(0), bitsPerSample(0), encrypted(false), format(Unknown) {} - - enum Format { - Unknown = 0, - AAC = 1, - ALAC = 2, - }; + PropertiesPrivate() : length(0), bitrate(0), sampleRate(0), channels(0), bitsPerSample(0), encrypted(false) {} int length; int bitrate; @@ -52,7 +46,6 @@ public: int channels; int bitsPerSample; bool encrypted; - Format format; }; MP4::Properties::Properties(File *file, MP4::Atoms *atoms, ReadStyle style) @@ -125,7 +118,6 @@ MP4::Properties::Properties(File *file, MP4::Atoms *atoms, ReadStyle style) file->seek(atom->offset); data = file->readBlock(atom->length); if(data.mid(20, 4) == "mp4a") { - d->format = PropertiesPrivate::AAC; d->channels = data.mid(40, 2).toShort(); d->bitsPerSample = data.mid(42, 2).toShort(); d->sampleRate = data.mid(46, 4).toUInt(); @@ -146,7 +138,6 @@ MP4::Properties::Properties(File *file, MP4::Atoms *atoms, ReadStyle style) } } else if (data.mid(20, 4) == "alac") { - d->format = PropertiesPrivate::ALAC; if (atom->length == 88 && data.mid(56, 4) == "alac") { d->bitsPerSample = data.at(69); d->channels = data.at(73); @@ -202,23 +193,3 @@ MP4::Properties::isEncrypted() const return d->encrypted; } -String -MP4::Properties::toString() const -{ - String format; - if(d->format == PropertiesPrivate::AAC) { - format = "AAC"; - } - else if(d->format == PropertiesPrivate::ALAC) { - format = "ALAC"; - } - else { - format = "Unknown"; - } - StringList desc; - desc.append("MPEG-4 audio (" + format + ")"); - desc.append(String::number(length()) + " seconds"); - desc.append(String::number(bitrate()) + " kbps"); - return desc.toString(", "); -} - diff --git a/taglib/mp4/mp4properties.h b/taglib/mp4/mp4properties.h index 0f12f934..7906824d 100644 --- a/taglib/mp4/mp4properties.h +++ b/taglib/mp4/mp4properties.h @@ -50,8 +50,6 @@ namespace TagLib { virtual int bitsPerSample() const; bool isEncrypted() const; - String toString() const; - private: class PropertiesPrivate; PropertiesPrivate *d; diff --git a/taglib/mp4/mp4tag.cpp b/taglib/mp4/mp4tag.cpp index 24693fe0..afecf98a 100644 --- a/taglib/mp4/mp4tag.cpp +++ b/taglib/mp4/mp4tag.cpp @@ -759,13 +759,3 @@ MP4::Tag::itemListMap() return d->items; } -String -MP4::Tag::toString() const -{ - StringList desc; - for(MP4::ItemListMap::Iterator i = d->items.begin(); i != d->items.end(); i++) { - desc.append(i->first + "=" + i->second.toString()); - } - return desc.toString("\n"); -} - diff --git a/taglib/mp4/mp4tag.h b/taglib/mp4/mp4tag.h index 2270432c..b5ea6ebb 100644 --- a/taglib/mp4/mp4tag.h +++ b/taglib/mp4/mp4tag.h @@ -67,8 +67,6 @@ namespace TagLib { ItemListMap &itemListMap(); - String toString() const; - private: AtomDataList parseData2(Atom *atom, TagLib::File *file, int expectedFlags = -1, bool freeForm = false); TagLib::ByteVectorList parseData(Atom *atom, TagLib::File *file, int expectedFlags = -1, bool freeForm = false); diff --git a/taglib/tag.cpp b/taglib/tag.cpp index 17dcca5c..67634081 100644 --- a/taglib/tag.cpp +++ b/taglib/tag.cpp @@ -178,17 +178,3 @@ void Tag::duplicate(const Tag *source, Tag *target, bool overwrite) // static target->setTrack(source->track()); } } - -String Tag::toString() const -{ - StringList desc; - desc.append("title=" + title()); - desc.append("artist=" + artist()); - desc.append("album=" + album()); - desc.append("comment=" + comment()); - desc.append("genre=" + genre()); - desc.append("year=" + String::number(year())); - desc.append("track=" + String::number(track())); - return desc.toString("\n"); -} - diff --git a/taglib/tag.h b/taglib/tag.h index 43337a74..76c9a82a 100644 --- a/taglib/tag.h +++ b/taglib/tag.h @@ -182,8 +182,6 @@ namespace TagLib { */ static void duplicate(const Tag *source, Tag *target, bool overwrite = true); - String toString() const; - protected: /*! * Construct a Tag. This is protected since tags should only be instantiated diff --git a/taglib/toolkit/tfile.cpp b/taglib/toolkit/tfile.cpp index 3d7c1885..8d7ccdc9 100644 --- a/taglib/toolkit/tfile.cpp +++ b/taglib/toolkit/tfile.cpp @@ -489,20 +489,6 @@ bool File::isWritable(const char *file) } -String File::toString() const -{ - StringList desc; - AudioProperties *properties = audioProperties(); - if(properties) { - desc.append(properties->toString()); - } - Tag *t = tag(); - if(t) { - desc.append(t->toString()); - } - return desc.toString("\n"); -} - //////////////////////////////////////////////////////////////////////////////// // protected members //////////////////////////////////////////////////////////////////////////////// diff --git a/taglib/toolkit/tfile.h b/taglib/toolkit/tfile.h index a7cd2d09..7e6f2b93 100644 --- a/taglib/toolkit/tfile.h +++ b/taglib/toolkit/tfile.h @@ -247,11 +247,6 @@ namespace TagLib { */ static bool isWritable(const char *name); - /*! - * Description of the file. - */ - String toString() const; - protected: /*! * Construct a File object and opens the \a file. \a file should be a From ade8dc1a218e43c3909c4eede58f4e28e09da73a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Lalinsk=C3=BD?= Date: Tue, 20 Nov 2012 14:15:16 +0100 Subject: [PATCH 57/67] Fix opening of read-only files on Windows The CreateFile* functions return INVALID_HANDLE_VALUE on error, not NULL. http://article.gmane.org/gmane.comp.kde.devel.taglib/2346 --- taglib/toolkit/tfilestream.cpp | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/taglib/toolkit/tfilestream.cpp b/taglib/toolkit/tfilestream.cpp index 16efb579..35300de6 100644 --- a/taglib/toolkit/tfilestream.cpp +++ b/taglib/toolkit/tfilestream.cpp @@ -56,10 +56,16 @@ namespace { { DWORD access = readOnly ? GENERIC_READ : (GENERIC_READ | GENERIC_WRITE); + HANDLE handle; if(wcslen(path) > 0) - return CreateFileW(path, access, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); + handle = CreateFileW(path, access, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); else - return CreateFileA(path, access, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); + handle = CreateFileA(path, access, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); + + if(handle == INVALID_HANDLE_VALUE) + handle = NULL; + + return handle; } size_t fread(void *ptr, size_t size, size_t nmemb, HANDLE stream) From 57b8ae6e1ca4822164d19865ef7ef1e8aee8b941 Mon Sep 17 00:00:00 2001 From: Tsuda Kageyu Date: Tue, 20 Nov 2012 22:20:34 +0900 Subject: [PATCH 58/67] Fix reading read-only files in Win32 --- taglib/toolkit/tfilestream.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/taglib/toolkit/tfilestream.cpp b/taglib/toolkit/tfilestream.cpp index 16efb579..d68dbc82 100644 --- a/taglib/toolkit/tfilestream.cpp +++ b/taglib/toolkit/tfilestream.cpp @@ -52,6 +52,8 @@ namespace { // Using native file handles instead of file descriptors for reducing the resource consumption. + const HANDLE InvalidFile = INVALID_HANDLE_VALUE; + HANDLE openFile(const FileName &path, bool readOnly) { DWORD access = readOnly ? GENERIC_READ : (GENERIC_READ | GENERIC_WRITE); @@ -82,6 +84,8 @@ namespace { // For non-Windows + const FILE *InvalidFile = 0; + struct FileNameHandle : public std::string { FileNameHandle(FileName name) : std::string(name) {} @@ -119,7 +123,7 @@ public: }; FileStream::FileStreamPrivate::FileStreamPrivate(FileName fileName, bool openReadOnly) : - file(0), + file(InvalidFile), name(fileName), readOnly(true), size(0) @@ -129,12 +133,12 @@ FileStream::FileStreamPrivate::FileStreamPrivate(FileName fileName, bool openRea if(!openReadOnly) file = openFile(name, false); - if(file) + if(file != InvalidFile) readOnly = false; else file = openFile(name, true); - if(!file) { + if(file == InvalidFile) { debug("Could not open file " + String((const char *) name)); } } From 3f6da779d2c68271417f7fe44c3c5ab7032692cf Mon Sep 17 00:00:00 2001 From: Tsuda Kageyu Date: Tue, 20 Nov 2012 22:46:03 +0900 Subject: [PATCH 59/67] Fix compilation in non-Win32 --- taglib/toolkit/tfilestream.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/taglib/toolkit/tfilestream.cpp b/taglib/toolkit/tfilestream.cpp index d68dbc82..52252023 100644 --- a/taglib/toolkit/tfilestream.cpp +++ b/taglib/toolkit/tfilestream.cpp @@ -84,7 +84,7 @@ namespace { // For non-Windows - const FILE *InvalidFile = 0; + FILE *const InvalidFile = 0; struct FileNameHandle : public std::string { From 45317ef7f203a7fac6719c553828ab4c2fa630dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Lalinsk=C3=BD?= Date: Tue, 20 Nov 2012 18:34:51 +0100 Subject: [PATCH 60/67] Revert "Fix opening of read-only files on Windows" This reverts commit ade8dc1a218e43c3909c4eede58f4e28e09da73a. --- taglib/toolkit/tfilestream.cpp | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/taglib/toolkit/tfilestream.cpp b/taglib/toolkit/tfilestream.cpp index 6077d705..52252023 100644 --- a/taglib/toolkit/tfilestream.cpp +++ b/taglib/toolkit/tfilestream.cpp @@ -58,16 +58,10 @@ namespace { { DWORD access = readOnly ? GENERIC_READ : (GENERIC_READ | GENERIC_WRITE); - HANDLE handle; if(wcslen(path) > 0) - handle = CreateFileW(path, access, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); + return CreateFileW(path, access, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); else - handle = CreateFileA(path, access, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); - - if(handle == INVALID_HANDLE_VALUE) - handle = NULL; - - return handle; + return CreateFileA(path, access, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); } size_t fread(void *ptr, size_t size, size_t nmemb, HANDLE stream) From 15b601f053685ff592c1c59827c87d62034329e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Lalinsk=C3=BD?= Date: Wed, 21 Nov 2012 14:40:26 +0100 Subject: [PATCH 61/67] Use PropertyMap in tagreader --- examples/tagreader.cpp | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/examples/tagreader.cpp b/examples/tagreader.cpp index 76fe0d1a..cc978eba 100644 --- a/examples/tagreader.cpp +++ b/examples/tagreader.cpp @@ -23,10 +23,12 @@ */ #include +#include #include #include #include +#include using namespace std; @@ -49,7 +51,7 @@ int main(int argc, char *argv[]) TagLib::Tag *tag = f.tag(); - cout << "-- TAG --" << endl; + cout << "-- TAG (basic) --" << endl; cout << "title - \"" << tag->title() << "\"" << endl; cout << "artist - \"" << tag->artist() << "\"" << endl; cout << "album - \"" << tag->album() << "\"" << endl; @@ -57,6 +59,23 @@ int main(int argc, char *argv[]) cout << "comment - \"" << tag->comment() << "\"" << endl; cout << "track - \"" << tag->track() << "\"" << endl; cout << "genre - \"" << tag->genre() << "\"" << endl; + + TagLib::PropertyMap tags = f.file()->properties(); + + unsigned int longest = 0; + for(TagLib::PropertyMap::ConstIterator i = tags.begin(); i != tags.end(); ++i) { + if (i->first.size() > longest) { + longest = i->first.size(); + } + } + + cout << "-- TAG (properties) --" << endl; + for(TagLib::PropertyMap::ConstIterator i = tags.begin(); i != tags.end(); ++i) { + for(TagLib::StringList::ConstIterator j = i->second.begin(); j != i->second.end(); ++j) { + cout << left << std::setw(longest) << i->first << " - " << '"' << *j << '"' << endl; + } + } + } if(!f.isNull() && f.audioProperties()) { From e75d6f616c82ebb00b405ce7dd03d77a7f589ff7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Lalinsk=C3=BD?= Date: Wed, 21 Nov 2012 17:21:30 +0100 Subject: [PATCH 62/67] Add support for reading MusicBrainz IDs from ID3v2 tags to PropertyMap --- .../id3v2/frames/textidentificationframe.cpp | 16 ++---- .../frames/uniquefileidentifierframe.cpp | 30 +++++++++++ .../id3v2/frames/uniquefileidentifierframe.h | 10 ++++ taglib/mpeg/id3v2/id3v2frame.cpp | 53 ++++++++++++++++++- taglib/mpeg/id3v2/id3v2frame.h | 9 ++++ taglib/mpeg/id3v2/id3v2tag.cpp | 8 ++- taglib/toolkit/tpropertymap.h | 48 +++++++++++++++++ tests/test_id3v2.cpp | 24 ++++++++- 8 files changed, 182 insertions(+), 16 deletions(-) diff --git a/taglib/mpeg/id3v2/frames/textidentificationframe.cpp b/taglib/mpeg/id3v2/frames/textidentificationframe.cpp index 3287063c..70ea50f8 100644 --- a/taglib/mpeg/id3v2/frames/textidentificationframe.cpp +++ b/taglib/mpeg/id3v2/frames/textidentificationframe.cpp @@ -381,18 +381,12 @@ void UserTextIdentificationFrame::setDescription(const String &s) PropertyMap UserTextIdentificationFrame::asProperties() const { - String tagName = description(); - PropertyMap map; - String key = tagName.upper(); - if(key.isNull()) // this frame's description is not a valid PropertyMap key -> add to unsupported list - map.unsupportedData().append(L"TXXX/" + description()); - else { - StringList v = fieldList(); - for(StringList::ConstIterator it = v.begin(); it != v.end(); ++it) - if(*it != description()) - map.insert(key, *it); - } + String tagName = txxxToKey(description()); + StringList v = fieldList(); + for(StringList::ConstIterator it = v.begin(); it != v.end(); ++it) + if(it != v.begin()) + map.insert(tagName, *it); return map; } diff --git a/taglib/mpeg/id3v2/frames/uniquefileidentifierframe.cpp b/taglib/mpeg/id3v2/frames/uniquefileidentifierframe.cpp index e12583ad..0725c729 100644 --- a/taglib/mpeg/id3v2/frames/uniquefileidentifierframe.cpp +++ b/taglib/mpeg/id3v2/frames/uniquefileidentifierframe.cpp @@ -24,8 +24,10 @@ ***************************************************************************/ #include +#include #include +#include "id3v2tag.h" #include "uniquefileidentifierframe.h" using namespace TagLib; @@ -87,6 +89,34 @@ String UniqueFileIdentifierFrame::toString() const return String::null; } +PropertyMap UniqueFileIdentifierFrame::asProperties() const +{ + PropertyMap map; + if(d->owner == "http://musicbrainz.org") { + map.insert("MUSICBRAINZ_RECORDINGID", String(d->identifier)); + } + else { + map.unsupportedData().append(frameID() + String("/") + d->owner); + } + return map; +} + +UniqueFileIdentifierFrame *UniqueFileIdentifierFrame::findByOwner(const ID3v2::Tag *tag, const String &o) // static +{ + ID3v2::FrameList comments = tag->frameList("UFID"); + + for(ID3v2::FrameList::ConstIterator it = comments.begin(); + it != comments.end(); + ++it) + { + UniqueFileIdentifierFrame *frame = dynamic_cast(*it); + if(frame && frame->owner() == o) + return frame; + } + + return 0; +} + void UniqueFileIdentifierFrame::parseFields(const ByteVector &data) { if(data.size() < 1) { diff --git a/taglib/mpeg/id3v2/frames/uniquefileidentifierframe.h b/taglib/mpeg/id3v2/frames/uniquefileidentifierframe.h index 0cf4b8f6..add5a2e0 100644 --- a/taglib/mpeg/id3v2/frames/uniquefileidentifierframe.h +++ b/taglib/mpeg/id3v2/frames/uniquefileidentifierframe.h @@ -94,6 +94,16 @@ namespace TagLib { virtual String toString() const; + PropertyMap asProperties() const; + + /*! + * UFID frames each have a unique owner. This searches for a UFID + * frame with the owner \a o and returns a pointer to it. + * + * \see owner() + */ + static UniqueFileIdentifierFrame *findByOwner(const Tag *tag, const String &o); + protected: virtual void parseFields(const ByteVector &data); virtual ByteVector renderFields() const; diff --git a/taglib/mpeg/id3v2/id3v2frame.cpp b/taglib/mpeg/id3v2/id3v2frame.cpp index 1079785d..372778b1 100644 --- a/taglib/mpeg/id3v2/id3v2frame.cpp +++ b/taglib/mpeg/id3v2/id3v2frame.cpp @@ -44,6 +44,7 @@ #include "frames/urllinkframe.h" #include "frames/unsynchronizedlyricsframe.h" #include "frames/commentsframe.h" +#include "frames/uniquefileidentifierframe.h" #include "frames/unknownframe.h" using namespace TagLib; @@ -126,6 +127,10 @@ Frame *Frame::createTextualFrame(const String &key, const StringList &values) // return frame; } } + if(key == "MUSICBRAINZ_RECORDINGID" && values.size() == 1) { + UniqueFileIdentifierFrame *frame = new UniqueFileIdentifierFrame("http://musicbrainz.org", values.front().data(String::UTF8)); + return frame; + } // now we check if it's one of the "special" cases: // -LYRICS: depending on the number of values, use USLT or TXXX (with description=LYRICS) if((key == "LYRICS" || key.startsWith(lyricsPrefix)) && values.size() == 1){ @@ -151,7 +156,7 @@ Frame *Frame::createTextualFrame(const String &key, const StringList &values) // return frame; } // if non of the above cases apply, we use a TXXX frame with the key as description - return new UserTextIdentificationFrame(key, values, String::UTF8); + return new UserTextIdentificationFrame(keyToTXXX(key), values, String::UTF8); } Frame::~Frame() @@ -387,6 +392,18 @@ static const char *frameTranslation[][2] = { //{ "USLT", "LYRICS" }, handled specially }; +static const TagLib::uint txxxFrameTranslationSize = 7; +static const char *txxxFrameTranslation[][2] = { + { "MusicBrainz Album Id", "MUSICBRAINZ_RELEASEID" }, + { "MusicBrainz Artist Id", "MUSICBRAINZ_ARTISTID" }, + { "MusicBrainz Album Artist Id", "MUSICBRAINZ_RELEASEARTISTID" }, + { "MusicBrainz Release Group Id", "MUSICBRAINZ_RELEASEGROUPID" }, + { "MusicBrainz Work Id", "MUSICBRAINZ_WORKID" }, + { "Acoustid Id", "ACOUSTID_ID" }, + { "Acoustid Fingerprint", "ACOUSTID_FINGERPRINT" }, + { "MusicIP PUID", "MUSICIP_PUID" }, +}; + Map &idMap() { static Map m; @@ -396,6 +413,18 @@ Map &idMap() return m; } +Map &txxxMap() +{ + static Map m; + if(m.isEmpty()) { + for(size_t i = 0; i < txxxFrameTranslationSize; ++i) { + String key = String(txxxFrameTranslation[i][0]).upper(); + m[key] = txxxFrameTranslation[i][1]; + } + } + return m; +} + // list of deprecated frames and their successors static const TagLib::uint deprecatedFramesSize = 4; static const char *deprecatedFrames[][2] = { @@ -435,6 +464,26 @@ ByteVector Frame::keyToFrameID(const String &s) return ByteVector::null; } +String Frame::txxxToKey(const String &description) +{ + Map &m = txxxMap(); + String d = description.upper(); + if(m.contains(d)) + return m[d]; + return d; +} + +String Frame::keyToTXXX(const String &s) +{ + static Map m; + if(m.isEmpty()) + for(size_t i = 0; i < txxxFrameTranslationSize; ++i) + m[txxxFrameTranslation[i][1]] = txxxFrameTranslation[i][0]; + if(m.contains(s.upper())) + return m[s]; + return s; +} + PropertyMap Frame::asProperties() const { if(dynamic_cast< const UnknownFrame *>(this)) { @@ -456,6 +505,8 @@ PropertyMap Frame::asProperties() const return dynamic_cast< const CommentsFrame* >(this)->asProperties(); else if(id == "USLT") return dynamic_cast< const UnsynchronizedLyricsFrame* >(this)->asProperties(); + else if(id == "UFID") + return dynamic_cast< const UniqueFileIdentifierFrame* >(this)->asProperties(); PropertyMap m; m.unsupportedData().append(id); return m; diff --git a/taglib/mpeg/id3v2/id3v2frame.h b/taglib/mpeg/id3v2/id3v2frame.h index 95c4070b..3e257d4f 100644 --- a/taglib/mpeg/id3v2/id3v2frame.h +++ b/taglib/mpeg/id3v2/id3v2frame.h @@ -274,6 +274,15 @@ namespace TagLib { */ static String frameIDToKey(const ByteVector &); + /*! + * Returns an appropriate TXXX frame description for the given free-form tag key. + */ + static String keyToTXXX(const String &); + + /*! + * Returns a free-form tag name for the given ID3 frame description. + */ + static String txxxToKey(const String &); /*! * This helper function splits the PropertyMap \a original into three ProperytMaps diff --git a/taglib/mpeg/id3v2/id3v2tag.cpp b/taglib/mpeg/id3v2/id3v2tag.cpp index 54e63920..a1b169e1 100644 --- a/taglib/mpeg/id3v2/id3v2tag.cpp +++ b/taglib/mpeg/id3v2/id3v2tag.cpp @@ -379,10 +379,12 @@ void ID3v2::Tag::removeUnsupportedProperties(const StringList &properties) for(FrameList::ConstIterator fit = l.begin(); fit != l.end(); fit++) if (dynamic_cast(*fit) != 0) removeFrame(*fit); - } else if(it->size() == 4){ + } + else if(it->size() == 4){ ByteVector id = it->data(String::Latin1); removeFrames(id); - } else { + } + else { ByteVector id = it->substr(0,4).data(String::Latin1); if(it->size() <= 5) continue; // invalid specification @@ -396,6 +398,8 @@ void ID3v2::Tag::removeUnsupportedProperties(const StringList &properties) frame = CommentsFrame::findByDescription(this, description); else if(id == "USLT") frame = UnsynchronizedLyricsFrame::findByDescription(this, description); + else if(id == "UFID") + frame = UniqueFileIdentifierFrame::findByOwner(this, description); if(frame) removeFrame(frame); } diff --git a/taglib/toolkit/tpropertymap.h b/taglib/toolkit/tpropertymap.h index 7f59b21e..8073003e 100644 --- a/taglib/toolkit/tpropertymap.h +++ b/taglib/toolkit/tpropertymap.h @@ -40,6 +40,54 @@ namespace TagLib { * Note that most metadata formats pose additional conditions on the tag keys. The * most popular ones (Vorbis, APE, ID3v2) should support all ASCII only words of * length between 2 and 16. + * + * This class can contain any tags, but here is a list of "well-known" tags that + * you might want to use: + * + * Basic tags: + * + * - TITLE + * - ALBUM + * - ARTIST + * - ALBUMARTIST + * - SUBTITLE + * - TRACKNUMBER + * - DISCNUMBER + * - DATE + * - ORIGINALDATE + * - GENRE + * - COMMENT + * + * Credits: + * + * - COMPOSER + * - LYRICIST + * - CONDUCTOR + * - REMIXER + * - PERFORMER: + * + * Other tags: + * + * - ISRC + * - ASIN + * - BPM + * - COPYRIGHT + * - ENCODEDBY + * - MOOD + * - COMMENT + * + * MusicBrainz identifiers: + * + * - MUSICBRAINZ_RECORDINGID + * - MUSICBRAINZ_RELEASEID + * - MUSICBRAINZ_RELEASEGROUPID + * - MUSICBRAINZ_WORKID + * - MUSICBRAINZ_ARTISTID + * - MUSICBRAINZ_RELEASEARTISTID + * - ACOUSTID_ID + * - ACOUSTID_FINGERPRINT + * - MUSICIP_PUID + * */ class TAGLIB_EXPORT PropertyMap: public SimplePropertyMap diff --git a/tests/test_id3v2.cpp b/tests/test_id3v2.cpp index 99f77101..b48bd68f 100644 --- a/tests/test_id3v2.cpp +++ b/tests/test_id3v2.cpp @@ -624,7 +624,7 @@ public: CPPUNIT_ASSERT_EQUAL(String("A COMMENT"), dict["COMMENT"].front()); CPPUNIT_ASSERT_EQUAL(1u, dict.unsupportedData().size()); - CPPUNIT_ASSERT_EQUAL(String("UFID"), dict.unsupportedData().front()); + CPPUNIT_ASSERT_EQUAL(String("UFID/supermihi@web.de"), dict.unsupportedData().front()); } void testPropertyInterface2() @@ -657,11 +657,23 @@ public: frame5->setText(tmclData); tag.addFrame(frame5); + ID3v2::UniqueFileIdentifierFrame *frame6 = new ID3v2::UniqueFileIdentifierFrame("http://musicbrainz.org", "152454b9-19ba-49f3-9fc9-8fc26545cf41"); + tag.addFrame(frame6); + + ID3v2::UniqueFileIdentifierFrame *frame7 = new ID3v2::UniqueFileIdentifierFrame("http://example.com", "123"); + tag.addFrame(frame7); + + ID3v2::UserTextIdentificationFrame *frame8 = new ID3v2::UserTextIdentificationFrame(); + frame8->setDescription("MusicBrainz Album Id"); + frame8->setText("95c454a5-d7e0-4d8f-9900-db04aca98ab3"); + tag.addFrame(frame8); + PropertyMap properties = tag.properties(); - CPPUNIT_ASSERT_EQUAL(2u, properties.unsupportedData().size()); + CPPUNIT_ASSERT_EQUAL(3u, properties.unsupportedData().size()); CPPUNIT_ASSERT(properties.unsupportedData().contains("TIPL")); CPPUNIT_ASSERT(properties.unsupportedData().contains("APIC")); + CPPUNIT_ASSERT(properties.unsupportedData().contains("UFID/http://example.com")); CPPUNIT_ASSERT(properties.contains("PERFORMER:VIOLIN")); CPPUNIT_ASSERT(properties.contains("PERFORMER:PIANO")); @@ -671,9 +683,17 @@ public: CPPUNIT_ASSERT(properties.contains("LYRICS")); CPPUNIT_ASSERT(properties.contains("LYRICS:TEST")); + CPPUNIT_ASSERT(properties.contains("MUSICBRAINZ_RECORDINGID")); + CPPUNIT_ASSERT_EQUAL(String("152454b9-19ba-49f3-9fc9-8fc26545cf41"), properties["MUSICBRAINZ_RECORDINGID"].front()); + + CPPUNIT_ASSERT(properties.contains("MUSICBRAINZ_RELEASEID")); + CPPUNIT_ASSERT_EQUAL(String("95c454a5-d7e0-4d8f-9900-db04aca98ab3"), properties["MUSICBRAINZ_RELEASEID"].front()); + tag.removeUnsupportedProperties(properties.unsupportedData()); CPPUNIT_ASSERT(tag.frameList("APIC").isEmpty()); CPPUNIT_ASSERT(tag.frameList("TIPL").isEmpty()); + CPPUNIT_ASSERT_EQUAL((ID3v2::UniqueFileIdentifierFrame *)0, ID3v2::UniqueFileIdentifierFrame::findByOwner(&tag, "http://example.com")); + CPPUNIT_ASSERT_EQUAL(frame6, ID3v2::UniqueFileIdentifierFrame::findByOwner(&tag, "http://musicbrainz.org")); } void testDeleteFrame() From c5dade5ee7073ee2d282af3a1fe70b598cb2eba0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Lalinsk=C3=BD?= Date: Wed, 21 Nov 2012 17:24:32 +0100 Subject: [PATCH 63/67] Use names that are consistent with Vorbis Comments --- taglib/mpeg/id3v2/frames/uniquefileidentifierframe.cpp | 2 +- taglib/mpeg/id3v2/id3v2frame.cpp | 6 +++--- taglib/toolkit/tpropertymap.h | 6 +++--- tests/test_id3v2.cpp | 8 ++++---- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/taglib/mpeg/id3v2/frames/uniquefileidentifierframe.cpp b/taglib/mpeg/id3v2/frames/uniquefileidentifierframe.cpp index 0725c729..a0e842e0 100644 --- a/taglib/mpeg/id3v2/frames/uniquefileidentifierframe.cpp +++ b/taglib/mpeg/id3v2/frames/uniquefileidentifierframe.cpp @@ -93,7 +93,7 @@ PropertyMap UniqueFileIdentifierFrame::asProperties() const { PropertyMap map; if(d->owner == "http://musicbrainz.org") { - map.insert("MUSICBRAINZ_RECORDINGID", String(d->identifier)); + map.insert("MUSICBRAINZ_TRACKID", String(d->identifier)); } else { map.unsupportedData().append(frameID() + String("/") + d->owner); diff --git a/taglib/mpeg/id3v2/id3v2frame.cpp b/taglib/mpeg/id3v2/id3v2frame.cpp index 372778b1..37d96846 100644 --- a/taglib/mpeg/id3v2/id3v2frame.cpp +++ b/taglib/mpeg/id3v2/id3v2frame.cpp @@ -127,7 +127,7 @@ Frame *Frame::createTextualFrame(const String &key, const StringList &values) // return frame; } } - if(key == "MUSICBRAINZ_RECORDINGID" && values.size() == 1) { + if(key == "MUSICBRAINZ_TRACKID" && values.size() == 1) { UniqueFileIdentifierFrame *frame = new UniqueFileIdentifierFrame("http://musicbrainz.org", values.front().data(String::UTF8)); return frame; } @@ -394,9 +394,9 @@ static const char *frameTranslation[][2] = { static const TagLib::uint txxxFrameTranslationSize = 7; static const char *txxxFrameTranslation[][2] = { - { "MusicBrainz Album Id", "MUSICBRAINZ_RELEASEID" }, + { "MusicBrainz Album Id", "MUSICBRAINZ_ALBUMID" }, { "MusicBrainz Artist Id", "MUSICBRAINZ_ARTISTID" }, - { "MusicBrainz Album Artist Id", "MUSICBRAINZ_RELEASEARTISTID" }, + { "MusicBrainz Album Artist Id", "MUSICBRAINZ_ALBUMARTISTID" }, { "MusicBrainz Release Group Id", "MUSICBRAINZ_RELEASEGROUPID" }, { "MusicBrainz Work Id", "MUSICBRAINZ_WORKID" }, { "Acoustid Id", "ACOUSTID_ID" }, diff --git a/taglib/toolkit/tpropertymap.h b/taglib/toolkit/tpropertymap.h index 8073003e..c6d19fef 100644 --- a/taglib/toolkit/tpropertymap.h +++ b/taglib/toolkit/tpropertymap.h @@ -78,12 +78,12 @@ namespace TagLib { * * MusicBrainz identifiers: * - * - MUSICBRAINZ_RECORDINGID - * - MUSICBRAINZ_RELEASEID + * - MUSICBRAINZ_TRACKID + * - MUSICBRAINZ_ALBUMID * - MUSICBRAINZ_RELEASEGROUPID * - MUSICBRAINZ_WORKID * - MUSICBRAINZ_ARTISTID - * - MUSICBRAINZ_RELEASEARTISTID + * - MUSICBRAINZ_ALBUMARTISTID * - ACOUSTID_ID * - ACOUSTID_FINGERPRINT * - MUSICIP_PUID diff --git a/tests/test_id3v2.cpp b/tests/test_id3v2.cpp index b48bd68f..48faf306 100644 --- a/tests/test_id3v2.cpp +++ b/tests/test_id3v2.cpp @@ -683,11 +683,11 @@ public: CPPUNIT_ASSERT(properties.contains("LYRICS")); CPPUNIT_ASSERT(properties.contains("LYRICS:TEST")); - CPPUNIT_ASSERT(properties.contains("MUSICBRAINZ_RECORDINGID")); - CPPUNIT_ASSERT_EQUAL(String("152454b9-19ba-49f3-9fc9-8fc26545cf41"), properties["MUSICBRAINZ_RECORDINGID"].front()); + CPPUNIT_ASSERT(properties.contains("MUSICBRAINZ_TRACKID")); + CPPUNIT_ASSERT_EQUAL(String("152454b9-19ba-49f3-9fc9-8fc26545cf41"), properties["MUSICBRAINZ_TRACKID"].front()); - CPPUNIT_ASSERT(properties.contains("MUSICBRAINZ_RELEASEID")); - CPPUNIT_ASSERT_EQUAL(String("95c454a5-d7e0-4d8f-9900-db04aca98ab3"), properties["MUSICBRAINZ_RELEASEID"].front()); + CPPUNIT_ASSERT(properties.contains("MUSICBRAINZ_ALBUMID")); + CPPUNIT_ASSERT_EQUAL(String("95c454a5-d7e0-4d8f-9900-db04aca98ab3"), properties["MUSICBRAINZ_ALBUMID"].front()); tag.removeUnsupportedProperties(properties.unsupportedData()); CPPUNIT_ASSERT(tag.frameList("APIC").isEmpty()); From 1b813d9d6cd4087af86179fd317a2d6fae8c1bfb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Lalinsk=C3=BD?= Date: Wed, 21 Nov 2012 17:26:17 +0100 Subject: [PATCH 64/67] Document sort names --- taglib/toolkit/tpropertymap.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/taglib/toolkit/tpropertymap.h b/taglib/toolkit/tpropertymap.h index c6d19fef..d67eea88 100644 --- a/taglib/toolkit/tpropertymap.h +++ b/taglib/toolkit/tpropertymap.h @@ -58,6 +58,13 @@ namespace TagLib { * - GENRE * - COMMENT * + * Sort names: + * + * - TITLESORT + * - ALBUMSORT + * - ARTISTSORT + * - ALBUMARTISTSORT + * * Credits: * * - COMPOSER From 353eb9f00f35f2e9498968de2cf07ce2f7845f41 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Lalinsk=C3=BD?= Date: Thu, 22 Nov 2012 10:40:22 +0100 Subject: [PATCH 65/67] Implement the PropertyMap interface for MP4 --- taglib/ape/apefile.h | 1 + taglib/mp4/mp4file.cpp | 16 ++++ taglib/mp4/mp4file.h | 16 ++++ taglib/mp4/mp4tag.cpp | 151 +++++++++++++++++++++++++++++++ taglib/mp4/mp4tag.h | 4 + taglib/mpeg/id3v2/id3v2frame.cpp | 2 +- taglib/toolkit/tfile.cpp | 10 +- taglib/toolkit/tpropertymap.h | 3 + tests/test_mp4.cpp | 51 +++++++++++ 9 files changed, 250 insertions(+), 4 deletions(-) diff --git a/taglib/ape/apefile.h b/taglib/ape/apefile.h index 8b187f6a..7be0f21d 100644 --- a/taglib/ape/apefile.h +++ b/taglib/ape/apefile.h @@ -132,6 +132,7 @@ namespace TagLib { * has no tag at all, APE will be created. */ PropertyMap setProperties(const PropertyMap &); + /*! * Returns the APE::Properties for this file. If no audio properties * were read then this will return a null pointer. diff --git a/taglib/mp4/mp4file.cpp b/taglib/mp4/mp4file.cpp index a9543211..f41ee888 100644 --- a/taglib/mp4/mp4file.cpp +++ b/taglib/mp4/mp4file.cpp @@ -29,6 +29,7 @@ #include #include +#include #include "mp4atom.h" #include "mp4tag.h" #include "mp4file.h" @@ -90,6 +91,21 @@ MP4::File::tag() const return d->tag; } +PropertyMap MP4::File::properties() const +{ + return d->tag->properties(); +} + +void MP4::File::removeUnsupportedProperties(const StringList &properties) +{ + d->tag->removeUnsupportedProperties(properties); +} + +PropertyMap MP4::File::setProperties(const PropertyMap &properties) +{ + return d->tag->setProperties(properties); +} + MP4::Properties * MP4::File::audioProperties() const { diff --git a/taglib/mp4/mp4file.h b/taglib/mp4/mp4file.h index 2ed3bea5..17fd5a95 100644 --- a/taglib/mp4/mp4file.h +++ b/taglib/mp4/mp4file.h @@ -88,6 +88,22 @@ namespace TagLib { */ Tag *tag() const; + /*! + * Implements the unified property interface -- export function. + */ + PropertyMap properties() const; + + /*! + * Removes unsupported properties. Forwards to the actual Tag's + * removeUnsupportedProperties() function. + */ + void removeUnsupportedProperties(const StringList &properties); + + /*! + * Implements the unified property interface -- import function. + */ + PropertyMap setProperties(const PropertyMap &); + /*! * Returns the MP4 audio properties for this file. */ diff --git a/taglib/mp4/mp4tag.cpp b/taglib/mp4/mp4tag.cpp index afecf98a..3c22f062 100644 --- a/taglib/mp4/mp4tag.cpp +++ b/taglib/mp4/mp4tag.cpp @@ -29,6 +29,7 @@ #include #include +#include #include "mp4atom.h" #include "mp4tag.h" #include "id3v1genres.h" @@ -759,3 +760,153 @@ MP4::Tag::itemListMap() return d->items; } +static const char *keyTranslation[][2] = { + { "\251nam", "TITLE" }, + { "\251ART", "ARTIST" }, + { "\251alb", "ALBUM" }, + { "\251cmt", "COMMENT" }, + { "\251gen", "GENRE" }, + { "\251day", "DATE" }, + { "\251wrt", "COMPOSER" }, + { "\251grp", "GROUPING" }, + { "trkn", "TRACKNUMBER" }, + { "disk", "DISCNUMBER" }, + { "cpil", "COMPILATION" }, + { "tmpo", "BPM" }, + { "cprt", "COPYRIGHT" }, + { "\251lyr", "LYRICS" }, + { "\251too", "ENCODEDBY" }, + { "soal", "ALBUMSORT" }, + { "soaa", "ALBUMARTISTSORT" }, + { "soar", "ARTISTSORT" }, + { "sonm", "TITLESORT" }, + { "soco", "COMPOSERSORT" }, + { "sosn", "SHOWSORT" }, + { "----:com.apple.iTunes:MusicBrainz Track Id", "MUSICBRAINZ_TRACKID" }, + { "----:com.apple.iTunes:MusicBrainz Artist Id", "MUSICBRAINZ_ARTISTID" }, + { "----:com.apple.iTunes:MusicBrainz Album Id", "MUSICBRAINZ_ALBUMID" }, + { "----:com.apple.iTunes:MusicBrainz Album Artist Id", "MUSICBRAINZ_ALBUMARTISTID" }, + { "----:com.apple.iTunes:MusicBrainz Release Group Id", "MUSICBRAINZ_RELEASEGROUPID" }, + { "----:com.apple.iTunes:MusicBrainz Work Id", "MUSICBRAINZ_WORKID" }, + { "----:com.apple.iTunes:ASIN", "ASIN" }, + { "----:com.apple.iTunes:LABEL", "LABEL" }, + { "----:com.apple.iTunes:LYRICIST", "LYRICIST" }, + { "----:com.apple.iTunes:CONDUCTOR", "CONDUCTOR" }, + { "----:com.apple.iTunes:REMIXER", "REMIXER" }, + { "----:com.apple.iTunes:ENGINEER", "ENGINEER" }, + { "----:com.apple.iTunes:PRODUCER", "PRODUCER" }, + { "----:com.apple.iTunes:DJMIXER", "DJMIXER" }, + { "----:com.apple.iTunes:MIXER", "MIXER" }, + { "----:com.apple.iTunes:SUBTITLE", "SUBTITLE" }, + { "----:com.apple.iTunes:DISCSUBTITLE", "DISCSUBTITLE" }, + { "----:com.apple.iTunes:MOOD", "MOOD" }, + { "----:com.apple.iTunes:ISRC", "ISRC" }, + { "----:com.apple.iTunes:CATALOGNUMBER", "CATALOGNUMBER" }, + { "----:com.apple.iTunes:BARCODE", "BARCODE" }, + { "----:com.apple.iTunes:SCRIPT", "SCRIPT" }, + { "----:com.apple.iTunes:LANGUAGE", "LANGUAGE" }, + { "----:com.apple.iTunes:LICENSE", "LICENSE" }, + { "----:com.apple.iTunes:MEDIA", "MEDIA" }, +}; + +PropertyMap MP4::Tag::properties() const +{ + static Map keyMap; + if(keyMap.isEmpty()) { + int numKeys = sizeof(keyTranslation) / sizeof(keyTranslation[0]); + for(int i = 0; i < numKeys; i++) { + keyMap[keyTranslation[i][0]] = keyTranslation[i][1]; + } + } + + PropertyMap props; + MP4::ItemListMap::ConstIterator it = d->items.begin(); + for(; it != d->items.end(); ++it) { + if(keyMap.contains(it->first)) { + String key = keyMap[it->first]; + if(key == "TRACKNUMBER" || key == "DISCNUMBER") { + MP4::Item::IntPair ip = it->second.toIntPair(); + String value = String::number(ip.first); + if(ip.second) { + value += "/" + String::number(ip.second); + } + props[key] = value; + } + else if(key == "BPM") { + props[key] = String::number(it->second.toInt()); + } + else if(key == "COMPILATION") { + props[key] = String::number(it->second.toBool()); + } + else { + props[key] = it->second.toStringList(); + } + } + else { + props.unsupportedData().append(it->first); + } + } + return props; +} + +void MP4::Tag::removeUnsupportedProperties(const StringList &props) +{ + StringList::ConstIterator it = props.begin(); + for(; it != props.end(); ++it) + d->items.erase(*it); +} + +PropertyMap MP4::Tag::setProperties(const PropertyMap &props) +{ + static Map reverseKeyMap; + if(reverseKeyMap.isEmpty()) { + int numKeys = sizeof(keyTranslation) / sizeof(keyTranslation[0]); + for(int i = 0; i < numKeys; i++) { + reverseKeyMap[keyTranslation[i][1]] = keyTranslation[i][0]; + } + } + + PropertyMap origProps = properties(); + PropertyMap::ConstIterator it = origProps.begin(); + for(; it != origProps.end(); ++it) { + if(!props.contains(it->first) || props[it->first].isEmpty()) { + d->items.erase(reverseKeyMap[it->first]); + } + } + + PropertyMap ignoredProps; + it = props.begin(); + for(; it != props.end(); ++it) { + if(reverseKeyMap.contains(it->first)) { + String name = reverseKeyMap[it->first]; + if(it->first == "TRACKNUMBER" || it->first == "DISCNUMBER") { + int first = 0, second = 0; + StringList parts = StringList::split(it->second.front(), "/"); + if(parts.size() > 0) { + first = parts[0].toInt(); + if(parts.size() > 1) { + second = parts[1].toInt(); + } + d->items[name] = MP4::Item(first, second); + } + } + else if(it->first == "BPM") { + int value = it->second.front().toInt(); + d->items[name] = MP4::Item(value); + } + else if(it->first == "COMPILATION") { + bool value = it->second.front().toInt(); + d->items[name] = MP4::Item(value > 0); + } + else { + d->items[name] = it->second; + } + } + else { + ignoredProps.insert(it->first, it->second); + } + } + + return ignoredProps; +} + diff --git a/taglib/mp4/mp4tag.h b/taglib/mp4/mp4tag.h index b5ea6ebb..0e1d0676 100644 --- a/taglib/mp4/mp4tag.h +++ b/taglib/mp4/mp4tag.h @@ -67,6 +67,10 @@ namespace TagLib { ItemListMap &itemListMap(); + PropertyMap properties() const; + void removeUnsupportedProperties(const StringList& properties); + PropertyMap setProperties(const PropertyMap &properties); + private: AtomDataList parseData2(Atom *atom, TagLib::File *file, int expectedFlags = -1, bool freeForm = false); TagLib::ByteVectorList parseData(Atom *atom, TagLib::File *file, int expectedFlags = -1, bool freeForm = false); diff --git a/taglib/mpeg/id3v2/id3v2frame.cpp b/taglib/mpeg/id3v2/id3v2frame.cpp index 37d96846..f94a074e 100644 --- a/taglib/mpeg/id3v2/id3v2frame.cpp +++ b/taglib/mpeg/id3v2/id3v2frame.cpp @@ -355,7 +355,7 @@ static const char *frameTranslation[][2] = { { "TLAN", "LANGUAGE" }, { "TLEN", "LENGTH" }, //{ "TMCL", "MUSICIANCREDITS" }, handled separately - { "TMED", "MEDIATYPE" }, + { "TMED", "MEDIA" }, { "TMOO", "MOOD" }, { "TOAL", "ORIGINALALBUM" }, { "TOFN", "ORIGINALFILENAME" }, diff --git a/taglib/toolkit/tfile.cpp b/taglib/toolkit/tfile.cpp index 8d7ccdc9..24579496 100644 --- a/taglib/toolkit/tfile.cpp +++ b/taglib/toolkit/tfile.cpp @@ -69,6 +69,7 @@ #include "s3mfile.h" #include "itfile.h" #include "xmfile.h" +#include "mp4file.h" using namespace TagLib; @@ -152,12 +153,11 @@ PropertyMap File::properties() const return dynamic_cast(this)->properties(); if(dynamic_cast(this)) return dynamic_cast(this)->properties(); + if(dynamic_cast(this)) + return dynamic_cast(this)->properties(); // no specialized implementation available -> use generic one // - ASF: ugly format, largely undocumented, not worth implementing // dict interface ... - // - MP4: taglib's MP4::Tag does not really support anything beyond - // the basic implementation, therefor we use just the default Tag - // interface return tag()->properties(); } @@ -193,6 +193,8 @@ void File::removeUnsupportedProperties(const StringList &properties) dynamic_cast(this)->removeUnsupportedProperties(properties); else if(dynamic_cast(this)) dynamic_cast(this)->removeUnsupportedProperties(properties); + else if(dynamic_cast(this)) + dynamic_cast(this)->removeUnsupportedProperties(properties); else tag()->removeUnsupportedProperties(properties); } @@ -231,6 +233,8 @@ PropertyMap File::setProperties(const PropertyMap &properties) return dynamic_cast(this)->setProperties(properties); else if(dynamic_cast(this)) return dynamic_cast(this)->setProperties(properties); + else if(dynamic_cast(this)) + return dynamic_cast(this)->setProperties(properties); else return tag()->setProperties(properties); } diff --git a/taglib/toolkit/tpropertymap.h b/taglib/toolkit/tpropertymap.h index d67eea88..51d5df6f 100644 --- a/taglib/toolkit/tpropertymap.h +++ b/taglib/toolkit/tpropertymap.h @@ -82,6 +82,9 @@ namespace TagLib { * - ENCODEDBY * - MOOD * - COMMENT + * - MEDIA + * - CATALOGNUMBER + * - BARCODE * * MusicBrainz identifiers: * diff --git a/tests/test_mp4.cpp b/tests/test_mp4.cpp index d6830a61..be7ad2c3 100644 --- a/tests/test_mp4.cpp +++ b/tests/test_mp4.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include @@ -25,6 +26,7 @@ class TestMP4 : public CppUnit::TestFixture CPPUNIT_TEST(testCovrRead); CPPUNIT_TEST(testCovrWrite); CPPUNIT_TEST(testCovrRead2); + CPPUNIT_TEST(testProperties); CPPUNIT_TEST_SUITE_END(); public: @@ -224,6 +226,55 @@ public: delete f; } + void testProperties() + { + MP4::File f(TEST_FILE_PATH_C("has-tags.m4a")); + + PropertyMap tags = f.properties(); + + CPPUNIT_ASSERT_EQUAL(StringList("Test Artist"), tags["ARTIST"]); + + tags["TRACKNUMBER"] = StringList("2/4"); + tags["DISCNUMBER"] = StringList("3/5"); + tags["BPM"] = StringList("123"); + tags["ARTIST"] = StringList("Foo Bar"); + tags["COMPILATION"] = StringList("1"); + f.setProperties(tags); + + tags = f.properties(); + + CPPUNIT_ASSERT(f.tag()->itemListMap().contains("trkn")); + CPPUNIT_ASSERT_EQUAL(2, f.tag()->itemListMap()["trkn"].toIntPair().first); + CPPUNIT_ASSERT_EQUAL(4, f.tag()->itemListMap()["trkn"].toIntPair().second); + CPPUNIT_ASSERT_EQUAL(StringList("2/4"), tags["TRACKNUMBER"]); + + CPPUNIT_ASSERT(f.tag()->itemListMap().contains("disk")); + CPPUNIT_ASSERT_EQUAL(3, f.tag()->itemListMap()["disk"].toIntPair().first); + CPPUNIT_ASSERT_EQUAL(5, f.tag()->itemListMap()["disk"].toIntPair().second); + CPPUNIT_ASSERT_EQUAL(StringList("3/5"), tags["DISCNUMBER"]); + + CPPUNIT_ASSERT(f.tag()->itemListMap().contains("tmpo")); + CPPUNIT_ASSERT_EQUAL(123, f.tag()->itemListMap()["tmpo"].toInt()); + CPPUNIT_ASSERT_EQUAL(StringList("123"), tags["BPM"]); + + CPPUNIT_ASSERT(f.tag()->itemListMap().contains("\251ART")); + CPPUNIT_ASSERT_EQUAL(StringList("Foo Bar"), f.tag()->itemListMap()["\251ART"].toStringList()); + CPPUNIT_ASSERT_EQUAL(StringList("Foo Bar"), tags["ARTIST"]); + + CPPUNIT_ASSERT(f.tag()->itemListMap().contains("cpil")); + CPPUNIT_ASSERT_EQUAL(true, f.tag()->itemListMap()["cpil"].toBool()); + CPPUNIT_ASSERT_EQUAL(StringList("1"), tags["COMPILATION"]); + + tags["COMPILATION"] = StringList("0"); + f.setProperties(tags); + + tags = f.properties(); + + CPPUNIT_ASSERT(f.tag()->itemListMap().contains("cpil")); + CPPUNIT_ASSERT_EQUAL(false, f.tag()->itemListMap()["cpil"].toBool()); + CPPUNIT_ASSERT_EQUAL(StringList("0"), tags["COMPILATION"]); + } + }; CPPUNIT_TEST_SUITE_REGISTRATION(TestMP4); From 812f63502b6b1aaf475edb555458b73357eee7f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Lalinsk=C3=BD?= Date: Fri, 23 Nov 2012 09:32:00 +0100 Subject: [PATCH 66/67] Implement the PropertyMap interface for WMA --- taglib/asf/asffile.cpp | 16 ++++ taglib/asf/asffile.h | 16 ++++ taglib/asf/asftag.cpp | 160 +++++++++++++++++++++++++++++++ taglib/asf/asftag.h | 4 + taglib/mpeg/id3v2/id3v2frame.cpp | 2 +- taglib/toolkit/tfile.cpp | 9 +- taglib/toolkit/tpropertymap.h | 1 + tests/test_asf.cpp | 39 +++++++- 8 files changed, 241 insertions(+), 6 deletions(-) diff --git a/taglib/asf/asffile.cpp b/taglib/asf/asffile.cpp index 96b8706f..eb000924 100644 --- a/taglib/asf/asffile.cpp +++ b/taglib/asf/asffile.cpp @@ -29,6 +29,7 @@ #include #include +#include #include #include "asffile.h" #include "asftag.h" @@ -403,6 +404,21 @@ ASF::Tag *ASF::File::tag() const return d->tag; } +PropertyMap ASF::File::properties() const +{ + return d->tag->properties(); +} + +void ASF::File::removeUnsupportedProperties(const StringList &properties) +{ + d->tag->removeUnsupportedProperties(properties); +} + +PropertyMap ASF::File::setProperties(const PropertyMap &properties) +{ + return d->tag->setProperties(properties); +} + ASF::Properties *ASF::File::audioProperties() const { return d->properties; diff --git a/taglib/asf/asffile.h b/taglib/asf/asffile.h index 3a7eef56..7d0e3bc7 100644 --- a/taglib/asf/asffile.h +++ b/taglib/asf/asffile.h @@ -90,6 +90,22 @@ namespace TagLib { */ virtual Tag *tag() const; + /*! + * Implements the unified property interface -- export function. + */ + PropertyMap properties() const; + + /*! + * Removes unsupported properties. Forwards to the actual Tag's + * removeUnsupportedProperties() function. + */ + void removeUnsupportedProperties(const StringList &properties); + + /*! + * Implements the unified property interface -- import function. + */ + PropertyMap setProperties(const PropertyMap &); + /*! * Returns the ASF audio properties for this file. */ diff --git a/taglib/asf/asftag.cpp b/taglib/asf/asftag.cpp index b7b541fc..1cbd16eb 100644 --- a/taglib/asf/asftag.cpp +++ b/taglib/asf/asftag.cpp @@ -27,6 +27,7 @@ #include #endif +#include #include "asftag.h" using namespace TagLib; @@ -196,3 +197,162 @@ bool ASF::Tag::isEmpty() const d->attributeListMap.isEmpty(); } +static const char *keyTranslation[][2] = { + { "WM/AlbumTitle", "ALBUM" }, + { "WM/Composer", "COMPOSER" }, + { "WM/Writer", "WRITER" }, + { "WM/Conductor", "CONDUCTOR" }, + { "WM/ModifiedBy", "REMIXER" }, + { "WM/Year", "DATE" }, + { "WM/OriginalReleaseYear", "ORIGINALDATE" }, + { "WM/Producer", "PRODUCER" }, + { "WM/ContentGroupDescription", "GROUPING" }, + { "WM/SubTitle", "SUBTITLE" }, + { "WM/SetSubTitle", "DISCSUBTITLE" }, + { "WM/TrackNumber", "TRACKNUMBER" }, + { "WM/PartOfSet", "DISCNUMBER" }, + { "WM/Genre", "GENRE" }, + { "WM/BeatsPerMinute", "BPM" }, + { "WM/Mood", "MOOD" }, + { "WM/ISRC", "ISRC" }, + { "WM/Lyrics", "LYRICS" }, + { "WM/Media", "MEDIA" }, + { "WM/Publisher", "LABEL" }, + { "WM/CatalogNo", "CATALOGNUMBER" }, + { "WM/Barcode", "BARCODE" }, + { "WM/EncodedBy", "ENCODEDBY" }, + { "WM/AlbumSortOrder", "ALBUMSORT" }, + { "WM/AlbumArtistSortOrder", "ALBUMARTISTSORT" }, + { "WM/ArtistSortOrder", "ARTISTSORT" }, + { "WM/TitleSortOrder", "TITLESORT" }, + { "WM/Script", "SCRIPT" }, + { "WM/Language", "LANGUAGE" }, + { "MusicBrainz/Track Id", "MUSICBRAINZ_TRACKID" }, + { "MusicBrainz/Artist Id", "MUSICBRAINZ_ARTISTID" }, + { "MusicBrainz/Album Id", "MUSICBRAINZ_ALBUMID" }, + { "MusicBrainz/Album Artist Id", "MUSICBRAINZ_ALBUMARTISTID" }, + { "MusicBrainz/Release Group Id", "MUSICBRAINZ_RELEASEGROUPID" }, + { "MusicBrainz/Work Id", "MUSICBRAINZ_WORKID" }, + { "MusicIP/PUID", "MUSICIP_PUID" }, + { "Acoustid/Id", "ACOUSTID_ID" }, + { "Acoustid/Fingerprint", "ACOUSTID_FINGERPRINT" }, +}; + +PropertyMap ASF::Tag::properties() const +{ + static Map keyMap; + if(keyMap.isEmpty()) { + int numKeys = sizeof(keyTranslation) / sizeof(keyTranslation[0]); + for(int i = 0; i < numKeys; i++) { + keyMap[keyTranslation[i][0]] = keyTranslation[i][1]; + } + } + + PropertyMap props; + + if(!d->title.isEmpty()) { + props["TITLE"] = d->title; + } + if(!d->artist.isEmpty()) { + props["ARTIST"] = d->artist; + } + if(!d->copyright.isEmpty()) { + props["COPYRIGHT"] = d->copyright; + } + if(!d->comment.isEmpty()) { + props["COMMENT"] = d->comment; + } + + ASF::AttributeListMap::ConstIterator it = d->attributeListMap.begin(); + for(; it != d->attributeListMap.end(); ++it) { + if(keyMap.contains(it->first)) { + String key = keyMap[it->first]; + AttributeList::ConstIterator it2 = it->second.begin(); + for(; it2 != it->second.end(); ++it2) { + if(key == "TRACKNUMBER") { + if(it2->type() == ASF::Attribute::DWordType) + props.insert(key, String::number(it2->toUInt())); + else + props.insert(key, it2->toString()); + } + else { + props.insert(key, it2->toString()); + } + } + } + else { + props.unsupportedData().append(it->first); + } + } + return props; +} + +void ASF::Tag::removeUnsupportedProperties(const StringList &props) +{ + StringList::ConstIterator it = props.begin(); + for(; it != props.end(); ++it) + d->attributeListMap.erase(*it); +} + +PropertyMap ASF::Tag::setProperties(const PropertyMap &props) +{ + static Map reverseKeyMap; + if(reverseKeyMap.isEmpty()) { + int numKeys = sizeof(keyTranslation) / sizeof(keyTranslation[0]); + for(int i = 0; i < numKeys; i++) { + reverseKeyMap[keyTranslation[i][1]] = keyTranslation[i][0]; + } + } + + PropertyMap origProps = properties(); + PropertyMap::ConstIterator it = origProps.begin(); + for(; it != origProps.end(); ++it) { + if(!props.contains(it->first) || props[it->first].isEmpty()) { + if(it->first == "TITLE") { + d->title = String::null; + } + else if(it->first == "ARTIST") { + d->artist = String::null; + } + else if(it->first == "COMMENT") { + d->comment = String::null; + } + else if(it->first == "COPYRIGHT") { + d->copyright = String::null; + } + else { + d->attributeListMap.erase(reverseKeyMap[it->first]); + } + } + } + + PropertyMap ignoredProps; + it = props.begin(); + for(; it != props.end(); ++it) { + if(reverseKeyMap.contains(it->first)) { + String name = reverseKeyMap[it->first]; + removeItem(name); + StringList::ConstIterator it2 = it->second.begin(); + for(; it2 != it->second.end(); ++it2) { + addAttribute(name, *it2); + } + } + else if(it->first == "TITLE") { + d->title = it->second.toString(); + } + else if(it->first == "ARTIST") { + d->artist = it->second.toString(); + } + else if(it->first == "COMMENT") { + d->comment = it->second.toString(); + } + else if(it->first == "COPYRIGHT") { + d->copyright = it->second.toString(); + } + else { + ignoredProps.insert(it->first, it->second); + } + } + + return ignoredProps; +} diff --git a/taglib/asf/asftag.h b/taglib/asf/asftag.h index 6b2d2bf3..f68579d8 100644 --- a/taglib/asf/asftag.h +++ b/taglib/asf/asftag.h @@ -176,6 +176,10 @@ namespace TagLib { */ void addAttribute(const String &name, const Attribute &attribute); + PropertyMap properties() const; + void removeUnsupportedProperties(const StringList& properties); + PropertyMap setProperties(const PropertyMap &properties); + private: class TagPrivate; diff --git a/taglib/mpeg/id3v2/id3v2frame.cpp b/taglib/mpeg/id3v2/id3v2frame.cpp index f94a074e..e2b84311 100644 --- a/taglib/mpeg/id3v2/id3v2frame.cpp +++ b/taglib/mpeg/id3v2/id3v2frame.cpp @@ -368,7 +368,7 @@ static const char *frameTranslation[][2] = { { "TPE4", "REMIXER" }, // could also be ARRANGER { "TPOS", "DISCNUMBER" }, { "TPRO", "PRODUCEDNOTICE" }, - { "TPUB", "PUBLISHER" }, + { "TPUB", "LABEL" }, { "TRCK", "TRACKNUMBER" }, { "TRSN", "RADIOSTATION" }, { "TRSO", "RADIOSTATIONOWNER" }, diff --git a/taglib/toolkit/tfile.cpp b/taglib/toolkit/tfile.cpp index 24579496..7251f3f7 100644 --- a/taglib/toolkit/tfile.cpp +++ b/taglib/toolkit/tfile.cpp @@ -155,9 +155,8 @@ PropertyMap File::properties() const return dynamic_cast(this)->properties(); if(dynamic_cast(this)) return dynamic_cast(this)->properties(); - // no specialized implementation available -> use generic one - // - ASF: ugly format, largely undocumented, not worth implementing - // dict interface ... + if(dynamic_cast(this)) + return dynamic_cast(this)->properties(); return tag()->properties(); } @@ -195,6 +194,8 @@ void File::removeUnsupportedProperties(const StringList &properties) dynamic_cast(this)->removeUnsupportedProperties(properties); else if(dynamic_cast(this)) dynamic_cast(this)->removeUnsupportedProperties(properties); + else if(dynamic_cast(this)) + dynamic_cast(this)->removeUnsupportedProperties(properties); else tag()->removeUnsupportedProperties(properties); } @@ -235,6 +236,8 @@ PropertyMap File::setProperties(const PropertyMap &properties) return dynamic_cast(this)->setProperties(properties); else if(dynamic_cast(this)) return dynamic_cast(this)->setProperties(properties); + else if(dynamic_cast(this)) + return dynamic_cast(this)->setProperties(properties); else return tag()->setProperties(properties); } diff --git a/taglib/toolkit/tpropertymap.h b/taglib/toolkit/tpropertymap.h index 51d5df6f..2be49ddb 100644 --- a/taglib/toolkit/tpropertymap.h +++ b/taglib/toolkit/tpropertymap.h @@ -83,6 +83,7 @@ namespace TagLib { * - MOOD * - COMMENT * - MEDIA + * - LABEL * - CATALOGNUMBER * - BARCODE * diff --git a/tests/test_asf.cpp b/tests/test_asf.cpp index 0cd1f3d7..8610c24b 100644 --- a/tests/test_asf.cpp +++ b/tests/test_asf.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include "utils.h" @@ -13,7 +14,7 @@ using namespace TagLib; class TestASF : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(TestASF); - CPPUNIT_TEST(testProperties); + CPPUNIT_TEST(testAudioProperties); CPPUNIT_TEST(testRead); CPPUNIT_TEST(testSaveMultipleValues); CPPUNIT_TEST(testSaveStream); @@ -22,11 +23,12 @@ class TestASF : public CppUnit::TestFixture CPPUNIT_TEST(testSaveLargeValue); CPPUNIT_TEST(testSavePicture); CPPUNIT_TEST(testSaveMultiplePictures); + CPPUNIT_TEST(testProperties); CPPUNIT_TEST_SUITE_END(); public: - void testProperties() + void testAudioProperties() { ASF::File f(TEST_FILE_PATH_C("silence-1.wma")); CPPUNIT_ASSERT_EQUAL(4, f.audioProperties()->length()); @@ -215,6 +217,39 @@ public: delete f; } + void testProperties() + { + ASF::File f(TEST_FILE_PATH_C("silence-1.wma")); + + PropertyMap tags = f.properties(); + + tags["TRACKNUMBER"] = StringList("2"); + tags["DISCNUMBER"] = StringList("3"); + tags["BPM"] = StringList("123"); + tags["ARTIST"] = StringList("Foo Bar"); + f.setProperties(tags); + + tags = f.properties(); + + CPPUNIT_ASSERT_EQUAL(String("Foo Bar"), f.tag()->artist()); + CPPUNIT_ASSERT_EQUAL(StringList("Foo Bar"), tags["ARTIST"]); + + CPPUNIT_ASSERT(f.tag()->attributeListMap().contains("WM/BeatsPerMinute")); + CPPUNIT_ASSERT_EQUAL(1u, f.tag()->attributeListMap()["WM/BeatsPerMinute"].size()); + CPPUNIT_ASSERT_EQUAL(String("123"), f.tag()->attributeListMap()["WM/BeatsPerMinute"].front().toString()); + CPPUNIT_ASSERT_EQUAL(StringList("123"), tags["BPM"]); + + CPPUNIT_ASSERT(f.tag()->attributeListMap().contains("WM/TrackNumber")); + CPPUNIT_ASSERT_EQUAL(1u, f.tag()->attributeListMap()["WM/TrackNumber"].size()); + CPPUNIT_ASSERT_EQUAL(String("2"), f.tag()->attributeListMap()["WM/TrackNumber"].front().toString()); + CPPUNIT_ASSERT_EQUAL(StringList("2"), tags["TRACKNUMBER"]); + + CPPUNIT_ASSERT(f.tag()->attributeListMap().contains("WM/PartOfSet")); + CPPUNIT_ASSERT_EQUAL(1u, f.tag()->attributeListMap()["WM/PartOfSet"].size()); + CPPUNIT_ASSERT_EQUAL(String("3"), f.tag()->attributeListMap()["WM/PartOfSet"].front().toString()); + CPPUNIT_ASSERT_EQUAL(StringList("3"), tags["DISCNUMBER"]); + } + }; CPPUNIT_TEST_SUITE_REGISTRATION(TestASF); From 3fa295d99ded94d90cabc0dfe7737b7e8f617d9d Mon Sep 17 00:00:00 2001 From: naota Date: Wed, 28 Nov 2012 07:54:08 +0900 Subject: [PATCH 67/67] Include sys/stat.h to define S_* properly Without including sys/stat.h, this file failed to build on FreeBSD with the following error. In file included from /var/tmp/portage/media-libs/taglib-1.8/work/taglib-1.8/tests/test_trueaudio.cpp:5:0: /var/tmp/portage/media-libs/taglib-1.8/work/taglib-1.8/tests/utils.h: In function 'std::string copyFile(const string&, const string&)': /var/tmp/portage/media-libs/taglib-1.8/work/taglib-1.8/tests/utils.h:36:62: error: 'S_IRUSR' was not declared in this scope /var/tmp/portage/media-libs/taglib-1.8/work/taglib-1.8/tests/utils.h:36:72: error: 'S_IWUSR' was not declared in this scope In file included from /var/tmp/portage/media-libs/taglib-1.8/work/taglib-1.8/tests/test_mpeg.cpp:6:0: /var/tmp/portage/media-libs/taglib-1.8/work/taglib-1.8/tests/utils.h: In function 'std::string copyFile(const string&, const string&)': /var/tmp/portage/media-libs/taglib-1.8/work/taglib-1.8/tests/utils.h:36:62: error: 'S_IRUSR' was not declared in this scope /var/tmp/portage/media-libs/taglib-1.8/work/taglib-1.8/tests/utils.h:36:72: error: 'S_IWUSR' was not declared in this scope gmake[2]: *** [tests/CMakeFiles/test_runner.dir/test_mpeg.cpp.o] Error 1 --- tests/utils.h | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/utils.h b/tests/utils.h index 39e15ce9..b69bfa50 100644 --- a/tests/utils.h +++ b/tests/utils.h @@ -7,6 +7,7 @@ #include #include #include +#include #endif #include #include