diff --git a/mpeg/Makefile.am b/mpeg/Makefile.am index cbec23c3..acc3614e 100644 --- a/mpeg/Makefile.am +++ b/mpeg/Makefile.am @@ -2,6 +2,7 @@ SUBDIRS = id3v1 id3v2 INCLUDES = \ -I$(top_srcdir)/taglib\ -I$(top_srcdir)/taglib/toolkit \ + -I$(top_srcdir)/taglib/ape \ -I$(top_srcdir)/taglib/mpeg/id3v2 -I./id3v2 \ -I$(top_srcdir)/taglib/mpeg/id3v1 -I./id3v1 \ $(all_includes) diff --git a/mpeg/mpegfile.cpp b/mpeg/mpegfile.cpp index 3ecc1625..4f2da521 100644 --- a/mpeg/mpegfile.cpp +++ b/mpeg/mpegfile.cpp @@ -22,6 +22,8 @@ #include #include #include +#include +#include #include #include @@ -172,11 +174,15 @@ public: ID3v2Tag(0), ID3v2Location(-1), ID3v2OriginalSize(0), + APETag(0), + APELocation(-1), + APEOriginalSize(0), ID3v1Tag(0), ID3v1Location(-1), tag(0), hasID3v2(false), hasID3v1(false), + hasAPE(false), properties(0) {} ~FilePrivate() { @@ -191,16 +197,21 @@ public: long ID3v2Location; uint ID3v2OriginalSize; + APE::Tag *APETag; + long APELocation; + uint APEOriginalSize; + ID3v1::Tag *ID3v1Tag; long ID3v1Location; MPEGTag *tag; - // These indicate whether the file *on disk* has an ID3v[1/2] tag, not if + // These indicate whether the file *on disk* has these tags, not if // this data structure does. This is used in computing offsets. bool hasID3v2; bool hasID3v1; + bool hasAPE; Properties *properties; }; @@ -247,7 +258,7 @@ MPEG::Properties *MPEG::File::audioProperties() const bool MPEG::File::save() { - return save(ID3v1 | ID3v2); + return save(AllTags); } bool MPEG::File::save(int tags) @@ -255,9 +266,9 @@ bool MPEG::File::save(int tags) if(tags == NoTags) return strip(AllTags); - if(!d->ID3v2Tag && !d->ID3v1Tag) { + if(!d->ID3v2Tag && !d->ID3v1Tag && !d->APETag) { - if(d->hasID3v1 || d->hasID3v2) + if(d->hasID3v1 || d->hasID3v2 || d->hasAPE) return strip(AllTags); return true; @@ -271,10 +282,10 @@ bool MPEG::File::save(int tags) // Create the tags if we've been asked to. Copy the values from the tag that // does exist into the new tag. - if(tags & ID3v2 && d->ID3v1Tag) + if((tags & ID3v2) && d->ID3v1Tag) Tag::duplicate(d->ID3v1Tag, ID3v2Tag(true), false); - if(tags & ID3v1 && d->ID3v2Tag) + if((tags & ID3v1) && d->ID3v2Tag) Tag::duplicate(d->ID3v2Tag, ID3v1Tag(true), false); bool success = true; @@ -306,6 +317,30 @@ bool MPEG::File::save(int tags) else if(d->hasID3v1) success = strip(ID3v1, false) && success; + // Dont save an APE-tag unless one has been created + if((APE & tags) && d->APETag) { + if(d->hasAPE) + insert(d->APETag->render(), d->APELocation, d->APEOriginalSize); + else { + if(d->hasID3v1) { + insert(d->APETag->render(), d->ID3v1Location, 0); + d->APEOriginalSize = d->APETag->footer()->completeTagSize(); + d->hasAPE = true; + d->APELocation = d->ID3v1Location; + d->ID3v1Location += d->APEOriginalSize; + } + else { + seek(0, End); + d->APELocation = tell(); + writeBlock(d->APETag->render()); + d->APEOriginalSize = d->APETag->footer()->completeTagSize(); + d->hasAPE = true; + } + } + } + else if(d->hasAPE) + success = strip(APE, false) && success; + return success; } @@ -331,6 +366,17 @@ ID3v1::Tag *MPEG::File::ID3v1Tag(bool create) return d->ID3v1Tag; } +APE::Tag *MPEG::File::APETag(bool create) +{ + if(!create || d->APETag) + return d->APETag; + + // no APE tag exists and we've been asked to create one + + d->APETag = new APE::Tag; + return d->APETag; +} + bool MPEG::File::strip(int tags) { return strip(tags, true); @@ -343,7 +389,7 @@ bool MPEG::File::strip(int tags, bool freeMemory) return false; } - if(tags & ID3v2 && d->hasID3v2) { + if((tags & ID3v2) && d->hasID3v2) { removeBlock(d->ID3v2Location, d->ID3v2OriginalSize); d->ID3v2Location = -1; d->ID3v2OriginalSize = 0; @@ -358,7 +404,7 @@ bool MPEG::File::strip(int tags, bool freeMemory) d->ID3v1Location = findID3v1(); } - if(tags & ID3v1 && d->hasID3v1) { + if((tags & ID3v1) && d->hasID3v1) { truncate(d->ID3v1Location); d->ID3v1Location = -1; d->hasID3v1 = false; @@ -368,6 +414,20 @@ bool MPEG::File::strip(int tags, bool freeMemory) } } + if((tags & APE) && d->hasAPE) { + removeBlock(d->APELocation, d->APEOriginalSize); + d->APELocation = -1; + d->hasAPE = false; + if(d->hasID3v1) { + if (d->ID3v1Location > d->APELocation) + d->ID3v1Location -= d->APEOriginalSize; + } + if(freeMemory) { + delete d->APETag; + d->APETag = 0; + } + } + return true; } @@ -468,6 +528,19 @@ void MPEG::File::read(bool readProperties, Properties::ReadStyle propertiesStyle d->hasID3v1 = true; } + // Look for an APE tag + + d->APELocation = findAPE(); + + if(d->APELocation >= 0) { + + d->APETag = new APE::Tag(this, d->APELocation); + + d->APEOriginalSize = d->APETag->footer()->completeTagSize(); + + d->hasAPE = true; + } + if(readProperties) d->properties = new Properties(this, propertiesStyle); } @@ -595,6 +668,22 @@ long MPEG::File::findID3v1() return -1; } +long MPEG::File::findAPE() +{ + if(isValid()) { + if (d->hasID3v1) + seek(-160, End); + else + seek(-32, End); + + long p = tell(); + + if(readBlock(8) == APE::Tag::fileIdentifier()) + return p; + } + return -1; +} + bool MPEG::File::secondSynchByte(char byte) { if(uchar(byte) == 0xff) diff --git a/mpeg/mpegfile.h b/mpeg/mpegfile.h index 334b58d1..431523e0 100644 --- a/mpeg/mpegfile.h +++ b/mpeg/mpegfile.h @@ -30,6 +30,7 @@ namespace TagLib { namespace ID3v2 { class Tag; class FrameFactory; } namespace ID3v1 { class Tag; } + namespace APE { class Tag; } //! An implementation of TagLib::File with MPEG (MP3) specific methods @@ -57,6 +58,8 @@ namespace TagLib { ID3v1 = 0x0001, //! Matches ID3v2 tags. ID3v2 = 0x0002, + //! Matches APE tags. + APE = 0x0004, //! Matches all tag types. AllTags = 0xffff }; @@ -103,6 +106,7 @@ namespace TagLib { * * \see ID3v1Tag() * \see ID3v2Tag() + * \see APETag() */ virtual Tag *tag() const; @@ -166,6 +170,19 @@ namespace TagLib { */ ID3v1::Tag *ID3v1Tag(bool create = false); + /*! + * Returns a pointer to the APE tag of the file. + * + * If \a create is false (the default) this will return a null pointer + * if there is no valid APE tag. If \a create is true it will create + * an APE tag if one does not exist. + * + * \note The Tag is still owned by the MPEG::File and should not be + * deleted by the user. It will be deleted when the file (object) is + * destroyed. + */ + APE::Tag *APETag(bool create = false); + /*! * This will strip the tags that match the OR-ed together TagTypes from the * file. By default it strips all tags. It returns true if the tags are @@ -173,7 +190,7 @@ namespace TagLib { * * This is equivalent to strip(tags, true) * - * \note This will also invalidate pointers to the ID3v2 and ID3v1 tags + * \note This will also invalidate pointers to the ID3 and APE tags * as their memory will be freed. */ bool strip(int tags = AllTags); @@ -183,7 +200,7 @@ namespace TagLib { * file. By default it strips all tags. It returns true if the tags are * successfully stripped. * - * If \a freeMemory is true the ID3v1 and ID3v2 tags will be deleted and + * If \a freeMemory is true the ID3 and APE tags will be deleted and * pointers to them will be invalidated. */ // BIC: merge with the method above @@ -225,6 +242,7 @@ namespace TagLib { void read(bool readProperties, Properties::ReadStyle propertiesStyle); long findID3v2(); long findID3v1(); + long findAPE(); /*! * MPEG frames can be recognized by the bit pattern 11111111 111, so the