TagLib: Add generic APE tag support, for TAK files for now

CQTexperiment
Christopher Snowhill 2021-04-10 16:20:55 -07:00
parent 9dd8dbe2f3
commit e603addc07
5 changed files with 427 additions and 0 deletions

View File

@ -152,6 +152,8 @@
4872B8881A675CCB00674347 /* libiconv.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 4872B8871A675CCB00674347 /* libiconv.dylib */; };
83790D241809E8CA0073CF51 /* opusfile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83790D201809E8CA0073CF51 /* opusfile.cpp */; };
83790D261809E8CA0073CF51 /* opusproperties.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83790D221809E8CA0073CF51 /* opusproperties.cpp */; };
83AF2CBE2622643300538240 /* apegenfile.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83AF2CBC2622643300538240 /* apegenfile.cpp */; };
83AF2CBF2622643300538240 /* apegenfile.h in Headers */ = {isa = PBXBuildFile; fileRef = 83AF2CBD2622643300538240 /* apegenfile.h */; };
8DC2EF530486A6940098B216 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 089C1666FE841158C02AAC07 /* InfoPlist.strings */; };
EDE862FD25CF6BD70086EFD3 /* tpropertymap.cpp in Sources */ = {isa = PBXBuildFile; fileRef = EDE862FC25CF6BD60086EFD3 /* tpropertymap.cpp */; };
EDE8630225CF6C260086EFD3 /* tfilestream.cpp in Sources */ = {isa = PBXBuildFile; fileRef = EDE8630025CF6C260086EFD3 /* tfilestream.cpp */; };
@ -362,6 +364,8 @@
83790D211809E8CA0073CF51 /* opusfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opusfile.h; sourceTree = "<group>"; };
83790D221809E8CA0073CF51 /* opusproperties.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = opusproperties.cpp; sourceTree = "<group>"; };
83790D231809E8CA0073CF51 /* opusproperties.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = opusproperties.h; sourceTree = "<group>"; };
83AF2CBC2622643300538240 /* apegenfile.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = apegenfile.cpp; sourceTree = "<group>"; };
83AF2CBD2622643300538240 /* apegenfile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = apegenfile.h; sourceTree = "<group>"; };
8DC2EF5A0486A6940098B216 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist; path = Info.plist; sourceTree = "<group>"; };
8DC2EF5B0486A6940098B216 /* TagLib.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = TagLib.framework; sourceTree = BUILT_PRODUCTS_DIR; };
EDE862FC25CF6BD60086EFD3 /* tpropertymap.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = tpropertymap.cpp; sourceTree = "<group>"; };
@ -586,6 +590,8 @@
32AE59A514E70ED600420CA0 /* apefile.h */,
32AE59A614E70ED600420CA0 /* apefooter.cpp */,
32AE59A714E70ED600420CA0 /* apefooter.h */,
83AF2CBC2622643300538240 /* apegenfile.cpp */,
83AF2CBD2622643300538240 /* apegenfile.h */,
32AE59A814E70ED600420CA0 /* apeitem.cpp */,
32AE59A914E70ED600420CA0 /* apeitem.h */,
32AE59AA14E70ED600420CA0 /* apeproperties.cpp */,
@ -1108,6 +1114,7 @@
32AE5AAF14E70ED600420CA0 /* id3v2footer.h in Headers */,
32AE5AB114E70ED600420CA0 /* id3v2frame.h in Headers */,
32AE5AB314E70ED600420CA0 /* id3v2framefactory.h in Headers */,
83AF2CBF2622643300538240 /* apegenfile.h in Headers */,
32AE5AB514E70ED600420CA0 /* id3v2header.h in Headers */,
32AE5AB714E70ED600420CA0 /* id3v2synchdata.h in Headers */,
32AE5AB914E70ED600420CA0 /* id3v2tag.h in Headers */,
@ -1241,6 +1248,7 @@
EDE8630725CF6C5B0086EFD3 /* itfile.cpp in Sources */,
32AE5A7614E70ED600420CA0 /* flacmetadatablock.cpp in Sources */,
32AE5A7814E70ED600420CA0 /* flacpicture.cpp in Sources */,
83AF2CBE2622643300538240 /* apegenfile.cpp in Sources */,
32AE5A7A14E70ED600420CA0 /* flacproperties.cpp in Sources */,
EDE863C625CF6D710086EFD3 /* ownershipframe.cpp in Sources */,
32AE5A7C14E70ED600420CA0 /* flacunknownmetadatablock.cpp in Sources */,

View File

@ -0,0 +1,204 @@
/***************************************************************************
copyright : (C) 2010 by Alex Novichkov
email : novichko@atnet.ru
copyright : (C) 2006 by Lukáš Lalinský
email : lalinsky@gmail.com
(original WavPack implementation)
copyright : (C) 2004 by Allan Sandfeld Jensen
email : kde@carewolf.org
(original MPC 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 <tbytevector.h>
#include <tstring.h>
#include <tdebug.h>
#include <tagunion.h>
#include <id3v1tag.h>
#include <id3v2header.h>
#include <tpropertymap.h>
#include <tagutils.h>
#include "apegenfile.h"
#include "apetag.h"
#include "apefooter.h"
using namespace TagLib;
namespace
{
enum { ApeGenAPEIndex = 0 };
}
class APEGen::File::FilePrivate
{
public:
FilePrivate() :
APELocation(-1),
APESize(0) {}
~FilePrivate()
{
}
long APELocation;
long APESize;
TagUnion tag;
};
////////////////////////////////////////////////////////////////////////////////
// static members
////////////////////////////////////////////////////////////////////////////////
bool APEGen::File::isSupported(IOStream *stream)
{
// Generic file support for anything with APE tags
const ByteVector buffer = Utils::readHeader(stream, bufferSize(), true);
return (buffer.find("APETAGEX") >= 0);
}
////////////////////////////////////////////////////////////////////////////////
// public members
////////////////////////////////////////////////////////////////////////////////
APEGen::File::File(FileName file, bool readProperties, Properties::ReadStyle) :
TagLib::File(file),
d(new FilePrivate())
{
if(isOpen())
read(readProperties);
}
APEGen::File::File(IOStream *stream, bool readProperties, Properties::ReadStyle) :
TagLib::File(stream),
d(new FilePrivate())
{
if(isOpen())
read(readProperties);
}
APEGen::File::~File()
{
delete d;
}
TagLib::Tag *APEGen::File::tag() const
{
return &d->tag;
}
PropertyMap APEGen::File::properties() const
{
return d->tag.properties();
}
void APEGen::File::removeUnsupportedProperties(const StringList &properties)
{
d->tag.removeUnsupportedProperties(properties);
}
PropertyMap APEGen::File::setProperties(const PropertyMap &properties)
{
return APETag(true)->setProperties(properties);
}
APEGen::Properties *APEGen::File::audioProperties() const
{
return NULL;
}
bool APEGen::File::save()
{
if(readOnly()) {
debug("APEGen::File::save() -- File is read only.");
return false;
}
// Update APE tag
if(APETag() && !APETag()->isEmpty()) {
// APE tag is not empty. Update the old one or create a new one.
if(d->APELocation < 0) {
d->APELocation = length();
}
const ByteVector data = APETag()->render();
insert(data, d->APELocation, d->APESize);
d->APESize = data.size();
}
else {
// APE tag is empty. Remove the old one.
if(d->APELocation >= 0) {
removeBlock(d->APELocation, d->APESize);
d->APELocation = -1;
d->APESize = 0;
}
}
return true;
}
APE::Tag *APEGen::File::APETag(bool create)
{
return d->tag.access<APE::Tag>(ApeGenAPEIndex, create);
}
void APEGen::File::strip(int tags)
{
if(tags & APE)
d->tag.set(ApeGenAPEIndex, 0);
APETag(true);
}
bool APEGen::File::hasAPETag() const
{
return (d->APELocation >= 0);
}
////////////////////////////////////////////////////////////////////////////////
// private members
////////////////////////////////////////////////////////////////////////////////
void APEGen::File::read(bool readProperties)
{
// Look for an APE tag
d->APELocation = Utils::findAPE(this, -1);
if(d->APELocation >= 0) {
d->tag.set(ApeGenAPEIndex, new APE::Tag(this, d->APELocation));
d->APESize = APETag()->footer()->completeTagSize();
d->APELocation = d->APELocation + APE::Footer::size() - d->APESize;
}
APETag(true);
}

View File

@ -0,0 +1,201 @@
/***************************************************************************
copyright : (C) 2010 by Alex Novichkov
email : novichko@atnet.ru
copyright : (C) 2006 by Lukáš Lalinský
email : lalinsky@gmail.com
(original WavPack implementation)
copyright : (C) 2004 by Allan Sandfeld Jensen
email : kde@carewolf.org
(original MPC 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_APEGENFILE_H
#define TAGLIB_APEGENFILE_H
#include "tfile.h"
#include "taglib_export.h"
#include "apeproperties.h"
namespace TagLib {
class Tag;
namespace APE { class Tag; }
//! An implementation of APE metadata
/*!
* This is implementation of APE metadata.
*
* This supports APE (v1 and v2) style comments only.
*/
namespace APEGen {
class Properties : public APE::Properties { };
//! An implementation of TagLib::File with APE specific methods
/*!
* This implements and provides an interface for any arbitrary file with
* APE tags appended, and no other tag format, using TagLib::Tag and
* TagLib::AudioProperties interfaces by way of implementing the abstract
* TagLib::File API as well as providing some additional information
* specific to APE tagged files. No technical info supported.
*/
class TAGLIB_EXPORT File : public TagLib::File
{
public:
/*!
* This set of flags is used for various operations and is suitable for
* being OR-ed together.
*/
enum TagTypes {
//! Empty set. Matches no tag types.
NoTags = 0x0000,
//! Matches APE tags.
APE = 0x0001,
//! Matches all tag types.
AllTags = 0xffff
};
/*!
* Constructs an APE file from \a file. If \a readProperties is true the
* file's audio properties will also be read.
*
* \note In the current implementation, \a propertiesStyle is ignored.
*/
File(FileName file, bool readProperties = true, Properties::ReadStyle propertiesStyle = Properties::Average);
/*!
* Constructs an APE file from \a stream. If \a readProperties is true the
* file's audio properties will also be read.
*
* \note TagLib will *not* take ownership of the stream, the caller is
* responsible for deleting it after the File object.
*
* \note In the current implementation, \a propertiesStyle is ignored.
*/
File(IOStream *stream, bool readProperties = true, Properties::ReadStyle propertiesStyle = Properties::Average);
/*!
* Destroys this instance of the File.
*/
virtual ~File();
/*!
* Returns the Tag for this file. This will be an APE tag.
*/
virtual 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.
* Creates an APEv2 tag if necessary.
*/
PropertyMap setProperties(const PropertyMap &);
/*!
* Returns the APE::Properties for this file. If no audio properties
* were read then this will return a null pointer.
*/
virtual Properties *audioProperties() const;
/*!
* Saves the file.
*
* Updates an existing APE tag, adds a new APEv2 tag, or removes it.
*/
virtual bool save();
/*!
* 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 MPEG::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.
*
* \note This will also invalidate pointers to the tags
* as their memory will be freed.
* \note In order to make the removal permanent save() still needs to be called
*/
void strip(int tags = AllTags);
/*!
* 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 using an
* APE tag parser. Does no other validation.
*
* \note This method is designed to do a quick check. The result may
* not necessarily be correct.
*/
static bool isSupported(IOStream *stream);
private:
File(const File &);
File &operator=(const File &);
void read(bool readProperties);
class FilePrivate;
FilePrivate *d;
};
}
}
#endif

View File

@ -50,6 +50,7 @@
#include "aifffile.h"
#include "wavfile.h"
#include "apefile.h"
#include "apegenfile.h"
#include "modfile.h"
#include "s3mfile.h"
#include "itfile.h"
@ -139,6 +140,8 @@ namespace
return new IT::File(stream, readAudioProperties, audioPropertiesStyle);
if(ext == "XM")
return new XM::File(stream, readAudioProperties, audioPropertiesStyle);
if(ext == "TAK")
return new APEGen::File(stream, readAudioProperties, audioPropertiesStyle);
return 0;
}
@ -178,6 +181,8 @@ namespace
file = new RIFF::WAV::File(stream, readAudioProperties, audioPropertiesStyle);
else if(APE::File::isSupported(stream))
file = new APE::File(stream, readAudioProperties, audioPropertiesStyle);
else if(APEGen::File::isSupported(stream))
file = new APEGen::File(stream, readAudioProperties, audioPropertiesStyle);
// isSupported() only does a quick check, so double check the file here.
@ -259,6 +264,8 @@ namespace
return new IT::File(fileName, readAudioProperties, audioPropertiesStyle);
if(ext == "XM")
return new XM::File(fileName, readAudioProperties, audioPropertiesStyle);
if(ext == "TAK")
return new APEGen::File(fileName, readAudioProperties, audioPropertiesStyle);
return 0;
}

View File

@ -58,6 +58,7 @@
#include "aifffile.h"
#include "wavfile.h"
#include "apefile.h"
#include "apegenfile.h"
#include "modfile.h"
#include "s3mfile.h"
#include "itfile.h"
@ -148,6 +149,8 @@ PropertyMap File::properties() const
return dynamic_cast<const MP4::File* >(this)->properties();
if(dynamic_cast<const ASF::File* >(this))
return dynamic_cast<const ASF::File* >(this)->properties();
if(dynamic_cast<const APEGen::File* >(this))
return dynamic_cast<const APEGen::File* >(this)->properties();
return tag()->properties();
}
@ -177,6 +180,8 @@ void File::removeUnsupportedProperties(const StringList &properties)
dynamic_cast<MP4::File* >(this)->removeUnsupportedProperties(properties);
else if(dynamic_cast<ASF::File* >(this))
dynamic_cast<ASF::File* >(this)->removeUnsupportedProperties(properties);
else if(dynamic_cast<APEGen::File* >(this))
dynamic_cast<APEGen::File* >(this)->removeUnsupportedProperties(properties);
else
tag()->removeUnsupportedProperties(properties);
}
@ -219,6 +224,8 @@ PropertyMap File::setProperties(const PropertyMap &properties)
return dynamic_cast<MP4::File* >(this)->setProperties(properties);
else if(dynamic_cast<ASF::File* >(this))
return dynamic_cast<ASF::File* >(this)->setProperties(properties);
else if(dynamic_cast<APEGen::File* >(this))
return dynamic_cast<APEGen::File* >(this)->setProperties(properties);
else
return tag()->setProperties(properties);
}