]> granicus.if.org Git - taglib/commitdiff
Add support for reading musepack(mpc)-files and parsing APE-tags(v1 and v2).
authorAllan Sandfeld Jensen <kde@carewolf.com>
Sat, 17 Jul 2004 13:39:27 +0000 (13:39 +0000)
committerAllan Sandfeld Jensen <kde@carewolf.com>
Sat, 17 Jul 2004 13:39:27 +0000 (13:39 +0000)
Bumb version to have something to check for in JuK and kfile_mpc

git-svn-id: svn://anonsvn.kde.org/home/kde/trunk/kdesupport/taglib@330294 283d02a7-25f6-0310-bc7c-ecb5cbfe19da

Makefile.am
fileref.cpp
mpc/Makefile.am [new file with mode: 0644]
mpc/apetag.cpp [new file with mode: 0644]
mpc/apetag.h [new file with mode: 0644]
mpc/mpcfile.cpp [new file with mode: 0644]
mpc/mpcfile.h [new file with mode: 0644]
mpc/mpcproperties.cpp [new file with mode: 0644]
mpc/mpcproperties.h [new file with mode: 0644]
toolkit/taglib.h

index e39cd4401e8de3d2e7128d6f534f079134dc2609..437e3a0e135078cd31b7c9fd6b6f97d7772b06ab 100644 (file)
@@ -1,10 +1,11 @@
-SUBDIRS = toolkit mpeg ogg flac
+SUBDIRS = toolkit mpeg ogg flac mpc
 
 INCLUDES = \
        -I$(top_srcdir)/taglib/toolkit \
        -I$(top_srcdir)/taglib/mpeg \
        -I$(top_srcdir)/taglib/ogg \
        -I$(top_srcdir)/taglib/flac \
+       -I$(top_srcdir)/taglib/mpc \
        -I$(top_srcdir)/taglib/ogg/vorbis \
        $(all_includes)
 
@@ -14,8 +15,8 @@ libtag_la_SOURCES = tag.cpp fileref.cpp audioproperties.cpp
 taglib_include_HEADERS = tag.h fileref.h audioproperties.h
 taglib_includedir = $(includedir)/taglib
 
-libtag_la_LDFLAGS = $(all_libraries) -no-undefined -version-info 2:0:1
-libtag_la_LIBADD = ./mpeg/libmpeg.la ./ogg/libogg.la ./flac/libflac.la ./toolkit/libtoolkit.la 
+libtag_la_LDFLAGS = $(all_libraries) -no-undefined -version-info 2:1:0
+libtag_la_LIBADD = ./mpeg/libmpeg.la ./ogg/libogg.la ./flac/libflac.la ./mpc/libmpc.la ./toolkit/libtoolkit.la
 
 bin_SCRIPTS = taglib-config
 
index 890b9b0fd2b73bed132e1e7fb3b92cc33096300c..4307c5e4c4ec2db0028b411c947cbe0066c18da0 100644 (file)
@@ -26,6 +26,7 @@
 #include "mpegfile.h"
 #include "vorbisfile.h"
 #include "flacfile.h"
+#include "mpcfile.h"
 
 using namespace TagLib;
 
@@ -129,6 +130,8 @@ File *FileRef::create(const char *fileName, bool readAudioProperties,
       return new MPEG::File(fileName, readAudioProperties, audioPropertiesStyle);
     if(s.substr(s.size() - 5, 5).upper() == ".FLAC")
       return new FLAC::File(fileName, readAudioProperties, audioPropertiesStyle);
+    if(s.substr(s.size() - 5, 4).upper() == ".MPC")
+      return new MPC::File(fileName, readAudioProperties, audioPropertiesStyle);
   }
 
   return 0;
diff --git a/mpc/Makefile.am b/mpc/Makefile.am
new file mode 100644 (file)
index 0000000..0121977
--- /dev/null
@@ -0,0 +1,15 @@
+INCLUDES = \
+       -I$(top_srcdir)/taglib \
+       -I$(top_srcdir)/taglib/toolkit \
+       -I$(top_srcdir)/taglib/mpeg/id3v1 \
+       -I$(top_srcdir)/taglib/mpeg/id3v2 \
+       $(all_includes)
+
+noinst_LTLIBRARIES = libmpc.la
+
+libmpc_la_SOURCES = mpcfile.cpp mpcproperties.cpp apetag.cpp
+
+taglib_include_HEADERS = mpcfile.h mpcproperties.h
+taglib_includedir = $(includedir)/taglib
+
+EXTRA_DIST = $(libmpc_la_SOURCES) $(taglib_include_HEADERS)
diff --git a/mpc/apetag.cpp b/mpc/apetag.cpp
new file mode 100644 (file)
index 0000000..8ca61c1
--- /dev/null
@@ -0,0 +1,365 @@
+/***************************************************************************
+    copyright            : (C) 2004 by Allan Sandfeld Jensen
+    email                : kde@carewolf.com
+ ***************************************************************************/
+
+/***************************************************************************
+ *   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                                                                   *
+ ***************************************************************************/
+
+#include <tdebug.h>
+#include <tfile.h>
+#include <tstring.h>
+#include <tmap.h>
+
+#include "apetag.h"
+
+using namespace TagLib;
+using namespace APE;
+
+class APE::Tag::TagPrivate
+{
+public:
+  TagPrivate() : file(0), tagOffset(-1), tagLength(0) {}
+
+  File *file;
+  long tagOffset;
+  long tagLength;
+
+  Map<const String, String> items;
+  Map<const String, ByteVector> unknowns;
+
+};
+/*
+struct APE::Tag::Item
+{
+  Item(String key) : key(key), type(STRING), value.str(String::null), readOnly(false) {};
+  const String key;
+  enum Type{ STRING, BINARY, URL, RESERVED } type;
+  union value{
+    String str;
+    ByteVector bin;
+  }
+  bool readOnly;
+}*/
+
+
+////////////////////////////////////////////////////////////////////////////////
+// public methods
+////////////////////////////////////////////////////////////////////////////////
+
+APE::Tag::Tag() : TagLib::Tag()
+{
+  d = new TagPrivate;
+}
+
+APE::Tag::Tag(File *file, long tagOffset) : TagLib::Tag()
+{
+  d = new TagPrivate;
+  d->file = file;
+  d->tagOffset = tagOffset;
+
+  read();
+}
+
+APE::Tag::~Tag()
+{
+  delete d;
+}
+
+using TagLib::uint;
+
+static ByteVector APEItem(String key, String value) {
+  ByteVector data;
+  uint flags = 0;
+
+  data.append(ByteVector::fromUInt(value.size(),false));
+  data.append(ByteVector::fromUInt(flags,false));
+  data.append(key.data(String::UTF8));
+  data.append(char(0));
+  data.append(value.data(String::UTF8));
+
+  return data;
+}
+
+static ByteVector APEFrame(bool isHeader, uint dataSize, uint itemCount) {
+  ByteVector header;
+  uint tagSize = 32 + dataSize;
+  // bit 31: Has a header
+  // bit 29: Is the header
+  uint flags = (1U<<31) | ((isHeader) ? (1U<<29) : 0);
+
+  header.append(APE::Tag::fileIdentifier());
+  header.append(ByteVector::fromUInt(2,false));
+  header.append(ByteVector::fromUInt(tagSize,false));
+  header.append(ByteVector::fromUInt(itemCount,false));
+  header.append(ByteVector::fromUInt(flags,false));
+  header.append(ByteVector::fromLongLong(0,false));
+
+  return header;
+}
+
+ByteVector APE::Tag::render() const
+{
+  ByteVector data;
+  uint itemCount = 0;
+
+  { Map<String,String>::Iterator i = d->items.begin();
+    while (i != d->items.end()) {
+      if (!i->second.isEmpty()) {
+        data.append(APEItem(i->first, i->second));
+        itemCount++;
+      }
+      i++;
+    }
+  }
+
+  { Map<String,ByteVector>::Iterator i = d->unknowns.begin();
+    while (i != d->unknown.end()) {
+      if (!i->second.isEmpty()) {
+          data.append(i->second);
+          itemCount++;
+      }
+      i++;
+    }
+  }
+
+  ByteVector tag;
+  tag.append(APEFrame(true, data.size(), itemCount));
+  tag.append(data);
+  tag.append(APEFrame(false, data.size(), itemCount));
+
+  return tag;
+}
+
+ByteVector APE::Tag::fileIdentifier()
+{
+  return ByteVector::fromCString("APETAGEX");
+}
+
+String APE::Tag::title() const
+{
+  if (d->items.contains("Title"))
+    return d->items["Title"];
+  else
+    return String::null;
+}
+
+String APE::Tag::artist() const
+{
+  if (d->items.contains("Artist"))
+    return d->items["Artist"];
+  else
+    return String::null;
+}
+
+String APE::Tag::album() const
+{
+  if (d->items.contains("Album"))
+    return d->items["Album"];
+  else
+    return String::null;
+}
+
+String APE::Tag::comment() const
+{
+  if (d->items.contains("Comment"))
+    return d->items["Comment"];
+  else
+    return String::null;
+}
+
+String APE::Tag::genre() const
+{
+  if (d->items.contains("Genre"))
+    return d->items["Genre"];
+  else
+    return String::null;
+}
+
+TagLib::uint APE::Tag::year() const
+{
+  if (d->items.contains("Year"))
+    return (d->items["Year"]).toInt();
+  return 0;
+}
+
+TagLib::uint APE::Tag::track() const
+{
+  if (d->items.contains("Track"))
+    return (d->items["Track"]).toInt();
+  return 0;
+}
+
+void APE::Tag::setTitle(const String &s)
+{
+  d->items["Title"] = s;
+}
+
+void APE::Tag::setArtist(const String &s)
+{
+  d->items["Artist"] = s;
+}
+
+void APE::Tag::setAlbum(const String &s)
+{
+  d->items["Album"] = s;
+}
+
+void APE::Tag::setComment(const String &s)
+{
+  if(s.isEmpty() )
+    removeComment("Comment");
+  else
+    d->items["Comment"] = s;
+}
+
+void APE::Tag::setGenre(const String &s)
+{
+  if(s.isEmpty())
+    removeComment("Genre");
+  else
+    d->items["Genre"] = s;
+}
+
+void APE::Tag::setYear(uint i)
+{
+  if(i <=0 )
+    removeComment("Year");
+  else
+    d->items["Year"] = String::number(i);
+}
+
+void APE::Tag::setTrack(uint i)
+{
+  if(i <=0 )
+    removeComment("Track");
+  else
+    d->items["Track"] = String::number(i);
+}
+
+void APE::Tag::removeComment(const String &key) {
+  Map<String,String>::Iterator it = d->items.find(key);
+  if (it != d->items.end())
+    d->items.erase(it);
+}
+
+void APE::Tag::addComment(const String &key, const String &value) {
+  if (value.isEmpty())
+    removeComment(key);
+  else
+    d->items[key] = value;
+}
+
+uint APE::Tag::tagSize(ByteVector footer) {
+
+    // The reported length (excl. header)
+
+    uint length = footer.mid(12,4).toUInt(false);
+
+    // Flags (bit 31: tag contains a header)
+
+    uint flags = footer.mid(20,4).toUInt(false);
+
+    return length + (flags & (1U<<31) ? 32 : 0);
+
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// protected methods
+////////////////////////////////////////////////////////////////////////////////
+
+void APE::Tag::read()
+{
+  if(d->file && d->file->isValid()) {
+    d->file->seek(d->tagOffset);
+    // read the footer -- always 32 bytes
+    ByteVector footer = d->file->readBlock(32);
+
+    // parse footer and some initial sanity checking
+    if(footer.size() == 32 && footer.mid(0, 8) == "APETAGEX") {
+      uint length = footer.mid(12,4).toUInt(false);
+      uint count = footer.mid(16,4).toUInt(false);
+      d->tagLength = length;
+      d->file->seek(d->tagOffset + 32 -length);
+      ByteVector data = d->file->readBlock(length-32);
+      parse(data,count);
+    }
+    else
+      debug("APE tag is not valid or could not be read at the specified offset.");
+  }
+}
+
+void APE::Tag::parse(const ByteVector &data, uint count)
+{
+    uint pos = 0;
+    uint vallen, flags;
+    String key, value;
+    while(count > 0) {
+        vallen = data.mid(pos+0,4).toUInt(false);
+        flags = data.mid(pos+4,4).toUInt(false);
+        key = String(data.mid(pos+8), String::UTF8);
+
+        if (flags == 0) {
+          value = String(data.mid(pos+8+key.size()+1, vallen), String::UTF8);
+          d->items.insert(key,value);
+        } else {
+          d->unknown.insert(data.mid(pos, 8+key.size()+1+vallen));
+        }
+
+        pos += 8+key.size()+1+vallen;
+        count--;
+    }
+}
+/*
+void APE::Tag::parse(const ByteVector &data, uint count)
+{
+    uint pos = 0;
+    uint vallen, flags;
+    String key;
+    while(count > 0) {
+      vallen = data.mid(pos+0,4).toUInt(false);
+      flags = data.mid(pos+4,4).toUInt(false);
+      key = String(data.mid(pos+8), String::UTF8);
+      Item item(key);
+
+      ByteVector value = data.mid(pos+8+key.size()+1, vallen);
+
+      switch ((flags >> 1) & 3) {
+        case 0:
+          item.value.str = String(value, String::UTF8);
+          item.type = Item::STRING;
+          break;
+        case 1:
+          item.value.bin = value;
+          item.type = Item::BINARY;
+          break;
+        case 2:
+          item.value.str = String(value, String::UTF8);
+          item.type = Item::URL;
+          break;
+        case 3:
+          item.value.bin = value;
+          item.type = Item::RESERVED;
+          break;
+      }
+      item.readOnly = (flags & 1);
+
+      d->items.insert(key,item);
+
+      pos += 8+key.size()+1+vallen;
+      count--;
+    }
+}*/
\ No newline at end of file
diff --git a/mpc/apetag.h b/mpc/apetag.h
new file mode 100644 (file)
index 0000000..356cfea
--- /dev/null
@@ -0,0 +1,120 @@
+/***************************************************************************
+    copyright            : (C) 2004 by Allan Sandfeld Jensen
+    email                : kde@carewolf.org
+ ***************************************************************************/
+
+/***************************************************************************
+ *   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                                                                   *
+ ***************************************************************************/
+
+#ifndef TAGLIB_APETAG_H
+#define TAGLIB_APETAG_H
+
+#include <tag.h>
+#include <tbytevector.h>
+
+namespace TagLib {
+
+  class File;
+
+  //! An APE tag implementation
+
+  namespace APE {
+
+    class Tag : public TagLib::Tag
+    {
+    public:
+      /*!
+       * Create an APE tag with default values.
+       */
+      Tag();
+
+      /*!
+       * Create an APE tag and parse the data in \a file with APE footer at
+       * \a tagOffset.
+       */
+      Tag(File *file, long tagOffset);
+
+      /*!
+       * Destroys this Tag instance.
+       */
+      virtual ~Tag();
+
+      /*!
+       * Renders the in memory values to a ByteVector suitable for writing to
+       * the file.
+       */
+      ByteVector render() const;
+
+      /*!
+       * Returns the string "APETAGEX" suitable for usage in locating the tag in a
+       * file.
+       */
+      static ByteVector fileIdentifier();
+
+      /*!
+       * Returns the size of the tag calculated based on the footer.
+       */
+      static uint tagSize(ByteVector footer);
+
+      // Reimplementations.
+
+      virtual String title() const;
+      virtual String artist() const;
+      virtual String album() const;
+      virtual String comment() const;
+      virtual String genre() const;
+      virtual uint year() const;
+      virtual uint track() const;
+
+      virtual void setTitle(const String &s);
+      virtual void setArtist(const String &s);
+      virtual void setAlbum(const String &s);
+      virtual void setComment(const String &s);
+      virtual void setGenre(const String &s);
+      virtual void setYear(uint i);
+      virtual void setTrack(uint i);
+
+      /*!
+       * Removes the \a key comment from the tag
+       */
+      void removeComment(const String &key);
+
+      /*!
+       * Adds the \a key comment with \a value
+       */
+      void addComment(const String &key, const String &value);
+
+    protected:
+      /*!
+       * Reads from the file specified in the constructor.
+       */
+      void read();
+      /*!
+       * Parses the body of the tag in \a data with \a count items.
+       */
+      void parse(const ByteVector &data, uint count);
+
+    private:
+      Tag(const Tag &);
+      Tag &operator=(const Tag &);
+
+      class TagPrivate;
+      TagPrivate *d;
+    };
+  }
+}
+
+#endif
diff --git a/mpc/mpcfile.cpp b/mpc/mpcfile.cpp
new file mode 100644 (file)
index 0000000..06e39d3
--- /dev/null
@@ -0,0 +1,232 @@
+/***************************************************************************
+    copyright            : (C) 2004 by Allan Sandfeld Jensen
+    email                : kde@carewolf.org
+ ***************************************************************************/
+
+/***************************************************************************
+ *   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                                                                   *
+ ***************************************************************************/
+
+#include <tbytevector.h>
+#include <tstring.h>
+#include <tdebug.h>
+
+#include "mpcfile.h"
+#include "id3v1tag.h"
+#include "id3v2header.h"
+#include "apetag.h"
+
+using namespace TagLib;
+
+class MPC::File::FilePrivate
+{
+public:
+  FilePrivate() :
+    APETag(0),
+    APEFooter(-1),
+    APELocation(-1),
+    APESize(0),
+    ID3v1Tag(0),
+    ID3v1Location(-1),
+    ID3v2Header(0),
+    ID3v2Location(-1),
+    ID3v2Size(0),
+    properties(0),
+    scanned(false),
+    hasAPE(false),
+    hasID3v1(false),
+    hasID3v2(false) {}
+
+  ~FilePrivate()
+  {
+    delete ID3v1Tag;
+    delete properties;
+  }
+
+  APE::Tag *APETag;
+  long APEFooter;
+  long APELocation;
+  uint APESize;
+
+  ID3v1::Tag *ID3v1Tag;
+  long ID3v1Location;
+
+  ID3v2::Header *ID3v2Header;
+  long ID3v2Location;
+  uint ID3v2Size;
+
+  Tag *tag;
+
+  Properties *properties;
+  bool scanned;
+
+  bool hasAPE;
+  bool hasID3v1;
+  bool hasID3v2;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// public members
+////////////////////////////////////////////////////////////////////////////////
+
+MPC::File::File(const char *file, bool readProperties,
+                 Properties::ReadStyle propertiesStyle) : TagLib::File(file)
+{
+  d = new FilePrivate;
+  read(readProperties, propertiesStyle);
+}
+
+MPC::File::~File()
+{
+  delete d;
+}
+
+TagLib::Tag *MPC::File::tag() const
+{
+  if (d->APETag)
+      return d->APETag;
+  else
+  if (d->ID3v1Tag)
+    return d->ID3v1Tag;
+  else {
+    d->APETag = new APE::Tag;
+    return d->APETag;
+  }
+}
+
+MPC::Properties *MPC::File::audioProperties() const
+{
+  return d->properties;
+}
+
+
+void MPC::File::save()
+{
+
+  // Update APE tag
+
+  if(d->hasAPE && d->APETag)
+  {
+    insert(d->APETag->render(), d->APELocation, d->APESize);
+  }
+  else
+
+  // Update ID3v1 tag
+
+  if(d->hasID3v1 && d->ID3v1Tag)
+  {
+    seek(-128, End);
+    writeBlock(d->ID3v1Tag->render());
+  }
+
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// private members
+////////////////////////////////////////////////////////////////////////////////
+
+void MPC::File::read(bool readProperties, Properties::ReadStyle propertiesStyle)
+{
+  // Look for an APE tag
+
+  findAPE();
+
+  if(d->APELocation >= 0) {
+    d->APETag = new APE::Tag(this, d->APEFooter);
+    d->hasAPE = true;
+  }
+
+  // Look for an ID3v1 tag
+
+  if (!d->hasAPE) {
+    d->ID3v1Location = findID3v1();
+
+    if(d->ID3v1Location >= 0) {
+      d->ID3v1Tag = new ID3v1::Tag(this, d->ID3v1Location);
+      d->hasID3v1 = true;
+    }
+  }
+
+  // Look for and skip an ID3v2 tag
+
+  d->ID3v2Location = findID3v2();
+
+  if(d->ID3v2Location >= 0) {
+    seek(d->ID3v2Location);
+    d->ID3v2Header = new ID3v2::Header(readBlock(ID3v2::Header::size()));
+    d->ID3v2Size = d->ID3v2Header->completeTagSize();
+    d->hasID3v2 = true;
+  }
+
+  if (d->hasID3v2)
+    seek(d->ID3v2Location + d->ID3v2Size);
+  else
+    seek(0);
+
+  // Look for MPC metadata
+
+  if(readProperties) {
+    d->properties = new Properties(readBlock(MPC::HeaderSize),
+                                   length() - d->ID3v2Size - d->APESize);
+  }
+}
+
+bool MPC::File::findAPE()
+{
+
+  if(!isValid())
+    return false;
+
+  seek(-32, End);
+  long p = tell();
+
+  ByteVector footer = readBlock(32);
+  if(footer.mid(0,8) == APE::Tag::fileIdentifier())
+  {
+     d->APEFooter = p;
+     d->APESize = APE::Tag::tagSize(footer);
+     d->APELocation = p + 32 - d->APESize;
+     return true;
+  }
+
+  return false;
+}
+
+long MPC::File::findID3v1()
+{
+  if(!isValid())
+    return -1;
+
+  seek(-128, End);
+  long p = tell();
+
+  if(readBlock(3) == ID3v1::Tag::fileIdentifier())
+    return p;
+
+  return -1;
+}
+
+long MPC::File::findID3v2()
+{
+  if(!isValid())
+    return -1;
+
+  seek(0);
+
+  if(readBlock(3) == ID3v2::Header::fileIdentifier())
+    return 0;
+
+  return -1;
+}
diff --git a/mpc/mpcfile.h b/mpc/mpcfile.h
new file mode 100644 (file)
index 0000000..290cf23
--- /dev/null
@@ -0,0 +1,102 @@
+/***************************************************************************
+    copyright            : (C) 2004 by Allan Sandfeld Jensen
+    email                : kde@carewolf.org
+ ***************************************************************************/
+
+/***************************************************************************
+ *   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                                                                   *
+ ***************************************************************************/
+
+#ifndef TAGLIB_MPCFILE_H
+#define TAGLIB_MPCFILE_H
+
+#include <tfile.h>
+
+#include "mpcproperties.h"
+
+namespace TagLib {
+
+  class Tag;
+
+  //! An implementation of MPC metadata
+
+  /*!
+   * This is implementation of MPC metadata.
+   *
+   * This supports ID3v1 and APE (v1 and v2) style comments as well as reading stream
+   * properties from the file. ID3v2 tags will be skipped and ignored.
+   */
+
+  namespace MPC {
+
+    //! An implementation of TagLib::File with MPC specific methods
+
+    /*!
+     * This implements and provides an interface for MPC 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 MPC files.
+     */
+
+    class File : public TagLib::File
+    {
+    public:
+      /*!
+       * Contructs an MPC 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(const char *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 either an APE or
+       * ID3v1 tag.
+       */
+      virtual TagLib::Tag *tag() const;
+
+      /*!
+       * Returns the MPC::Properties for this file.  If no audio properties
+       * were read then this will return a null pointer.
+       */
+      virtual Properties *audioProperties() const;
+
+      /*!
+       * Save the file.
+       */
+      virtual void save();
+
+    private:
+      File(const File &);
+      File &operator=(const File &);
+
+      void read(bool readProperties, Properties::ReadStyle propertiesStyle);
+      void scan();
+      bool findAPE();
+      long findID3v1();
+      long findID3v2();
+
+      class FilePrivate;
+      FilePrivate *d;
+    };
+  }
+}
+
+#endif
diff --git a/mpc/mpcproperties.cpp b/mpc/mpcproperties.cpp
new file mode 100644 (file)
index 0000000..d2964b3
--- /dev/null
@@ -0,0 +1,137 @@
+/***************************************************************************
+    copyright            : (C) 2004 by Allan Sandfeld Jensen
+    email                : kde@carewolf.org
+ ***************************************************************************/
+
+/***************************************************************************
+ *   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                                                                   *
+ ***************************************************************************/
+
+#include <tstring.h>
+#include <tdebug.h>
+#include <bitset>
+
+#include "mpcproperties.h"
+#include "mpcfile.h"
+
+using namespace TagLib;
+
+class MPC::Properties::PropertiesPrivate
+{
+public:
+  PropertiesPrivate(ByteVector d, long st, ReadStyle s) :
+    data(d),
+    streamLength(st),
+    style(s),
+    version(0),
+    length(0),
+    bitrate(0),
+    sampleRate(0),
+    channels(0) {}
+
+  ByteVector data;
+  long streamLength;
+  ReadStyle style;
+  int version;
+  int length;
+  int bitrate;
+  int sampleRate;
+  int channels;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// public members
+////////////////////////////////////////////////////////////////////////////////
+
+MPC::Properties::Properties(ByteVector data, long streamLength, ReadStyle style) : AudioProperties(style)
+{
+  d = new PropertiesPrivate(data, streamLength, style);
+  read();
+}
+
+MPC::Properties::~Properties()
+{
+  delete d;
+}
+
+int MPC::Properties::length() const
+{
+  return d->length;
+}
+
+int MPC::Properties::bitrate() const
+{
+  return d->bitrate;
+}
+
+int MPC::Properties::sampleRate() const
+{
+  return d->sampleRate;
+}
+/*
+int MPC::Properties::sampleWidth() const
+{
+  return d->sampleWidth;
+}*/
+
+int MPC::Properties::channels() const
+{
+  return d->channels;
+}
+
+int MPC::Properties::mpcVersion() const
+{
+  return d->version;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// private members
+////////////////////////////////////////////////////////////////////////////////
+
+static const unsigned short sftable [4] = { 44100, 48000, 37800, 32000 };
+
+void MPC::Properties::read()
+{
+  if (d->data.mid(0,3) != "MP+") return;
+
+  d->version = d->data[3] & 15;
+
+  unsigned int frames;
+  if (d->version >= 7) {
+    frames = d->data.mid(4,4).toUInt(false);
+
+    std::bitset<32> flags = d->data.mid(8,4).toUInt(true);
+    d->sampleRate = sftable[flags[17]*2+flags[16]];
+    d->channels = 2;
+
+  } else {
+    unsigned int headerData = d->data.mid(0,4).toUInt(false);
+    d->bitrate = (headerData >> 23) & 0x01ff;
+    d->version = (headerData >> 11) & 0x03ff;
+    d->sampleRate = 44100;
+    d->channels = 2;
+    if (d->version >= 5)
+        frames = d->data.mid(4,4).toUInt(false);
+    else
+        frames = d->data.mid(4,2).toUInt(false);
+  }
+
+  unsigned int samples = frames * 1152 - 576;
+  d->length = (samples+(d->sampleRate/2)) / d->sampleRate;
+
+  if (!d->bitrate)
+      d->bitrate = ((d->streamLength*8L) / d->length)/1000;
+
+}
diff --git a/mpc/mpcproperties.h b/mpc/mpcproperties.h
new file mode 100644 (file)
index 0000000..222cfe9
--- /dev/null
@@ -0,0 +1,78 @@
+/***************************************************************************
+    copyright            : (C) 2004 by Allan Sandfeld Jensen
+    email                : kde@carewolf.org
+ ***************************************************************************/
+
+/***************************************************************************
+ *   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                                                                   *
+ ***************************************************************************/
+
+#ifndef TAGLIB_MPCPROPERTIES_H
+#define TAGLIB_MPCPROPERTIES_H
+
+#include <audioproperties.h>
+
+namespace TagLib {
+
+  namespace MPC {
+
+    class File;
+
+    static const uint HeaderSize = 8*7;
+
+
+    //! An implementation of audio property reading for MPC
+
+    /*!
+     * This reads the data from an MPC stream found in the AudioProperties
+     * API.
+     */
+
+    class Properties : public AudioProperties
+    {
+    public:
+      /*!
+       * Create an instance of MPC::Properties with the data read from the
+       * ByteVector \a data.
+       */
+      Properties(ByteVector data, long streamLength, ReadStyle style = Average);
+
+      /*!
+       * Destroys this MPC::Properties instance.
+       */
+      virtual ~Properties();
+
+      // Reimplementations.
+
+      virtual int length() const;
+      virtual int bitrate() const;
+      virtual int sampleRate() const;
+      virtual int channels() const;
+
+      /*!
+       * Returns the version of the bitstream (SV4-SV7)
+       */
+      int mpcVersion() const;
+
+    private:
+      void read();
+
+      class PropertiesPrivate;
+      PropertiesPrivate *d;
+    };
+  }
+}
+
+#endif
index 1176b4a27094791cb5e6d858e74e6ca0839ed6e1..85f5a98f9dce3f5659b45722078bf3e33db11bc0 100644 (file)
@@ -23,7 +23,7 @@
 #define TAGLIB_H
 
 #define TAGLIB_MAJOR_VERSION 1
-#define TAGLIB_MINOR_VERSION 1
+#define TAGLIB_MINOR_VERSION 2
 
 #include <string>