]> granicus.if.org Git - taglib/commitdiff
Support for reading/writing tags from Monkey's Audio files
authorLukáš Lalinský <lalinsky@gmail.com>
Sat, 3 Jul 2010 11:32:27 +0000 (11:32 +0000)
committerLukáš Lalinský <lalinsky@gmail.com>
Sat, 3 Jul 2010 11:32:27 +0000 (11:32 +0000)
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

17 files changed:
NEWS
taglib/CMakeLists.txt
taglib/ape/CMakeLists.txt
taglib/ape/Makefile.am
taglib/ape/apefile.cpp [new file with mode: 0644]
taglib/ape/apefile.h [new file with mode: 0644]
taglib/ape/apeproperties.cpp [new file with mode: 0644]
taglib/ape/apeproperties.h [new file with mode: 0644]
taglib/ape/apetag.h
taglib/fileref.cpp
tests/CMakeLists.txt
tests/Makefile.am
tests/data/mac-390-hdr.ape [new file with mode: 0644]
tests/data/mac-396.ape [new file with mode: 0644]
tests/data/mac-399.ape [new file with mode: 0644]
tests/test_ape.cpp [new file with mode: 0644]
tests/test_fileref.cpp

diff --git a/NEWS b/NEWS
index eafea45392e53e2cfc8a299464d7036ddfc8f826..48324e612fd90e34594db590a188462b5dd5b699 100644 (file)
--- 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)
 ===========================
 
index 3314d6cebda4461134c8936df7fd446df0018436..619e31d915aa9baf323cadf0beb8d3bcbb904ef9 100644 (file)
@@ -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
index ce7d1c35c8478d800dbcd8c39c989ff942dec513..1a3c2093c6d2e9263b157beb4fe519948784fef2 100644 (file)
@@ -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)
index c5d861c785caac8120d15fe94c02f42d8701419a..2b6931ed8d124ee28fa17730d63432a7064d3493 100644 (file)
@@ -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 (file)
index 0000000..63d03ae
--- /dev/null
@@ -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;
+}
diff --git a/taglib/ape/apefile.h b/taglib/ape/apefile.h
new file mode 100644 (file)
index 0000000..3f641b7
--- /dev/null
@@ -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
diff --git a/taglib/ape/apeproperties.cpp b/taglib/ape/apeproperties.cpp
new file mode 100644 (file)
index 0000000..7ee55bc
--- /dev/null
@@ -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;
+}
+
diff --git a/taglib/ape/apeproperties.h b/taglib/ape/apeproperties.h
new file mode 100644 (file)
index 0000000..3fb020e
--- /dev/null
@@ -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
index 03a3c9177907c52a576af03592967dc3eee2a814..253f8a864acea100809a2a0dbbe406811454adb0 100644 (file)
@@ -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.
index fec4516df7ad2f78aa40000548dacfd6b8f3f327..43d48760dbaa381bf0e3c82b08aa2768e51b7ac8 100644 (file)
@@ -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;
index 005b7a5a0c38ed5bb7d0c1f6771a6a038e38b510..21820d75dbd41610b2a2b59da8f527a1304030d0 100644 (file)
@@ -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}
index 9f99683b8546374830e419adb8edb2aff0a3a6ad..9413182109a1794d2a45ea29e799ddb2cd8dc1e2 100644 (file)
@@ -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 (file)
index 0000000..c703e2e
Binary files /dev/null and b/tests/data/mac-390-hdr.ape differ
diff --git a/tests/data/mac-396.ape b/tests/data/mac-396.ape
new file mode 100644 (file)
index 0000000..fa7ae41
Binary files /dev/null and b/tests/data/mac-396.ape differ
diff --git a/tests/data/mac-399.ape b/tests/data/mac-399.ape
new file mode 100644 (file)
index 0000000..ae895ba
Binary files /dev/null and b/tests/data/mac-399.ape differ
diff --git a/tests/test_ape.cpp b/tests/test_ape.cpp
new file mode 100644 (file)
index 0000000..1001093
--- /dev/null
@@ -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);
index bc0c5fe18110bdaa30fa668142456d9866864daa..965afa9a0bb12cdf0b995c46a9f332734541890e 100644 (file)
@@ -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);