TagLib: Implement support for APE tags on TTA

TrueAudio will now read APE tags, and if I should start writing tags
some day, will prefer creating APE tags if no tags exist.

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
CQTexperiment
Christopher Snowhill 2022-02-07 15:33:50 -08:00
parent 91da112e35
commit 0b8a659bc2
2 changed files with 105 additions and 7 deletions

View File

@ -39,12 +39,14 @@
#include "id3v1tag.h"
#include "id3v2tag.h"
#include "id3v2header.h"
#include "apefooter.h"
#include "apetag.h"
using namespace TagLib;
namespace
{
enum { TrueAudioID3v2Index = 0, TrueAudioID3v1Index = 1 };
enum { TrueAudioID3v2Index = 0, TrueAudioAPEIndex = 1, TrueAudioID3v1Index = 2 };
}
class TrueAudio::File::FilePrivate
@ -55,6 +57,8 @@ public:
ID3v2Location(-1),
ID3v2OriginalSize(0),
ID3v1Location(-1),
APELocation(-1),
APEOriginalSize(0),
properties(0) {}
~FilePrivate()
@ -66,6 +70,9 @@ public:
long ID3v2Location;
long ID3v2OriginalSize;
long APELocation;
long APEOriginalSize;
long ID3v1Location;
TagUnion tag;
@ -148,7 +155,10 @@ PropertyMap TrueAudio::File::setProperties(const PropertyMap &properties)
if(ID3v1Tag())
ID3v1Tag()->setProperties(properties);
return ID3v2Tag(true)->setProperties(properties);
if(ID3v2Tag())
ID3v2Tag()->setProperties(properties);
return APETag(true)->setProperties(properties);
}
TrueAudio::Properties *TrueAudio::File::audioProperties() const
@ -180,6 +190,9 @@ bool TrueAudio::File::save()
const ByteVector data = ID3v2Tag()->render();
insert(data, d->ID3v2Location, d->ID3v2OriginalSize);
if(d->APELocation >= 0)
d->APELocation += (static_cast<long>(data.size()) - d->ID3v2OriginalSize);
if(d->ID3v1Location >= 0)
d->ID3v1Location += (static_cast<long>(data.size()) - d->ID3v2OriginalSize);
@ -192,6 +205,9 @@ bool TrueAudio::File::save()
if(d->ID3v2Location >= 0) {
removeBlock(d->ID3v2Location, d->ID3v2OriginalSize);
if(d->APELocation >= 0)
d->APELocation -= d->ID3v2OriginalSize;
if(d->ID3v1Location >= 0)
d->ID3v1Location -= d->ID3v2OriginalSize;
@ -226,6 +242,35 @@ bool TrueAudio::File::save()
}
}
if(APETag() && !APETag()->isEmpty()) {
// APE tag is not empty. Update the old one or create a new one.
if(d->APELocation < 0) {
if(d->ID3v1Location >= 0)
d->APELocation = d->ID3v1Location;
else
d->APELocation = length();
}
const ByteVector data = APETag()->render();
insert(data, d->APELocation, d->APEOriginalSize);
if(d->ID3v1Location >= 0)
d->ID3v1Location += (static_cast<long>(data.size()) - d->APEOriginalSize);
d->APEOriginalSize = data.size();
}
else {
// APE tag is empty. Remove the old one.
if(d->APELocation >= 0) {
removeBlock(d->APELocation, d->APEOriginalSize);
if (d->ID3v1Location >= 0) {
d->ID3v1Location -= d->APEOriginalSize;
}
}
}
return true;
}
@ -239,6 +284,11 @@ ID3v2::Tag *TrueAudio::File::ID3v2Tag(bool create)
return d->tag.access<ID3v2::Tag>(TrueAudioID3v2Index, create);
}
APE::Tag *TrueAudio::File::APETag(bool create)
{
return d->tag.access<APE::Tag>(TrueAudioAPEIndex, create);
}
void TrueAudio::File::strip(int tags)
{
if(tags & ID3v1)
@ -247,8 +297,11 @@ void TrueAudio::File::strip(int tags)
if(tags & ID3v2)
d->tag.set(TrueAudioID3v2Index, 0);
if(tags & APE)
d->tag.set(TrueAudioAPEIndex, 0);
if(!ID3v1Tag())
ID3v2Tag(true);
APETag(true);
}
bool TrueAudio::File::hasID3v1Tag() const
@ -261,6 +314,11 @@ bool TrueAudio::File::hasID3v2Tag() const
return (d->ID3v2Location >= 0);
}
bool TrueAudio::File::hasAPETag() const
{
return (d->APELocation >= 0);
}
////////////////////////////////////////////////////////////////////////////////
// private members
////////////////////////////////////////////////////////////////////////////////
@ -284,7 +342,17 @@ void TrueAudio::File::read(bool readProperties)
d->tag.set(TrueAudioID3v1Index, new ID3v1::Tag(this, d->ID3v1Location));
if(d->ID3v1Location < 0)
ID3v2Tag(true);
APETag(true);
// Look for an APE tag
d->APELocation = Utils::findAPE(this, d->ID3v1Location);
if(d->APELocation >= 0) {
d->tag.set(TrueAudioAPEIndex, new APE::Tag(this, d->APELocation));
d->APEOriginalSize = APETag()->footer()->completeTagSize();
d->APELocation = d->APELocation + APE::Footer::size() - d->APEOriginalSize;
}
// Look for TrueAudio metadata

View File

@ -39,13 +39,14 @@ namespace TagLib {
namespace ID3v2 { class Tag; class FrameFactory; }
namespace ID3v1 { class Tag; }
namespace APE { class Tag; }
//! An implementation of TrueAudio metadata
/*!
* This is implementation of TrueAudio metadata.
*
* This supports ID3v1 and ID3v2 tags as well as reading stream
* This supports ID3v1, ID3v2, and APE tags as well as reading stream
* properties from the file.
*/
@ -74,6 +75,8 @@ namespace TagLib {
ID3v1 = 0x0001,
//! Matches ID3v2 tags.
ID3v2 = 0x0002,
//! Matches APE tags.
APE = 0x0004,
//! Matches all tag types.
AllTags = 0xffff
};
@ -141,13 +144,14 @@ namespace TagLib {
/*!
* Implements the unified property interface -- export function.
* If the file contains both ID3v1 and v2 tags, only ID3v2 will be
* converted to the PropertyMap.
* converted to the PropertyMap. If the file contains APE tags,
* only they will be converted to the PropertyMap.
*/
PropertyMap properties() const;
/*!
* Implements the unified property interface -- import function.
* Creates in ID3v2 tag if necessary. If an ID3v1 tag exists, it will
* Creates an APE tag if necessary. If an ID3v1 tag exists, it will
* be updated as well, within the limitations of ID3v1.
*/
PropertyMap setProperties(const PropertyMap &);
@ -211,6 +215,25 @@ namespace TagLib {
*/
ID3v2::Tag *ID3v2Tag(bool create = false);
/*!
* Returns a pointer to the APE tag of the file.
*
* If \a create is false (the default) this may 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 and returns a valid pointer.
*
* \note This may return a valid pointer regardless of whether or not the
* file on disk has an APE tag. Use hasAPETag() to check if the file
* on disk actually has an APE tag.
*
* \note The Tag <b>is still</b> owned by the TTA::File and should not be
* deleted by the user. It will be deleted when the file (object) is
* destroyed.
*
* \see hasAPETag()
*/
APE::Tag *APETag(bool create = false);
/*!
* This will remove the tags that match the OR-ed together TagTypes from the
* file. By default it removes all tags.
@ -235,6 +258,13 @@ namespace TagLib {
*/
bool hasID3v2Tag() const;
/*!
* Returns whether or not the file on disk actually has an APE tag.
*
* \see APETag()
*/
bool hasAPETag() const;
/*!
* Returns whether or not the given \a stream can be opened as a TrueAudio
* file.