-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)
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
#include "mpegfile.h"
#include "vorbisfile.h"
#include "flacfile.h"
+#include "mpcfile.h"
using namespace TagLib;
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;
--- /dev/null
+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)
--- /dev/null
+/***************************************************************************
+ 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
--- /dev/null
+/***************************************************************************
+ 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
--- /dev/null
+/***************************************************************************
+ 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;
+}
--- /dev/null
+/***************************************************************************
+ 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
--- /dev/null
+/***************************************************************************
+ 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;
+
+}
--- /dev/null
+/***************************************************************************
+ 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
#define TAGLIB_H
#define TAGLIB_MAJOR_VERSION 1
-#define TAGLIB_MINOR_VERSION 1
+#define TAGLIB_MINOR_VERSION 2
#include <string>