From cda53a3db4f0f44f253f695d7ba4bfc2692b2350 Mon Sep 17 00:00:00 2001 From: Allan Sandfeld Jensen Date: Sat, 17 Jul 2004 13:39:27 +0000 Subject: [PATCH] Add support for reading musepack(mpc)-files and parsing APE-tags(v1 and v2). 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 | 7 +- fileref.cpp | 3 + mpc/Makefile.am | 15 ++ mpc/apetag.cpp | 365 ++++++++++++++++++++++++++++++++++++++++++ mpc/apetag.h | 120 ++++++++++++++ mpc/mpcfile.cpp | 232 +++++++++++++++++++++++++++ mpc/mpcfile.h | 102 ++++++++++++ mpc/mpcproperties.cpp | 137 ++++++++++++++++ mpc/mpcproperties.h | 78 +++++++++ toolkit/taglib.h | 2 +- 10 files changed, 1057 insertions(+), 4 deletions(-) create mode 100644 mpc/Makefile.am create mode 100644 mpc/apetag.cpp create mode 100644 mpc/apetag.h create mode 100644 mpc/mpcfile.cpp create mode 100644 mpc/mpcfile.h create mode 100644 mpc/mpcproperties.cpp create mode 100644 mpc/mpcproperties.h diff --git a/Makefile.am b/Makefile.am index e39cd440..437e3a0e 100644 --- a/Makefile.am +++ b/Makefile.am @@ -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 diff --git a/fileref.cpp b/fileref.cpp index 890b9b0f..4307c5e4 100644 --- a/fileref.cpp +++ b/fileref.cpp @@ -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 index 00000000..01219777 --- /dev/null +++ b/mpc/Makefile.am @@ -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 index 00000000..8ca61c18 --- /dev/null +++ b/mpc/apetag.cpp @@ -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 +#include +#include +#include + +#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 items; + Map 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::Iterator i = d->items.begin(); + while (i != d->items.end()) { + if (!i->second.isEmpty()) { + data.append(APEItem(i->first, i->second)); + itemCount++; + } + i++; + } + } + + { Map::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::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 index 00000000..356cfead --- /dev/null +++ b/mpc/apetag.h @@ -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 +#include + +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 index 00000000..06e39d35 --- /dev/null +++ b/mpc/mpcfile.cpp @@ -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 +#include +#include + +#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 index 00000000..290cf230 --- /dev/null +++ b/mpc/mpcfile.h @@ -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 + +#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 index 00000000..d2964b36 --- /dev/null +++ b/mpc/mpcproperties.cpp @@ -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 +#include +#include + +#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 index 00000000..222cfe91 --- /dev/null +++ b/mpc/mpcproperties.h @@ -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 + +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 diff --git a/toolkit/taglib.h b/toolkit/taglib.h index 1176b4a2..85f5a98f 100644 --- a/toolkit/taglib.h +++ b/toolkit/taglib.h @@ -23,7 +23,7 @@ #define TAGLIB_H #define TAGLIB_MAJOR_VERSION 1 -#define TAGLIB_MINOR_VERSION 1 +#define TAGLIB_MINOR_VERSION 2 #include