From ab1bc0617211586351e30bac10fd3e41e363b3ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Lalinsk=C3=BD?= Date: Sat, 3 Jul 2010 11:32:27 +0000 Subject: [PATCH] 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 --- NEWS | 5 + taglib/CMakeLists.txt | 2 + taglib/ape/CMakeLists.txt | 2 +- taglib/ape/Makefile.am | 4 +- taglib/ape/apefile.cpp | 270 +++++++++++++++++++++++++++++++++++ taglib/ape/apefile.h | 171 ++++++++++++++++++++++ taglib/ape/apeproperties.cpp | 224 +++++++++++++++++++++++++++++ taglib/ape/apeproperties.h | 98 +++++++++++++ taglib/ape/apetag.h | 2 +- taglib/fileref.cpp | 8 ++ tests/CMakeLists.txt | 2 + tests/Makefile.am | 2 + tests/data/mac-390-hdr.ape | Bin 0 -> 128 bytes tests/data/mac-396.ape | Bin 0 -> 104 bytes tests/data/mac-399.ape | Bin 0 -> 172 bytes tests/test_ape.cpp | 52 +++++++ tests/test_fileref.cpp | 5 + 17 files changed, 843 insertions(+), 4 deletions(-) create mode 100644 taglib/ape/apefile.cpp create mode 100644 taglib/ape/apefile.h create mode 100644 taglib/ape/apeproperties.cpp create mode 100644 taglib/ape/apeproperties.h create mode 100644 tests/data/mac-390-hdr.ape create mode 100644 tests/data/mac-396.ape create mode 100644 tests/data/mac-399.ape create mode 100644 tests/test_ape.cpp diff --git a/NEWS b/NEWS index eafea453..48324e61 100644 --- a/NEWS +++ b/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) =========================== diff --git a/taglib/CMakeLists.txt b/taglib/CMakeLists.txt index 3314d6ce..619e31d9 100644 --- a/taglib/CMakeLists.txt +++ b/taglib/CMakeLists.txt @@ -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 diff --git a/taglib/ape/CMakeLists.txt b/taglib/ape/CMakeLists.txt index ce7d1c35..1a3c2093 100644 --- a/taglib/ape/CMakeLists.txt +++ b/taglib/ape/CMakeLists.txt @@ -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) diff --git a/taglib/ape/Makefile.am b/taglib/ape/Makefile.am index c5d861c7..2b6931ed 100644 --- a/taglib/ape/Makefile.am +++ b/taglib/ape/Makefile.am @@ -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 diff --git a/taglib/ape/apefile.cpp b/taglib/ape/apefile.cpp new file mode 100644 index 00000000..63d03ae1 --- /dev/null +++ b/taglib/ape/apefile.cpp @@ -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 +#include +#include +#include + +#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(ID3v1Index, create); +} + +APE::Tag *APE::File::APETag(bool create) +{ + return d->tag.access(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; +} diff --git a/taglib/ape/apefile.h b/taglib/ape/apefile.h new file mode 100644 index 00000000..3f641b7d --- /dev/null +++ b/taglib/ape/apefile.h @@ -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 is still 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 is still 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 diff --git a/taglib/ape/apeproperties.cpp b/taglib/ape/apeproperties.cpp new file mode 100644 index 00000000..7ee55bc7 --- /dev/null +++ b/taglib/ape/apeproperties.cpp @@ -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 +#include +#include +#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; +} + diff --git a/taglib/ape/apeproperties.h b/taglib/ape/apeproperties.h new file mode 100644 index 00000000..3fb020e3 --- /dev/null +++ b/taglib/ape/apeproperties.h @@ -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 diff --git a/taglib/ape/apetag.h b/taglib/ape/apetag.h index 03a3c917..253f8a86 100644 --- a/taglib/ape/apetag.h +++ b/taglib/ape/apetag.h @@ -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. diff --git a/taglib/fileref.cpp b/taglib/fileref.cpp index fec4516d..43d48760 100644 --- a/taglib/fileref.cpp +++ b/taglib/fileref.cpp @@ -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; diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 005b7a5a..21820d75 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -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} diff --git a/tests/Makefile.am b/tests/Makefile.am index 9f99683b..94131821 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -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 \ diff --git a/tests/data/mac-390-hdr.ape b/tests/data/mac-390-hdr.ape new file mode 100644 index 0000000000000000000000000000000000000000..c703e2e2fd595469973c4162cfbebcc0722ab829 GIT binary patch literal 128 zcmeZubXKt8U%)TMz{KFPhJitc0Rp&yR9*@L!%cMt1`s>Q)6MOUpcX^8W0-4NZi#{b zP>d0(M_?lp0}F!yLrP*v;trsi8lap{9TP*cE-ORIPIiXhwpPx# literal 0 HcmV?d00001 diff --git a/tests/data/mac-396.ape b/tests/data/mac-396.ape new file mode 100644 index 0000000000000000000000000000000000000000..fa7ae4149460cc0a94174a1e3ce72fa4f1106138 GIT binary patch literal 104 zcmeZubXKU~zrZfWz{KFPhJitc0Rk9-)PX7{FbBjA@^o`kdCkcX?il8pmRq7A02E_{ h>Jiw;#K6KJz>tzylE?s569JSH7VO>34%f}V003n#561ui literal 0 HcmV?d00001 diff --git a/tests/data/mac-399.ape b/tests/data/mac-399.ape new file mode 100644 index 0000000000000000000000000000000000000000..ae895ba21cb8d6ca9561db087d4373d976503e3e GIT binary patch literal 172 zcmeZubXJ(g&%j{9z`!5@#5_Q(1H>9Y3_8a>Mur1c z3=GUbrT_yIgUcEQh87@uA`s65;vi2qHTWVi@qf*Alqb{*XS literal 0 HcmV?d00001 diff --git a/tests/test_ape.cpp b/tests/test_ape.cpp new file mode 100644 index 00000000..10010932 --- /dev/null +++ b/tests/test_ape.cpp @@ -0,0 +1,52 @@ +#include +#include +#include +#include +#include +#include +#include +#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); diff --git a/tests/test_fileref.cpp b/tests/test_fileref.cpp index bc0c5fe1..965afa9a 100644 --- a/tests/test_fileref.cpp +++ b/tests/test_fileref.cpp @@ -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(f->file()) == NULL); } + void testAPE() + { + fileRefSave("mac-399.ape", ".ape"); + } }; CPPUNIT_TEST_SUITE_REGISTRATION(TestFileRef);