mirror of
https://github.com/taglib/taglib.git
synced 2025-06-04 01:28:21 -04:00
Support for reading/writing tags from Monkey's Audio files
Patch by Alex Novichkov, slightly modified by me (code formatting + tests). git-svn-id: svn://anonsvn.kde.org/home/kde/trunk/kdesupport/taglib@1145554 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
This commit is contained in:
parent
8792f8df82
commit
ab1bc06172
5
NEWS
5
NEWS
@ -1,3 +1,8 @@
|
||||
TagLib 1.7
|
||||
==========
|
||||
|
||||
* Support for reading/writing tags from Monkey's Audio files.
|
||||
|
||||
TagLib 1.6.3 (Apr 17, 2010)
|
||||
===========================
|
||||
|
||||
|
@ -121,6 +121,8 @@ SET(ape_SRCS
|
||||
ape/apetag.cpp
|
||||
ape/apefooter.cpp
|
||||
ape/apeitem.cpp
|
||||
ape/apefile.cpp
|
||||
ape/apeproperties.cpp
|
||||
)
|
||||
|
||||
SET(wavpack_SRCS
|
||||
|
@ -1 +1 @@
|
||||
INSTALL( FILES apetag.h apefooter.h apeitem.h DESTINATION ${INCLUDE_INSTALL_DIR}/taglib)
|
||||
INSTALL( FILES apefile.h apeproperties.h apetag.h apefooter.h apeitem.h DESTINATION ${INCLUDE_INSTALL_DIR}/taglib)
|
||||
|
@ -6,7 +6,7 @@ INCLUDES = \
|
||||
|
||||
noinst_LTLIBRARIES = libape.la
|
||||
|
||||
libape_la_SOURCES = apetag.cpp apefooter.cpp apeitem.cpp
|
||||
libape_la_SOURCES = apetag.cpp apefooter.cpp apeitem.cpp apefile.cpp apeproperties.cpp
|
||||
|
||||
taglib_include_HEADERS = apetag.h apefooter.h apeitem.h
|
||||
taglib_include_HEADERS = apetag.h apefooter.h apeitem.h apefile.h apeproperties.cpp
|
||||
taglib_includedir = $(includedir)/taglib
|
||||
|
270
taglib/ape/apefile.cpp
Normal file
270
taglib/ape/apefile.cpp
Normal file
@ -0,0 +1,270 @@
|
||||
/***************************************************************************
|
||||
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
|
||||
* 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 "apefile.h"
|
||||
#include "id3v1tag.h"
|
||||
|
||||
#include "apetag.h"
|
||||
#include "apefooter.h"
|
||||
|
||||
using namespace TagLib;
|
||||
|
||||
namespace
|
||||
{
|
||||
enum { APEIndex, ID3v1Index };
|
||||
}
|
||||
|
||||
class APE::File::FilePrivate
|
||||
{
|
||||
public:
|
||||
FilePrivate() :
|
||||
APELocation(-1),
|
||||
APESize(0),
|
||||
ID3v1Location(-1),
|
||||
properties(0),
|
||||
hasAPE(false),
|
||||
hasID3v1(false) {}
|
||||
|
||||
~FilePrivate()
|
||||
{
|
||||
delete properties;
|
||||
}
|
||||
|
||||
long APELocation;
|
||||
uint APESize;
|
||||
|
||||
long ID3v1Location;
|
||||
|
||||
TagUnion tag;
|
||||
|
||||
Properties *properties;
|
||||
|
||||
// These indicate whether the file *on disk* has these tags, not if
|
||||
// this data structure does. This is used in computing offsets.
|
||||
|
||||
bool hasAPE;
|
||||
bool hasID3v1;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// public members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
APE::File::File(FileName file, bool readProperties,
|
||||
Properties::ReadStyle propertiesStyle) : TagLib::File(file)
|
||||
{
|
||||
d = new FilePrivate;
|
||||
read(readProperties, propertiesStyle);
|
||||
}
|
||||
|
||||
APE::File::~File()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
|
||||
TagLib::Tag *APE::File::tag() const
|
||||
{
|
||||
return &d->tag;
|
||||
}
|
||||
|
||||
APE::Properties *APE::File::audioProperties() const
|
||||
{
|
||||
return d->properties;
|
||||
}
|
||||
|
||||
bool APE::File::save()
|
||||
{
|
||||
if(readOnly()) {
|
||||
debug("APE::File::save() -- File is read only.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Update ID3v1 tag
|
||||
|
||||
if(ID3v1Tag()) {
|
||||
if(d->hasID3v1) {
|
||||
seek(d->ID3v1Location);
|
||||
writeBlock(ID3v1Tag()->render());
|
||||
}
|
||||
else {
|
||||
seek(0, End);
|
||||
d->ID3v1Location = tell();
|
||||
writeBlock(ID3v1Tag()->render());
|
||||
d->hasID3v1 = true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if(d->hasID3v1) {
|
||||
removeBlock(d->ID3v1Location, 128);
|
||||
d->hasID3v1 = false;
|
||||
if(d->hasAPE) {
|
||||
if(d->APELocation > d->ID3v1Location)
|
||||
d->APELocation -= 128;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Update APE tag
|
||||
|
||||
if(APETag()) {
|
||||
if(d->hasAPE)
|
||||
insert(APETag()->render(), d->APELocation, d->APESize);
|
||||
else {
|
||||
if(d->hasID3v1) {
|
||||
insert(APETag()->render(), d->ID3v1Location, 0);
|
||||
d->APESize = APETag()->footer()->completeTagSize();
|
||||
d->hasAPE = true;
|
||||
d->APELocation = d->ID3v1Location;
|
||||
d->ID3v1Location += d->APESize;
|
||||
}
|
||||
else {
|
||||
seek(0, End);
|
||||
d->APELocation = tell();
|
||||
writeBlock(APETag()->render());
|
||||
d->APESize = APETag()->footer()->completeTagSize();
|
||||
d->hasAPE = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if(d->hasAPE) {
|
||||
removeBlock(d->APELocation, d->APESize);
|
||||
d->hasAPE = false;
|
||||
if(d->hasID3v1) {
|
||||
if(d->ID3v1Location > d->APELocation) {
|
||||
d->ID3v1Location -= d->APESize;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
ID3v1::Tag *APE::File::ID3v1Tag(bool create)
|
||||
{
|
||||
return d->tag.access<ID3v1::Tag>(ID3v1Index, create);
|
||||
}
|
||||
|
||||
APE::Tag *APE::File::APETag(bool create)
|
||||
{
|
||||
return d->tag.access<APE::Tag>(APEIndex, create);
|
||||
}
|
||||
|
||||
void APE::File::strip(int tags)
|
||||
{
|
||||
if(tags & ID3v1) {
|
||||
d->tag.set(ID3v1Index, 0);
|
||||
APETag(true);
|
||||
}
|
||||
|
||||
if(tags & APE) {
|
||||
d->tag.set(APEIndex, 0);
|
||||
|
||||
if(!ID3v1Tag())
|
||||
APETag(true);
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// private members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void APE::File::read(bool readProperties, Properties::ReadStyle /* propertiesStyle */)
|
||||
{
|
||||
// Look for an ID3v1 tag
|
||||
|
||||
d->ID3v1Location = findID3v1();
|
||||
|
||||
if(d->ID3v1Location >= 0) {
|
||||
d->tag.set(ID3v1Index, new ID3v1::Tag(this, d->ID3v1Location));
|
||||
d->hasID3v1 = true;
|
||||
}
|
||||
|
||||
// Look for an APE tag
|
||||
|
||||
d->APELocation = findAPE();
|
||||
|
||||
if(d->APELocation >= 0) {
|
||||
d->tag.set(APEIndex, new APE::Tag(this, d->APELocation));
|
||||
d->APESize = APETag()->footer()->completeTagSize();
|
||||
d->APELocation = d->APELocation + APETag()->footer()->size() - d->APESize;
|
||||
d->hasAPE = true;
|
||||
}
|
||||
|
||||
if(!d->hasID3v1)
|
||||
APETag(true);
|
||||
|
||||
// Look for APE audio properties
|
||||
|
||||
if(readProperties) {
|
||||
d->properties = new Properties(this);
|
||||
}
|
||||
}
|
||||
|
||||
long APE::File::findAPE()
|
||||
{
|
||||
if(!isValid())
|
||||
return -1;
|
||||
|
||||
if(d->hasID3v1)
|
||||
seek(-160, End);
|
||||
else
|
||||
seek(-32, End);
|
||||
|
||||
long p = tell();
|
||||
|
||||
if(readBlock(8) == APE::Tag::fileIdentifier())
|
||||
return p;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
long APE::File::findID3v1()
|
||||
{
|
||||
if(!isValid())
|
||||
return -1;
|
||||
|
||||
seek(-128, End);
|
||||
long p = tell();
|
||||
|
||||
if(readBlock(3) == ID3v1::Tag::fileIdentifier())
|
||||
return p;
|
||||
|
||||
return -1;
|
||||
}
|
171
taglib/ape/apefile.h
Normal file
171
taglib/ape/apefile.h
Normal file
@ -0,0 +1,171 @@
|
||||
/***************************************************************************
|
||||
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
|
||||
* 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_APEFILE_H
|
||||
#define TAGLIB_APEFILE_H
|
||||
|
||||
#include "tfile.h"
|
||||
#include "taglib_export.h"
|
||||
#include "apeproperties.h"
|
||||
|
||||
namespace TagLib {
|
||||
|
||||
class Tag;
|
||||
|
||||
namespace ID3v1 { class Tag; }
|
||||
namespace APE { class Tag; }
|
||||
|
||||
//! An implementation of APE metadata
|
||||
|
||||
/*!
|
||||
* This is implementation of APE metadata.
|
||||
*
|
||||
* This supports ID3v1 and APE (v1 and v2) style comments as well as reading stream
|
||||
* properties from the file.
|
||||
*/
|
||||
|
||||
namespace APE {
|
||||
|
||||
//! An implementation of TagLib::File with APE specific methods
|
||||
|
||||
/*!
|
||||
* This implements and provides an interface APE WavPack files to the
|
||||
* 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 files.
|
||||
*/
|
||||
|
||||
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 ID3v1 tags.
|
||||
ID3v1 = 0x0001,
|
||||
//! Matches APE tags.
|
||||
APE = 0x0002,
|
||||
//! Matches all tag types.
|
||||
AllTags = 0xffff
|
||||
};
|
||||
|
||||
/*!
|
||||
* 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.
|
||||
*/
|
||||
File(FileName file, 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, an ID3v1 tag
|
||||
* or a combination of the two.
|
||||
*/
|
||||
virtual TagLib::Tag *tag() const;
|
||||
|
||||
/*!
|
||||
* 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.
|
||||
*
|
||||
* \note According to the official Monkey's Audio SDK, an APE file
|
||||
* can only have either ID3V1 or APE tags, so a parameter is used here.
|
||||
*/
|
||||
virtual bool save();
|
||||
|
||||
/*!
|
||||
* Returns a pointer to the ID3v1 tag of the file.
|
||||
*
|
||||
* If \a create is false (the default) this will return a null pointer
|
||||
* if there is no valid ID3v1 tag. If \a create is true it will create
|
||||
* an ID3v1 tag if one does not exist. If there is already an APE tag, the
|
||||
* new ID3v1 tag will be placed after it.
|
||||
*
|
||||
* \note The Tag <b>is still</b> owned by the APE::File and should not be
|
||||
* deleted by the user. It will be deleted when the file (object) is
|
||||
* destroyed.
|
||||
*/
|
||||
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
|
||||
* a APE tag if one does not exist.
|
||||
*
|
||||
* \note The Tag <b>is still</b> owned by the APE::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 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);
|
||||
|
||||
private:
|
||||
File(const File &);
|
||||
File &operator=(const File &);
|
||||
|
||||
void read(bool readProperties, Properties::ReadStyle propertiesStyle);
|
||||
void scan();
|
||||
long findID3v1();
|
||||
long findAPE();
|
||||
|
||||
class FilePrivate;
|
||||
FilePrivate *d;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
224
taglib/ape/apeproperties.cpp
Normal file
224
taglib/ape/apeproperties.cpp
Normal file
@ -0,0 +1,224 @@
|
||||
/***************************************************************************
|
||||
copyright : (C) 2010 by Alex Novichkov
|
||||
email : novichko@atnet.ru
|
||||
|
||||
copyright : (C) 2006 by Lukáš Lalinský
|
||||
email : lalinsky@gmail.com
|
||||
(original WavPack 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
|
||||
* 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 <tstring.h>
|
||||
#include <tdebug.h>
|
||||
#include <bitset>
|
||||
#include "id3v2tag.h"
|
||||
#include "apeproperties.h"
|
||||
#include "apefile.h"
|
||||
|
||||
using namespace TagLib;
|
||||
|
||||
class APE::Properties::PropertiesPrivate
|
||||
{
|
||||
public:
|
||||
PropertiesPrivate(File *file, long streamLength) :
|
||||
length(0),
|
||||
bitrate(0),
|
||||
sampleRate(0),
|
||||
channels(0),
|
||||
version(0),
|
||||
bitsPerSample(0),
|
||||
file(file),
|
||||
streamLength(streamLength) {}
|
||||
|
||||
long streamLength;
|
||||
int length;
|
||||
int bitrate;
|
||||
int sampleRate;
|
||||
int channels;
|
||||
int version;
|
||||
int bitsPerSample;
|
||||
File *file;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// public members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
APE::Properties::Properties(File *file, ReadStyle style) : AudioProperties(style)
|
||||
{
|
||||
d = new PropertiesPrivate(file, file->length());
|
||||
read();
|
||||
}
|
||||
|
||||
APE::Properties::~Properties()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
|
||||
int APE::Properties::length() const
|
||||
{
|
||||
return d->length;
|
||||
}
|
||||
|
||||
int APE::Properties::bitrate() const
|
||||
{
|
||||
return d->bitrate;
|
||||
}
|
||||
|
||||
int APE::Properties::sampleRate() const
|
||||
{
|
||||
return d->sampleRate;
|
||||
}
|
||||
|
||||
int APE::Properties::channels() const
|
||||
{
|
||||
return d->channels;
|
||||
}
|
||||
|
||||
int APE::Properties::version() const
|
||||
{
|
||||
return d->version;
|
||||
}
|
||||
|
||||
int APE::Properties::bitsPerSample() const
|
||||
{
|
||||
return d->bitsPerSample;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// private members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
void APE::Properties::read()
|
||||
{
|
||||
// First we are searching the descriptor
|
||||
long offset = findDescriptor();
|
||||
if(offset < 0)
|
||||
return;
|
||||
|
||||
// Then we read the header common for all versions of APE
|
||||
d->file->seek(offset);
|
||||
ByteVector commonHeader=d->file->readBlock(6);
|
||||
if(!commonHeader.startsWith("MAC "))
|
||||
return;
|
||||
d->version = commonHeader.mid(4).toUInt(false);
|
||||
|
||||
if(d->version >= 3980) {
|
||||
analyzeCurrent();
|
||||
}
|
||||
else {
|
||||
analyzeOld();
|
||||
}
|
||||
}
|
||||
|
||||
long APE::Properties::findDescriptor()
|
||||
{
|
||||
long ID3v2Location = findID3v2();
|
||||
long ID3v2OriginalSize = 0;
|
||||
bool hasID3v2 = false;
|
||||
if(ID3v2Location >= 0) {
|
||||
ID3v2::Tag tag(d->file, ID3v2Location, 0);
|
||||
ID3v2OriginalSize = tag.header()->completeTagSize();
|
||||
if(tag.header()->tagSize() > 0)
|
||||
hasID3v2 = true;
|
||||
}
|
||||
|
||||
long offset = 0;
|
||||
if(hasID3v2)
|
||||
offset = d->file->find("MAC ", ID3v2Location + ID3v2OriginalSize);
|
||||
else
|
||||
offset = d->file->find("MAC ");
|
||||
|
||||
if(offset < 0) {
|
||||
debug("APE::Properties::findDescriptor() -- APE descriptor not found");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return offset;
|
||||
}
|
||||
|
||||
long APE::Properties::findID3v2()
|
||||
{
|
||||
if(!d->file->isValid())
|
||||
return -1;
|
||||
|
||||
d->file->seek(0);
|
||||
|
||||
if(d->file->readBlock(3) == ID3v2::Header::fileIdentifier())
|
||||
return 0;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
void APE::Properties::analyzeCurrent()
|
||||
{
|
||||
// Read the descriptor
|
||||
d->file->seek(2, File::Current);
|
||||
ByteVector descriptor = d->file->readBlock(44);
|
||||
uint descriptorBytes = descriptor.mid(0,4).toUInt(false);
|
||||
|
||||
if ((descriptorBytes - 52) > 0)
|
||||
d->file->seek(descriptorBytes - 52, File::Current);
|
||||
|
||||
// Read the header
|
||||
ByteVector header = d->file->readBlock(24);
|
||||
|
||||
// Get the APE info
|
||||
d->channels = header.mid(18, 2).toShort(false);
|
||||
d->sampleRate = header.mid(20, 4).toUInt(false);
|
||||
d->bitsPerSample = header.mid(16, 2).toShort(false);
|
||||
//d->compressionLevel =
|
||||
|
||||
uint totalFrames = header.mid(12, 4).toUInt(false);
|
||||
uint blocksPerFrame = header.mid(4, 4).toUInt(false);
|
||||
uint finalFrameBlocks = header.mid(8, 4).toUInt(false);
|
||||
uint totalBlocks = totalFrames > 0 ? (totalFrames - 1) * blocksPerFrame + finalFrameBlocks : 0;
|
||||
d->length = totalBlocks / d->sampleRate;
|
||||
d->bitrate = d->length > 0 ? ((d->streamLength * 8L) / d->length) / 1000 : 0;
|
||||
}
|
||||
|
||||
void APE::Properties::analyzeOld()
|
||||
{
|
||||
ByteVector header = d->file->readBlock(26);
|
||||
uint totalFrames = header.mid(18, 4).toUInt(false);
|
||||
|
||||
// Fail on 0 length APE files (catches non-finalized APE files)
|
||||
if(totalFrames == 0)
|
||||
return;
|
||||
|
||||
short compressionLevel = header.mid(0, 2).toShort(false);
|
||||
uint blocksPerFrame;
|
||||
if(d->version >= 3950)
|
||||
blocksPerFrame = 73728 * 4;
|
||||
else if(d->version >= 3900 || (d->version >= 3800 && compressionLevel == 4000))
|
||||
blocksPerFrame = 73728;
|
||||
else
|
||||
blocksPerFrame = 9216;
|
||||
d->channels = header.mid(4, 2).toShort(false);
|
||||
d->sampleRate = header.mid(6, 4).toUInt(false);
|
||||
uint finalFrameBlocks = header.mid(22, 4).toUInt(false);
|
||||
uint totalBlocks = totalFrames > 0 ? (totalFrames - 1) * blocksPerFrame + finalFrameBlocks : 0;
|
||||
d->length = totalBlocks / d->sampleRate;
|
||||
d->bitrate = d->length > 0 ? ((d->streamLength * 8L) / d->length) / 1000 : 0;
|
||||
}
|
||||
|
98
taglib/ape/apeproperties.h
Normal file
98
taglib/ape/apeproperties.h
Normal file
@ -0,0 +1,98 @@
|
||||
/***************************************************************************
|
||||
copyright : (C) 2010 by Alex Novichkov
|
||||
email : novichko@atnet.ru
|
||||
|
||||
copyright : (C) 2006 by Lukáš Lalinský
|
||||
email : lalinsky@gmail.com
|
||||
(original WavPack 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
|
||||
* 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_APEPROPERTIES_H
|
||||
#define TAGLIB_APEPROPERTIES_H
|
||||
|
||||
#include "taglib_export.h"
|
||||
#include "audioproperties.h"
|
||||
|
||||
namespace TagLib {
|
||||
|
||||
namespace APE {
|
||||
|
||||
class File;
|
||||
|
||||
//! An implementation of audio property reading for APE
|
||||
|
||||
/*!
|
||||
* This reads the data from an APE stream found in the AudioProperties
|
||||
* API.
|
||||
*/
|
||||
|
||||
class TAGLIB_EXPORT Properties : public AudioProperties
|
||||
{
|
||||
public:
|
||||
/*!
|
||||
* Create an instance of APE::Properties with the data read from the
|
||||
* ByteVector \a data.
|
||||
*/
|
||||
Properties(File *f, ReadStyle style = Average);
|
||||
|
||||
/*!
|
||||
* Destroys this APE::Properties instance.
|
||||
*/
|
||||
virtual ~Properties();
|
||||
|
||||
// Reimplementations.
|
||||
|
||||
virtual int length() const;
|
||||
virtual int bitrate() const;
|
||||
virtual int sampleRate() const;
|
||||
virtual int channels() const;
|
||||
|
||||
/*!
|
||||
* Returns number of bits per sample.
|
||||
*/
|
||||
int bitsPerSample() const;
|
||||
|
||||
/*!
|
||||
* Returns APE version.
|
||||
*/
|
||||
int version() const;
|
||||
|
||||
private:
|
||||
Properties(const Properties &);
|
||||
Properties &operator=(const Properties &);
|
||||
|
||||
void read();
|
||||
|
||||
long findDescriptor();
|
||||
long findID3v2();
|
||||
|
||||
void analyzeCurrent();
|
||||
void analyzeOld();
|
||||
|
||||
class PropertiesPrivate;
|
||||
PropertiesPrivate *d;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
@ -66,7 +66,7 @@ namespace TagLib {
|
||||
* Create an APE tag and parse the data in \a file with APE footer at
|
||||
* \a tagOffset.
|
||||
*/
|
||||
Tag(File *file, long footerLocation);
|
||||
Tag(TagLib::File *file, long footerLocation);
|
||||
|
||||
/*!
|
||||
* Destroys this Tag instance.
|
||||
|
@ -1,6 +1,10 @@
|
||||
/***************************************************************************
|
||||
copyright : (C) 2002 - 2008 by Scott Wheeler
|
||||
email : wheeler@kde.org
|
||||
|
||||
copyright : (C) 2010 by Alex Novichkov
|
||||
email : novichko@atnet.ru
|
||||
(added APE file support)
|
||||
***************************************************************************/
|
||||
|
||||
/***************************************************************************
|
||||
@ -44,6 +48,7 @@
|
||||
#include "trueaudiofile.h"
|
||||
#include "aifffile.h"
|
||||
#include "wavfile.h"
|
||||
#include "apefile.h"
|
||||
|
||||
using namespace TagLib;
|
||||
|
||||
@ -156,6 +161,7 @@ StringList FileRef::defaultFileExtensions()
|
||||
l.append("aif");
|
||||
l.append("aiff");
|
||||
l.append("wav");
|
||||
l.append("ape");
|
||||
|
||||
return l;
|
||||
}
|
||||
@ -254,6 +260,8 @@ File *FileRef::create(FileName fileName, bool readAudioProperties,
|
||||
return new RIFF::WAV::File(fileName, readAudioProperties, audioPropertiesStyle);
|
||||
if(ext == "AIFF")
|
||||
return new RIFF::AIFF::File(fileName, readAudioProperties, audioPropertiesStyle);
|
||||
if(ext == "APE")
|
||||
return new APE::File(fileName, readAudioProperties, audioPropertiesStyle);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
@ -3,6 +3,7 @@ if(BUILD_TESTS)
|
||||
INCLUDE_DIRECTORIES(
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../taglib
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../taglib/toolkit
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../taglib/ape
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../taglib/asf
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../taglib/mpeg/id3v1
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../taglib/mpeg/id3v2
|
||||
@ -37,6 +38,7 @@ SET(test_runner_SRCS
|
||||
test_ogg.cpp
|
||||
test_oggflac.cpp
|
||||
test_flac.cpp
|
||||
test_ape.cpp
|
||||
)
|
||||
IF(WITH_MP4)
|
||||
SET(test_runner_SRCS ${test_runner_SRCS}
|
||||
|
@ -2,6 +2,7 @@ INCLUDES = \
|
||||
-I$(top_srcdir)/taglib\
|
||||
-I$(top_srcdir)/taglib/toolkit \
|
||||
-I$(top_srcdir)/taglib/trueaudio \
|
||||
-I$(top_srcdir)/taglib/ape \
|
||||
-I$(top_srcdir)/taglib/mpeg \
|
||||
-I$(top_srcdir)/taglib/mpeg/id3v1 \
|
||||
-I$(top_srcdir)/taglib/mpeg/id3v2 \
|
||||
@ -19,6 +20,7 @@ test_runner_SOURCES = \
|
||||
test_map.cpp \
|
||||
test_mpeg.cpp \
|
||||
test_synchdata.cpp \
|
||||
test_ape.cpp \
|
||||
test_trueaudio.cpp \
|
||||
test_bytevector.cpp \
|
||||
test_string.cpp \
|
||||
|
BIN
tests/data/mac-390-hdr.ape
Normal file
BIN
tests/data/mac-390-hdr.ape
Normal file
Binary file not shown.
BIN
tests/data/mac-396.ape
Normal file
BIN
tests/data/mac-396.ape
Normal file
Binary file not shown.
BIN
tests/data/mac-399.ape
Normal file
BIN
tests/data/mac-399.ape
Normal file
Binary file not shown.
52
tests/test_ape.cpp
Normal file
52
tests/test_ape.cpp
Normal file
@ -0,0 +1,52 @@
|
||||
#include <cppunit/extensions/HelperMacros.h>
|
||||
#include <string>
|
||||
#include <stdio.h>
|
||||
#include <tag.h>
|
||||
#include <tstringlist.h>
|
||||
#include <tbytevectorlist.h>
|
||||
#include <apefile.h>
|
||||
#include "utils.h"
|
||||
|
||||
using namespace std;
|
||||
using namespace TagLib;
|
||||
|
||||
class TestAPE : public CppUnit::TestFixture
|
||||
{
|
||||
CPPUNIT_TEST_SUITE(TestAPE);
|
||||
CPPUNIT_TEST(testProperties399);
|
||||
CPPUNIT_TEST(testProperties396);
|
||||
CPPUNIT_TEST(testProperties390);
|
||||
CPPUNIT_TEST_SUITE_END();
|
||||
|
||||
public:
|
||||
|
||||
void testProperties399()
|
||||
{
|
||||
APE::File f("data/mac-399.ape");
|
||||
CPPUNIT_ASSERT_EQUAL(3, f.audioProperties()->length());
|
||||
CPPUNIT_ASSERT_EQUAL(0, f.audioProperties()->bitrate());
|
||||
CPPUNIT_ASSERT_EQUAL(2, f.audioProperties()->channels());
|
||||
CPPUNIT_ASSERT_EQUAL(44100, f.audioProperties()->sampleRate());
|
||||
}
|
||||
|
||||
void testProperties396()
|
||||
{
|
||||
APE::File f("data/mac-396.ape");
|
||||
CPPUNIT_ASSERT_EQUAL(3, f.audioProperties()->length());
|
||||
CPPUNIT_ASSERT_EQUAL(0, f.audioProperties()->bitrate());
|
||||
CPPUNIT_ASSERT_EQUAL(2, f.audioProperties()->channels());
|
||||
CPPUNIT_ASSERT_EQUAL(44100, f.audioProperties()->sampleRate());
|
||||
}
|
||||
|
||||
void testProperties390()
|
||||
{
|
||||
APE::File f("data/mac-390-hdr.ape");
|
||||
CPPUNIT_ASSERT_EQUAL(15, f.audioProperties()->length());
|
||||
CPPUNIT_ASSERT_EQUAL(0, f.audioProperties()->bitrate());
|
||||
CPPUNIT_ASSERT_EQUAL(2, f.audioProperties()->channels());
|
||||
CPPUNIT_ASSERT_EQUAL(44100, f.audioProperties()->sampleRate());
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
CPPUNIT_TEST_SUITE_REGISTRATION(TestAPE);
|
@ -32,6 +32,7 @@ class TestFileRef : public CppUnit::TestFixture
|
||||
CPPUNIT_TEST(testMP4_3);
|
||||
#endif
|
||||
CPPUNIT_TEST(testTrueAudio);
|
||||
CPPUNIT_TEST(testAPE);
|
||||
CPPUNIT_TEST_SUITE_END();
|
||||
|
||||
public:
|
||||
@ -148,6 +149,10 @@ public:
|
||||
CPPUNIT_ASSERT(dynamic_cast<Ogg::FLAC::File *>(f->file()) == NULL);
|
||||
}
|
||||
|
||||
void testAPE()
|
||||
{
|
||||
fileRefSave("mac-399.ape", ".ape");
|
||||
}
|
||||
};
|
||||
|
||||
CPPUNIT_TEST_SUITE_REGISTRATION(TestFileRef);
|
||||
|
Loading…
x
Reference in New Issue
Block a user