--- /dev/null
+SUBDIRS = toolkit mpeg ogg flac
+
+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/ogg/vorbis \
+ $(all_includes)
+
+lib_LTLIBRARIES = libtag.la
+
+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 1:0:0
+libtag_la_LIBADD = ./mpeg/libmpeg.la ./ogg/libogg.la ./flac/libflac.la ./toolkit/libtoolkit.la
+
+bin_SCRIPTS = taglib-config
+
+EXTRA_DIST = $(libtag_la_SOURCES) $(taglib_include_HEADERS)
+
+examples: examples-all
+
+examples-all:
+ cd examples ; \
+ $(MAKE) all
+
+apidox:
+ $(mkinstalldirs) doc/api; \
+ if test ! -x doc/common; then \
+ $(LN_S) $(kde_libs_htmldir)/en/common doc/common ; \
+ fi; \
+ cp $(top_srcdir)/admin/Doxyfile.global taglib.doxyfile; \
+ echo "PROJECT_NAME = TagLib" >> taglib.doxyfile; \
+ echo "PROJECT_NUMBER = \"Version 1.0\"" >> taglib.doxyfile; \
+ echo "INPUT = $(srcdir)" >> taglib.doxyfile; \
+ echo "OUTPUT_DIRECTORY = doc/api" >> taglib.doxyfile; \
+ echo "HTML_OUTPUT = html" >> taglib.doxyfile; \
+ echo "GENERATE_HTML = YES" >> taglib.doxyfile ; \
+ echo "GENERATE_MAN = NO" >> taglib.doxyfile ; \
+ echo "GENERATE_LATEX = NO" >> taglib.doxyfile ; \
+ echo "HTML_HEADER = doc/common/header.html" >> taglib.doxyfile ; \
+ echo "HTML_FOOTER = doc/common/footer.html" >> taglib.doxyfile ; \
+ echo "HTML_STYLESHEET = doc/common/doxygen.css" >> taglib.doxyfile ; \
+ echo "FILE_PATTERNS = *.h" >> taglib.doxyfile ; \
+ echo "PREDEFINED = DO_NOT_DOCUMENT" >> taglib.doxyfile ; \
+ echo "EXTRACT_ALL = YES" >> taglib.doxyfile ; \
+ doxygen taglib.doxyfile
--- /dev/null
+/***************************************************************************
+ copyright : (C) 2003 by Scott Wheeler
+ email : wheeler@kde.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 "audioproperties.h"
+
+using namespace TagLib;
+
+class AudioProperties::AudioPropertiesPrivate
+{
+
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// public methods
+////////////////////////////////////////////////////////////////////////////////
+
+AudioProperties::~AudioProperties()
+{
+
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// protected methods
+////////////////////////////////////////////////////////////////////////////////
+
+AudioProperties::AudioProperties(ReadStyle)
+{
+
+}
--- /dev/null
+/***************************************************************************
+ copyright : (C) 2003 by Scott Wheeler
+ email : wheeler@kde.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_AUDIOPROPERTIES_H
+#define TAGLIB_AUDIOPROPERTIES_H
+
+namespace TagLib {
+
+ //! A simple, abstract interface to common audio properties
+
+ /*!
+ * The values here are common to most audio formats. For more specific, codec
+ * dependant values, please see see the subclasses APIs. This is meant to
+ * compliment the TagLib::File and TagLib::Tag APIs in providing a simple
+ * interface that is sufficient for most applications.
+ */
+
+ class AudioProperties
+ {
+ public:
+
+ /*!
+ * Reading audio properties from a file can sometimes be very time consuming
+ * and for the most accurate results can often involve reading the entire
+ * file. Because in many situations speed is critical or the accuracy of the
+ * values is not particularly important this allows the level of desired
+ * accuracy to be set.
+ */
+ enum ReadStyle {
+ //! Read as little of the file as possible
+ Fast,
+ //! Read more of the file and make better values guesses
+ Average,
+ //! Read as much of the file as needed to report accurate values
+ Accurate
+ };
+
+ /*!
+ * Destroys this AudioProperties instance.
+ */
+ virtual ~AudioProperties();
+
+ /*!
+ * Returns the lenght of the file in seconds.
+ */
+ virtual int length() const = 0;
+
+ /*!
+ * Returns the most appropriate bit rate for the file in kb/s. For constant
+ * bitrate formats this is simply the bitrate of the file. For variable
+ * bitrate formats this is either the average or nominal bitrate.
+ */
+ virtual int bitrate() const = 0;
+
+ /*!
+ * Returns the sample rate in Hz.
+ */
+ virtual int sampleRate() const = 0;
+
+ /*!
+ * Returns the number of audio channels.
+ */
+ virtual int channels() const = 0;
+
+ protected:
+
+ /*!
+ * Construct an audio properties instance. This is protected as this class
+ * should not be instantiated directly, but should be instantiated via its
+ * subclasses and can be fetched from the FileRef or File APIs.
+ *
+ * \see ReadStyle
+ */
+ AudioProperties(ReadStyle style);
+
+ private:
+ AudioProperties(const AudioProperties &);
+ AudioProperties &operator=(const AudioProperties &);
+
+ class AudioPropertiesPrivate;
+ AudioPropertiesPrivate *d;
+ };
+
+}
+
+#endif
--- /dev/null
+SUBDIRS = c
--- /dev/null
+INCLUDES = \
+ -I$(top_srcdir)/taglib \
+ -I$(top_srcdir)/taglib/toolkit \
+ -I$(top_srcdir)/taglib/mpeg \
+ -I$(top_srcdir)/taglib/ogg \
+ -I$(top_srcdir)/taglib/ogg/vorbis \
+ -I$(top_srcdir)/taglib/flac \
+ $(all_includes)
+
+lib_LTLIBRARIES = libtag_c.la
+
+libtag_c_la_SOURCES = tag_c.cpp
+taglib_include_HEADERS = tag_c.h
+taglib_includedir = $(includedir)/taglib
+
+libtag_c_la_LDFLAGS = $(all_libraries) -no-undefined -version-info 0:0
+libtag_c_la_LIBADD = ../../libtag.la
+
+EXTRA_DIST = $(libtag_c_la_SOURCES) $(taglib_include_HEADERS)
--- /dev/null
+/***************************************************************************
+ copyright : (C) 2003 by Scott Wheeler
+ email : wheeler@kde.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 "tag_c.h"
+
+#include <fileref.h>
+#include <tfile.h>
+#include <vorbisfile.h>
+#include <mpegfile.h>
+#include <flacfile.h>
+#include <tag.h>
+
+namespace TagLib
+{
+ static List<char *> strings;
+ static bool unicodeStrings = true;
+ static bool stringManagementEnabled = true;
+}
+
+using namespace TagLib;
+
+void taglib_set_strings_unicode(BOOL unicode)
+{
+ unicodeStrings = bool(unicode);
+}
+
+void taglib_set_string_management_enabled(BOOL management)
+{
+ stringManagementEnabled = bool(management);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// TagLib::File wrapper
+////////////////////////////////////////////////////////////////////////////////
+
+TagLib_File *taglib_file_new(const char *filename)
+{
+ return reinterpret_cast<TagLib_File *>(FileRef::create(filename));
+}
+
+TagLib_File *taglib_file_new_type(const char *filename, TagLib_File_Type type)
+{
+ switch(type) {
+ case TagLib_File_MPEG:
+ return reinterpret_cast<TagLib_File *>(new MPEG::File(filename));
+ case TagLib_File_OggVorbis:
+ return reinterpret_cast<TagLib_File *>(new Vorbis::File(filename));
+ case TagLib_File_FLAC:
+ return reinterpret_cast<TagLib_File *>(new FLAC::File(filename));
+ }
+
+ return 0;
+}
+
+void taglib_file_free(TagLib_File *file)
+{
+ delete reinterpret_cast<File *>(file);
+}
+
+TagLib_Tag *taglib_file_tag(const TagLib_File *file)
+{
+ const File *f = reinterpret_cast<const File *>(file);
+ return reinterpret_cast<TagLib_Tag *>(f->tag());
+}
+
+const TagLib_AudioProperties *taglib_file_audioproperties(const TagLib_File *file)
+{
+ const File *f = reinterpret_cast<const File *>(file);
+ return reinterpret_cast<const TagLib_AudioProperties *>(f->audioProperties());
+}
+
+void taglib_file_save(TagLib_File *file)
+{
+ reinterpret_cast<File *>(file)->save();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// TagLib::Tag wrapper
+////////////////////////////////////////////////////////////////////////////////
+
+char *taglib_tag_title(const TagLib_Tag *tag)
+{
+ const Tag *t = reinterpret_cast<const Tag *>(tag);
+ char *s = ::strdup(t->title().toCString(unicodeStrings));
+ if(stringManagementEnabled)
+ strings.append(s);
+ return s;
+}
+
+char *taglib_tag_artist(const TagLib_Tag *tag)
+{
+ const Tag *t = reinterpret_cast<const Tag *>(tag);
+ char *s = ::strdup(t->artist().toCString(unicodeStrings));
+ if(stringManagementEnabled)
+ strings.append(s);
+ return s;
+}
+
+char *taglib_tag_album(const TagLib_Tag *tag)
+{
+ const Tag *t = reinterpret_cast<const Tag *>(tag);
+ char *s = ::strdup(t->album().toCString(unicodeStrings));
+ if(stringManagementEnabled)
+ strings.append(s);
+ return s;
+}
+
+char *taglib_tag_comment(const TagLib_Tag *tag)
+{
+ const Tag *t = reinterpret_cast<const Tag *>(tag);
+ char *s = ::strdup(t->comment().toCString(unicodeStrings));
+ if(stringManagementEnabled)
+ strings.append(s);
+ return s;
+}
+
+char *taglib_tag_genre(const TagLib_Tag *tag)
+{
+ const Tag *t = reinterpret_cast<const Tag *>(tag);
+ char *s = ::strdup(t->genre().toCString(unicodeStrings));
+ if(stringManagementEnabled)
+ strings.append(s);
+ return s;
+}
+
+unsigned int taglib_tag_year(const TagLib_Tag *tag)
+{
+ const Tag *t = reinterpret_cast<const Tag *>(tag);
+ return t->year();
+}
+
+unsigned int taglib_tag_track(const TagLib_Tag *tag)
+{
+ const Tag *t = reinterpret_cast<const Tag *>(tag);
+ return t->track();
+}
+
+void taglib_tag_set_title(TagLib_Tag *tag, const char *title)
+{
+ Tag *t = reinterpret_cast<Tag *>(tag);
+ t->setTitle(String(title, unicodeStrings ? String::UTF8 : String::Latin1));
+}
+
+void taglib_tag_set_artist(TagLib_Tag *tag, const char *artist)
+{
+ Tag *t = reinterpret_cast<Tag *>(tag);
+ t->setArtist(String(artist, unicodeStrings ? String::UTF8 : String::Latin1));
+}
+
+void taglib_tag_set_album(TagLib_Tag *tag, const char *album)
+{
+ Tag *t = reinterpret_cast<Tag *>(tag);
+ t->setAlbum(String(album, unicodeStrings ? String::UTF8 : String::Latin1));
+}
+
+void taglib_tag_set_comment(TagLib_Tag *tag, const char *comment)
+{
+ Tag *t = reinterpret_cast<Tag *>(tag);
+ t->setComment(String(comment, unicodeStrings ? String::UTF8 : String::Latin1));
+}
+
+void taglib_tag_set_genre(TagLib_Tag *tag, const char *genre)
+{
+ Tag *t = reinterpret_cast<Tag *>(tag);
+ t->setGenre(String(genre, unicodeStrings ? String::UTF8 : String::Latin1));
+}
+
+void taglib_tag_set_year(TagLib_Tag *tag, unsigned int year)
+{
+ Tag *t = reinterpret_cast<Tag *>(tag);
+ t->setYear(year);
+}
+
+void taglib_tag_set_track(TagLib_Tag *tag, unsigned int track)
+{
+ Tag *t = reinterpret_cast<Tag *>(tag);
+ t->setTrack(track);
+}
+
+void taglib_tag_free_strings()
+{
+ if(!stringManagementEnabled)
+ return;
+
+ for(List<char *>::Iterator it = strings.begin(); it != strings.end(); ++it)
+ delete [] *it;
+ strings.clear();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// TagLib::AudioProperties wrapper
+////////////////////////////////////////////////////////////////////////////////
+
+int taglib_audioproperties_length(const TagLib_AudioProperties *audioProperties)
+{
+ const AudioProperties *p = reinterpret_cast<const AudioProperties *>(audioProperties);
+ return p->length();
+}
+
+int taglib_audioproperties_bitrate(const TagLib_AudioProperties *audioProperties)
+{
+ const AudioProperties *p = reinterpret_cast<const AudioProperties *>(audioProperties);
+ return p->bitrate();
+}
+
+int taglib_audioproperties_samplerate(const TagLib_AudioProperties *audioProperties)
+{
+ const AudioProperties *p = reinterpret_cast<const AudioProperties *>(audioProperties);
+ return p->sampleRate();
+}
+
+int taglib_audioproperties_channels(const TagLib_AudioProperties *audioProperties)
+{
+ const AudioProperties *p = reinterpret_cast<const AudioProperties *>(audioProperties);
+ return p->channels();
+}
--- /dev/null
+/***************************************************************************
+ copyright : (C) 2003 by Scott Wheeler
+ email : wheeler@kde.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_TAG_C
+#define TAGLIB_TAG_C
+
+/* Do not include this in the main TagLib documentation. */
+#ifndef DO_NOT_DOCUMENT
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef BOOL
+#define BOOL int
+#endif
+
+/*******************************************************************************
+ * [ TagLib C Binding ]
+ *
+ * This is an interface to TagLib's "simple" API, meaning that you can read and
+ * modify media files in a generic, but not specialized way. This is a rough
+ * representation of TagLib::File and TagLib::Tag, for which the documentation
+ * is somewhat more complete and worth consulting.
+ *******************************************************************************/
+
+/*
+ * These are used for type provide some type safety to the C API (as opposed to
+ * using void *, but pointers to them are simply cast to the coresponding C++
+ * types in the implementation.
+ */
+
+typedef struct { int dummy; } TagLib_File;
+typedef struct { int dummy; } TagLib_Tag;
+typedef struct { int dummy; } TagLib_AudioProperties;
+
+/*!
+ * By default all strings coming into or out of TagLib's C API are in UTF8.
+ * However, it may be desirable for TagLib to operate on Latin1 (ISO-8859-1)
+ * strings in which case this should be set to FALSE.
+ */
+void taglib_set_strings_unicode(BOOL unicode);
+
+/*!
+ * TagLib can keep track of strings that are created when outputting tag values
+ * and clear them using taglib_tag_clear_strings(). This is enabled by default.
+ * However if you wish to do more fine grained management of strings, you can do
+ * so by setting \a management to FALSE.
+ */
+void taglib_set_string_management_enabled(BOOL management);
+
+/*******************************************************************************
+ * File API
+ ******************************************************************************/
+
+typedef enum {
+ TagLib_File_MPEG,
+ TagLib_File_OggVorbis,
+ TagLib_File_FLAC
+} TagLib_File_Type;
+
+/*!
+ * Creates a TagLib file based on \a filename. TagLib will try to guess the file
+ * type.
+ */
+TagLib_File *taglib_file_new(const char *filename);
+
+/*!
+ * Creates a TagLib file based on \a filename. Rather than attempting to guess
+ * the type, it will use the one specified by \a type.
+ */
+TagLib_File *taglib_file_new_type(const char *filename, TagLib_File_Type type);
+
+/*!
+ * Frees and closes the file.
+ */
+void taglib_file_free(TagLib_File *file);
+
+/*!
+ * Returns a pointer to the tag associated with this file. This will be freed
+ * automatically when the file is freed.
+ */
+TagLib_Tag *taglib_file_tag(const TagLib_File *file);
+
+/*!
+ * Returns a pointer to the the audio properties associated with this file. This
+ * will be freed automatically when the file is freed.
+ */
+const TagLib_AudioProperties *taglib_file_audioproperties(const TagLib_File *file);
+
+/*!
+ * Saves the \a file to disk.
+ */
+void taglib_file_save(TagLib_File *file);
+
+/******************************************************************************
+ * Tag API
+ ******************************************************************************/
+
+/*!
+ * Returns a string with this tag's title.
+ *
+ * \note By default this string should be UTF8 encoded and its memory should be
+ * freed using taglib_tag_free_strings().
+ */
+char *taglib_tag_title(const TagLib_Tag *tag);
+
+/*!
+ * Returns a string with this tag's artist.
+ *
+ * \note By default this string should be UTF8 encoded and its memory should be
+ * freed using taglib_tag_free_strings().
+ */
+char *taglib_tag_artist(const TagLib_Tag *tag);
+
+/*!
+ * Returns a string with this tag's album name.
+ *
+ * \note By default this string should be UTF8 encoded and its memory should be
+ * freed using taglib_tag_free_strings().
+ */
+char *taglib_tag_album(const TagLib_Tag *tag);
+
+/*!
+ * Returns a string with this tag's comment.
+ *
+ * \note By default this string should be UTF8 encoded and its memory should be
+ * freed using taglib_tag_free_strings().
+ */
+char *taglib_tag_comment(const TagLib_Tag *tag);
+
+/*!
+ * Returns a string with this tag's genre.
+ *
+ * \note By default this string should be UTF8 encoded and its memory should be
+ * freed using taglib_tag_free_strings().
+ */
+char *taglib_tag_genre(const TagLib_Tag *tag);
+
+/*!
+ * Returns the tag's year or 0 if year is not set.
+ */
+unsigned int taglib_tag_year(const TagLib_Tag *tag);
+
+/*!
+ * Returns the tag's track number or 0 if track number is not set.
+ */
+unsigned int taglib_tag_track(const TagLib_Tag *tag);
+
+/*!
+ * Sets the tag's title.
+ *
+ * \note By default this string should be UTF8 encoded.
+ */
+void taglib_tag_set_title(TagLib_Tag *tag, const char *title);
+
+/*!
+ * Sets the tag's artist.
+ *
+ * \note By default this string should be UTF8 encoded.
+ */
+void taglib_tag_set_artist(TagLib_Tag *tag, const char *artist);
+
+/*!
+ * Sets the tag's album.
+ *
+ * \note By default this string should be UTF8 encoded.
+ */
+void taglib_tag_set_album(TagLib_Tag *tag, const char *album);
+
+/*!
+ * Sets the tag's comment.
+ *
+ * \note By default this string should be UTF8 encoded.
+ */
+void taglib_tag_set_comment(TagLib_Tag *tag, const char *comment);
+
+/*!
+ * Sets the tag's genre.
+ *
+ * \note By default this string should be UTF8 encoded.
+ */
+void taglib_tag_set_genre(TagLib_Tag *tag, const char *genre);
+
+/*!
+ * Sets the tag's year. 0 indicates that this field should be cleared.
+ */
+void taglib_tag_set_year(TagLib_Tag *tag, unsigned int year);
+
+/*!
+ * Sets the tag's track number. 0 indicates that this field should be cleared.
+ */
+void taglib_tag_set_track(TagLib_Tag *tag, unsigned int track);
+
+/*!
+ * Frees all of the strings that have been created by the tag.
+ */
+void taglib_tag_free_strings();
+
+/******************************************************************************
+ * Audio Properties API
+ ******************************************************************************/
+
+/*!
+ * Returns the lenght of the file in seconds.
+ */
+int taglib_audioproperties_length(const TagLib_AudioProperties *audioProperties);
+
+/*!
+ * Returns the bitrate of the file in kb/s.
+ */
+int taglib_audioproperties_bitrate(const TagLib_AudioProperties *audioProperties);
+
+/*!
+ * Returns the sample rate of the file in Hz.
+ */
+int taglib_audioproperties_samplerate(const TagLib_AudioProperties *audioProperties);
+
+/*!
+ * Returns the number of channels in the audio stream.
+ */
+int taglib_audioproperties_channels(const TagLib_AudioProperties *audioProperties);
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* DO_NOT_DOCUMENT */
+#endif
--- /dev/null
+#AM_INIT_AUTOMAKE(taglib,1.0)
+dnl don't remove the below
+dnl AC_OUTPUT(taglib/taglib-config)
--- /dev/null
+bin_PROGRAMS = tagreader tagreader_c tagwriter framelist strip-id3v1
+tagreader_SOURCES = tagreader.cpp
+tagreader_c_SOURCES = tagreader_c.c
+tagwriter_SOURCES = tagwriter.cpp
+framelist_SOURCES = framelist.cpp
+strip_id3v1_SOURCES = strip-id3v1.cpp
+
+INCLUDES = \
+ -I$(top_srcdir)/taglib \
+ -I$(top_srcdir)/taglib/toolkit \
+ -I$(top_srcdir)/taglib/mpeg \
+ -I$(top_srcdir)/taglib/mpeg/id3v1 \
+ -I$(top_srcdir)/taglib/mpeg/id3v2 \
+ -I$(top_srcdir)/taglib/bindings/c
+
+LDADD = ../libtag.la
+tagreader_c_LDADD = ../bindings/c/libtag_c.la
--- /dev/null
+/* Copyright (C) 2003 Scott Wheeler <wheeler@kde.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <iostream>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <tbytevector.h>
+
+#include <mpegfile.h>
+
+#include <id3v2tag.h>
+#include <id3v2frame.h>
+#include <id3v2header.h>
+
+#include <id3v1tag.h>
+
+using namespace std;
+using namespace TagLib;
+
+int main(int argc, char *argv[])
+{
+ // process the command line args
+
+
+ for(int i = 1; i < argc; i++) {
+
+ cout << "******************** \"" << argv[i] << "\"********************" << endl;
+
+ MPEG::File f(argv[i]);
+
+ ID3v2::Tag *id3v2tag = f.ID3v2Tag();
+
+ if(id3v2tag) {
+
+ cout << "ID3v2."
+ << id3v2tag->header()->majorVersion()
+ << "."
+ << id3v2tag->header()->revisionNumber()
+ << ", "
+ << id3v2tag->header()->tagSize()
+ << " bytes in tag"
+ << endl;
+
+ ID3v2::FrameList::ConstIterator it = id3v2tag->frameList().begin();
+ for(; it != id3v2tag->frameList().end(); it++)
+ cout << (*it)->frameID() << " - \"" << (*it)->toString() << "\"" << endl;
+ }
+ else
+ cout << "file does not have a valid id3v2 tag" << endl;
+
+ cout << endl << "ID3v1" << endl;
+
+ ID3v1::Tag *id3v1tag = f.ID3v1Tag();
+
+ if(id3v1tag) {
+ cout << "title - \"" << id3v1tag->title() << "\"" << endl;
+ cout << "artist - \"" << id3v1tag->artist() << "\"" << endl;
+ cout << "album - \"" << id3v1tag->album() << "\"" << endl;
+ cout << "year - \"" << id3v1tag->year() << "\"" << endl;
+ cout << "comment - \"" << id3v1tag->comment() << "\"" << endl;
+ cout << "track - \"" << id3v1tag->track() << "\"" << endl;
+ cout << "genre - \"" << id3v1tag->genre() << "\"" << endl;
+ }
+ else
+ cout << "file does not have a valid id3v1 tag" << endl;
+
+ cout << endl;
+ }
+}
--- /dev/null
+/* Copyright (C) 2003 Scott Wheeler <wheeler@kde.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <iostream>
+#include <mpegfile.h>
+#include <tstring.h>
+
+using namespace TagLib;
+
+int main(int argc, char *argv[])
+{
+ for(int i = 1; i < argc; i++) {
+
+ std::cout << "******************** Stripping ID3v1 Tag From: \"" << argv[i] << "\"********************" << std::endl;
+
+ MPEG::File f(argv[i]);
+ f.strip(MPEG::File::ID3v1);
+ }
+}
--- /dev/null
+/* Copyright (C) 2003 Scott Wheeler <wheeler@kde.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <iostream>
+#include <stdio.h>
+
+#include <fileref.h>
+#include <tag.h>
+
+using namespace std;
+
+TagLib::String formatSeconds(int seconds)
+{
+ char secondsString[3];
+ sprintf(secondsString, "%02i", seconds);
+ return secondsString;
+}
+
+int main(int argc, char *argv[])
+{
+ for(int i = 1; i < argc; i++) {
+
+ cout << "******************** \"" << argv[i] << "\" ********************" << endl;
+
+ TagLib::FileRef f(argv[i]);
+
+ if(!f.isNull() && f.tag()) {
+
+ TagLib::Tag *tag = f.tag();
+
+ cout << "-- TAG --" << endl;
+ cout << "title - \"" << tag->title() << "\"" << endl;
+ cout << "artist - \"" << tag->artist() << "\"" << endl;
+ cout << "album - \"" << tag->album() << "\"" << endl;
+ cout << "year - \"" << tag->year() << "\"" << endl;
+ cout << "comment - \"" << tag->comment() << "\"" << endl;
+ cout << "track - \"" << tag->track() << "\"" << endl;
+ cout << "genre - \"" << tag->genre() << "\"" << endl;
+ }
+
+ if(!f.isNull() && f.audioProperties()) {
+
+ TagLib::AudioProperties *properties = f.audioProperties();
+
+ int seconds = properties->length() % 60;
+ int minutes = (properties->length() - seconds) / 60;
+
+ cout << "-- AUDIO --" << endl;
+ cout << "bitrate - " << properties->bitrate() << endl;
+ cout << "sample rate - " << properties->sampleRate() << endl;
+ cout << "channels - " << properties->channels() << endl;
+ cout << "length - " << minutes << ":" << formatSeconds(seconds) << endl;
+ }
+ }
+ return 0;
+}
--- /dev/null
+/* Copyright (C) 2003 Scott Wheeler <wheeler@kde.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <tag_c.h>
+
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+int main(int argc, char *argv[])
+{
+ int i;
+ int seconds;
+ int minutes;
+ TagLib_File *file;
+ TagLib_Tag *tag;
+ const TagLib_AudioProperties *properties;
+
+ taglib_set_strings_unicode(FALSE);
+
+ for(i = 1; i < argc; i++) {
+ printf("******************** \"%s\" ********************\n", argv[i]);
+
+ file = taglib_file_new(argv[i]);
+ tag = taglib_file_tag(file);
+ properties = taglib_file_audioproperties(file);
+
+ printf("-- TAG --\n");
+ printf("title - \"%s\"\n", taglib_tag_title(tag));
+ printf("artist - \"%s\"\n", taglib_tag_artist(tag));
+ printf("album - \"%s\"\n", taglib_tag_album(tag));
+ printf("year - \"%i\"\n", taglib_tag_year(tag));
+ printf("comment - \"%s\"\n", taglib_tag_comment(tag));
+ printf("track - \"%i\"\n", taglib_tag_track(tag));
+ printf("genre - \"%s\"\n", taglib_tag_genre(tag));
+
+ seconds = taglib_audioproperties_length(properties) % 60;
+ minutes = (taglib_audioproperties_length(properties) - seconds) / 60;
+
+ printf("-- AUDIO --\n");
+ printf("bitrate - %i\n", taglib_audioproperties_bitrate(properties));
+ printf("sample rate - %i\n", taglib_audioproperties_samplerate(properties));
+ printf("channels - %i\n", taglib_audioproperties_channels(properties));
+ printf("length - %i:%02i\n", minutes, seconds);
+
+ taglib_tag_free_strings();
+ taglib_file_free(file);
+ }
+
+ return 0;
+}
--- /dev/null
+/* Copyright (C) 2004 Scott Wheeler <wheeler@kde.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <iostream>
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <tlist.h>
+#include <fileref.h>
+#include <tfile.h>
+#include <tag.h>
+
+using namespace std;
+
+bool isArgument(const char *s)
+{
+ return strlen(s) == 2 && s[0] == '-';
+}
+
+bool isFile(const char *s)
+{
+ struct stat st;
+ return ::stat(s, &st) == 0 && (st.st_mode & (S_IFREG | S_IFLNK));
+}
+
+void usage()
+{
+ cout << endl;
+ cout << "Usage: tagwriter <fields> <files>" << endl;
+ cout << endl;
+ cout << "Where the valid fields are:" << endl;
+ cout << " -t <title>" << endl;
+ cout << " -a <artist>" << endl;
+ cout << " -A <album>" << endl;
+ cout << " -c <comment>" << endl;
+ cout << " -g <genre>" << endl;
+ cout << " -y <year>" << endl;
+ cout << " -T <track>" << endl;
+ cout << endl;
+
+ exit(1);
+}
+
+int main(int argc, char *argv[])
+{
+ TagLib::List<TagLib::FileRef> fileList;
+
+ while(argc > 0 && isFile(argv[argc - 1])) {
+
+ TagLib::FileRef f(argv[argc - 1]);
+
+ if(!f.isNull() && f.tag())
+ fileList.append(f);
+
+ argc--;
+ }
+
+ if(fileList.isEmpty())
+ usage();
+
+ for(int i = 1; i < argc - 1; i += 2) {
+
+ if(isArgument(argv[i]) && i + 1 < argc && !isArgument(argv[i + 1])) {
+
+ char field = argv[i][1];
+ TagLib::String value = argv[i + 1];
+
+ TagLib::List<TagLib::FileRef>::Iterator it;
+ for(it = fileList.begin(); it != fileList.end(); ++it) {
+
+ TagLib::Tag *t = (*it).tag();
+
+ switch (field) {
+ case 't':
+ t->setTitle(value);
+ break;
+ case 'a':
+ t->setArtist(value);
+ break;
+ case 'A':
+ t->setAlbum(value);
+ break;
+ case 'c':
+ t->setComment(value);
+ break;
+ case 'g':
+ t->setGenre(value);
+ break;
+ case 'y':
+ t->setYear(value.toInt());
+ break;
+ case 'T':
+ t->setTrack(value.toInt());
+ break;
+ default:
+ usage();
+ break;
+ }
+ }
+ }
+ else
+ usage();
+ }
+
+ TagLib::List<TagLib::FileRef>::Iterator it;
+ for(it = fileList.begin(); it != fileList.end(); ++it)
+ (*it).file()->save();
+
+ return 0;
+}
--- /dev/null
+/***************************************************************************
+ copyright : (C) 2003 by Scott Wheeler
+ email : wheeler@kde.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 <tfile.h>
+#include <tstring.h>
+
+#include "fileref.h"
+#include "mpegfile.h"
+#include "vorbisfile.h"
+#include "flacfile.h"
+
+using namespace TagLib;
+
+class FileRef::FileRefPrivate : public RefCounter
+{
+public:
+ FileRefPrivate(File *f) : RefCounter(), file(f) {}
+ ~FileRefPrivate() {
+ delete file;
+ }
+
+ File *file;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// public members
+////////////////////////////////////////////////////////////////////////////////
+
+FileRef::FileRef(const char *fileName, bool readAudioProperties,
+ AudioProperties::ReadStyle audioPropertiesStyle)
+{
+ d = new FileRefPrivate(create(fileName, readAudioProperties, audioPropertiesStyle));
+}
+
+FileRef::FileRef(File *file)
+{
+ d = new FileRefPrivate(file);
+}
+
+FileRef::FileRef(const FileRef &ref) : d(ref.d)
+{
+ d->ref();
+}
+
+FileRef::~FileRef()
+{
+ if(d->deref())
+ delete d;
+}
+
+Tag *FileRef::tag() const
+{
+ return d->file->tag();
+}
+
+AudioProperties *FileRef::audioProperties() const
+{
+ return d->file->audioProperties();
+}
+
+File *FileRef::file() const
+{
+ return d->file;
+}
+
+void FileRef::save()
+{
+ d->file->save();
+}
+
+bool FileRef::isNull() const
+{
+ return !d->file || !d->file->isValid();
+}
+
+FileRef &FileRef::operator=(const FileRef &ref)
+{
+ if(&ref == this)
+ return *this;
+
+ if(d->deref())
+ delete d;
+
+ d = ref.d;
+
+ return *this;
+}
+
+bool FileRef::operator==(const FileRef &ref) const
+{
+ return ref.d->file == d->file;
+}
+
+bool FileRef::operator!=(const FileRef &ref) const
+{
+ return ref.d->file != d->file;
+}
+
+File *FileRef::create(const char *fileName, bool readAudioProperties,
+ AudioProperties::ReadStyle audioPropertiesStyle) // static
+{
+ // Ok, this is really dumb for now, but it works for testing.
+
+ String s = fileName;
+
+ if(s.size() > 4) {
+ if(s.substr(s.size() - 4, 4).upper() == ".OGG")
+ return new Vorbis::File(fileName, readAudioProperties, audioPropertiesStyle);
+ if(s.substr(s.size() - 4, 4).upper() == ".MP3")
+ return new MPEG::File(fileName, readAudioProperties, audioPropertiesStyle);
+ if(s.substr(s.size() - 5, 5).upper() == ".FLAC")
+ return new FLAC::File(fileName, readAudioProperties, audioPropertiesStyle);
+ }
+
+ return 0;
+}
--- /dev/null
+/***************************************************************************
+ copyright : (C) 2003 by Scott Wheeler
+ email : wheeler@kde.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_FILEREF_H
+#define TAGLIB_FILEREF_H
+
+#include "audioproperties.h"
+
+namespace TagLib {
+
+ class String;
+ class File;
+ class Tag;
+
+ //! This class provides a simple abstraction for creating and handling files
+
+ /*!
+ * FileRef exists to provide a minimal, generic and value-based wrapper around
+ * a File. It is lightweight and implicitly shared, and as such suitable for
+ * pass-by-value use. This hides some of the uglier details of TagLib::File
+ * and the non-generic portions of the concrete file implementations.
+ *
+ * This class is useful in a "simple usage" situation where it is desirable
+ * to be able to get and set some of the tag information that is similar
+ * across file types.
+ *
+ * Also note that it is probably a good idea to plug this into your mime
+ * type system rather than using the constructor that accepts a file name.
+ *
+ * For example in KDE this could be done with:
+ *
+ * \code
+ *
+ * TagLib::FileRef createFileRef( const QString &fileName )
+ * {
+ * KMimeType::Ptr result = KMimeType::findByPath( fileName, 0, true );
+ *
+ * if( result->name() == "audio/x-mp3" )
+ * return FileRef( new MPEG::File( QFile::encodeName( fileName ).data() ) );
+ *
+ * if( result->name() == "application/ogg" )
+ * return FileRef( new Vorbis::File( QFile::encodeName( fileName ).data() ) );
+ *
+ * return FileRef( 0 );
+ * }
+ *
+ * \endcode
+ */
+
+ class FileRef
+ {
+ public:
+
+ /*!
+ * Create a FileRef from \a fileName. If \a readAudioProperties is true then
+ * the audio properties will be read using \a audioPropertiesStyle. If
+ * \a readAudioProperties is false then \a audioPropertiesStyle will be
+ * ignored.
+ *
+ * Also see the note in the class documentation about why you may not want to
+ * use this method in your application.
+ */
+ explicit FileRef(const char *fileName,
+ bool readAudioProperties = true,
+ AudioProperties::ReadStyle
+ audioPropertiesStyle = AudioProperties::Average);
+
+ /*!
+ * Contruct a FileRef using \a file. The FileRef now takes ownership of the
+ * pointer and will delete the File when it passes out of scope.
+ */
+ explicit FileRef(File *file);
+
+ /*!
+ * Make a copy of \a ref.
+ */
+ FileRef(const FileRef &ref);
+
+ /*!
+ * Destroys this FileRef instance.
+ */
+ virtual ~FileRef();
+
+ /*!
+ * Returns a pointer to represented file's tag.
+ *
+ * \warning This pointer will become invalid when this FileRef and all
+ * copies pass out of scope.
+ *
+ * \see File::tag()
+ */
+ Tag *tag() const;
+
+ /*!
+ * Returns the audio properties for this FileRef. If no audio properties
+ * were read then this will returns a null pointer.
+ */
+ AudioProperties *audioProperties() const;
+
+ /*!
+ * Returns a pointer to the file represented by this handler class.
+ *
+ * As a general rule this call should be avoided since if you need to work
+ * with file objects directly, you are probably better served instantiating
+ * the File subclasses (i.e. MPEG::File) manually and working with their APIs.
+ *
+ * This <i>handle</i> exists to provide a minimal, generic and value-based
+ * wrapper around a File. Accessing the file directly generally indicates
+ * a moving away from this simplicity (and into things beyond the scope of
+ * FileRef).
+ *
+ * \warning This pointer will become invalid when this FileRef and all
+ * copies pass out of scope.
+ */
+ File *file() const;
+
+ /*!
+ * Saves the file.
+ */
+ void save();
+
+ /*!
+ * Returns true if the file (and as such other pointers) are null.
+ */
+ bool isNull() const;
+
+ /*!
+ * Assign the file pointed to by \a ref to this FileRef.
+ */
+ FileRef &operator=(const FileRef &ref);
+
+ /*!
+ * Returns true if this FileRef and \a ref point to the same File object.
+ */
+ bool operator==(const FileRef &ref) const;
+
+ /*!
+ * Returns true if this FileRef and \a ref do not point to the same File
+ * object.
+ */
+ bool operator!=(const FileRef &ref) const;
+
+ /*!
+ * A simple implementation of file type guessing. If \a readAudioProperties
+ * is true then the audio properties will be read using
+ * \a audioPropertiesStyle. If \a readAudioProperties is false then
+ * \a audioPropertiesStyle will be ignored.
+ *
+ * \note You generally shouldn't use this method, but instead the constructor
+ * directly.
+ */
+ static File *create(const char *fileName,
+ bool readAudioProperties = true,
+ AudioProperties::ReadStyle audioPropertiesStyle = AudioProperties::Average);
+
+ private:
+ class FileRefPrivate;
+ FileRefPrivate *d;
+ };
+
+} // namespace TagLib
+
+#endif
--- /dev/null
+INCLUDES = \
+ -I$(top_srcdir)/taglib \
+ -I$(top_srcdir)/taglib/toolkit \
+ -I$(top_srcdir)/taglib/ogg \
+ -I$(top_srcdir)/taglib/mpeg/id3v2 \
+ -I$(top_srcdir)/taglib/mpeg/id3v1 \
+ $(all_includes)
+
+noinst_LTLIBRARIES = libflac.la
+
+libflac_la_SOURCES = flacfile.cpp flacproperties.cpp
+
+taglib_include_HEADERS = flacfile.h flacproperties.h
+taglib_includedir = $(includedir)/taglib
+
+EXTRA_DIST = $(libflac_la_SOURCES) $(taglib_include_HEADERS)
--- /dev/null
+/***************************************************************************
+ copyright : (C) 2003 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 <id3v2header.h>
+
+#include "flacfile.h"
+#include "flactag.h"
+
+using namespace TagLib;
+
+class FLAC::File::FilePrivate
+{
+public:
+ FilePrivate() :
+ ID3v2FrameFactory(ID3v2::FrameFactory::instance()),
+ ID3v2Tag(0),
+ ID3v2Location(-1),
+ ID3v2OriginalSize(0),
+ ID3v1Tag(0),
+ ID3v1Location(-1),
+ comment(0),
+ properties(0),
+ flacStart(0),
+ streamStart(0),
+ streamLength(0),
+ scanned(false),
+ hasXiphComment(false),
+ hasID3v2(false),
+ hasID3v1(false) {}
+
+ ~FilePrivate()
+ {
+ delete ID3v2Tag;
+ delete ID3v1Tag;
+ delete comment;
+ delete properties;
+ }
+
+ const ID3v2::FrameFactory *ID3v2FrameFactory;
+ ID3v2::Tag *ID3v2Tag;
+ long ID3v2Location;
+ uint ID3v2OriginalSize;
+
+ ID3v1::Tag *ID3v1Tag;
+ long ID3v1Location;
+
+ Ogg::XiphComment *comment;
+
+ FLAC::Tag *tag;
+
+ Properties *properties;
+ ByteVector streamInfoData;
+ ByteVector xiphCommentData;
+ long flacStart;
+ long streamStart;
+ long streamLength;
+ bool scanned;
+
+ bool hasXiphComment;
+ bool hasID3v2;
+ bool hasID3v1;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// public members
+////////////////////////////////////////////////////////////////////////////////
+
+FLAC::File::File(const char *file, bool readProperties,
+ Properties::ReadStyle propertiesStyle) : TagLib::File(file)
+{
+ d = new FilePrivate;
+ read(readProperties, propertiesStyle);
+}
+
+FLAC::File::~File()
+{
+ delete d;
+}
+
+TagLib::Tag *FLAC::File::tag() const
+{
+ return d->tag;
+}
+
+FLAC::Properties *FLAC::File::audioProperties() const
+{
+ return d->properties;
+}
+
+
+void FLAC::File::save()
+{
+ // Create new vorbis comments
+
+ if(!d->comment) {
+ d->comment = new Ogg::XiphComment;
+ if(d->tag)
+ Tag::duplicate(d->tag, d->comment, true);
+ }
+
+ d->xiphCommentData = d->comment->render();
+
+ ByteVector v = ByteVector::fromUInt(d->xiphCommentData.size());
+
+ // Set the type of the comment to be a Xiph / Vorbis comment
+ // (See scan() for comments on header-format)
+ v[0] = 4;
+ v.append(d->xiphCommentData);
+
+
+ // If file already have comment => find and update it
+ // if not => insert one
+ // TODO: Search for padding and use that
+
+ if(d->hasXiphComment) {
+ long nextPageOffset = d->flacStart;
+ seek(nextPageOffset);
+ ByteVector header = readBlock(4);
+ uint length = header.mid(1, 3).toUInt();
+
+ nextPageOffset += length + 4;
+
+ // Search through the remaining metadata
+
+ char blocktype = header[0] & 0x7f;
+ bool lastblock = header[0] & 0x80;
+
+ while(!lastblock) {
+ seek(nextPageOffset);
+
+ header = readBlock(4);
+ blocktype = header[0] & 0x7f;
+ lastblock = header[0] & 0x80;
+ length = header.mid(1, 3).toUInt();
+
+ // Type is vorbiscomment
+ if( blocktype == 4 ) {
+ v[0] = header[0];
+ insert(v, nextPageOffset, length + 4);
+ break;
+ }
+
+ nextPageOffset += length + 4;
+ }
+ }
+ else {
+ long nextPageOffset = d->flacStart;
+
+ seek(nextPageOffset);
+
+ ByteVector header = readBlock(4);
+ // char blockType = header[0] & 0x7f;
+ bool lastBlock = header[0] & 0x80;
+ uint length = header.mid(1, 3).toUInt();
+
+ // If last block was last, make this one last
+
+ if(lastBlock) {
+
+ // Copy the bottom seven bits into the new value
+
+ ByteVector h(static_cast<char>(header[0] & 0x7F));
+ insert(h, nextPageOffset, 1);
+
+ // Set the last bit
+ v[0] |= 0x80;
+ }
+
+ insert(v, nextPageOffset + length + 4, 0);
+ d->hasXiphComment = true;
+ }
+
+ // Update ID3 tag
+
+ if(d->hasID3v2 && d->ID3v2Tag)
+ insert(d->ID3v2Tag->render(), d->ID3v2Location, d->ID3v2OriginalSize);
+
+ if(d->hasID3v1 && d->ID3v1Tag) {
+ seek(-128, End);
+ writeBlock(d->ID3v1Tag->render());
+ }
+
+}
+
+void FLAC::File::setID3v2FrameFactory(const ID3v2::FrameFactory *factory)
+{
+ d->ID3v2FrameFactory = factory;
+}
+
+
+////////////////////////////////////////////////////////////////////////////////
+// private members
+////////////////////////////////////////////////////////////////////////////////
+
+void FLAC::File::read(bool readProperties, Properties::ReadStyle propertiesStyle)
+{
+ // Look for an ID3v2 tag
+
+ d->ID3v2Location = findID3v2();
+
+ if(d->ID3v2Location >= 0) {
+
+ d->ID3v2Tag = new ID3v2::Tag(this, d->ID3v2Location, d->ID3v2FrameFactory);
+
+ d->ID3v2OriginalSize = d->ID3v2Tag->header()->completeTagSize();
+
+ if(d->ID3v2Tag->header()->tagSize() <= 0) {
+ delete d->ID3v2Tag;
+ d->ID3v2Tag = 0;
+ }
+ else
+ d->hasID3v2 = true;
+ }
+
+ // Look for an ID3v1 tag
+
+ d->ID3v1Location = findID3v1();
+
+ if(d->ID3v1Location >= 0) {
+ d->ID3v1Tag = new ID3v1::Tag(this, d->ID3v1Location);
+ d->hasID3v1 = true;
+ }
+
+ // Look for FLAC metadata, including vorbis comments
+
+ scan();
+
+ if(d->hasXiphComment)
+ d->comment = new Ogg::XiphComment(xiphCommentData());
+
+ if(d->hasXiphComment || d->hasID3v2 || d->hasID3v1)
+ d->tag = new FLAC::Tag(d->comment, d->ID3v2Tag, d->ID3v1Tag);
+ else
+ d->tag = new FLAC::Tag(new Ogg::XiphComment);
+
+ if(readProperties)
+ d->properties = new Properties(this, propertiesStyle);
+}
+
+ByteVector FLAC::File::streamInfoData()
+{
+ scan();
+ return d->streamInfoData;
+}
+
+ByteVector FLAC::File::xiphCommentData()
+{
+ scan();
+ return d->xiphCommentData;
+}
+
+long FLAC::File::streamLength()
+{
+ return d->streamLength;
+}
+
+void FLAC::File::scan()
+{
+ // Scan the metadata pages
+
+ if(d->scanned)
+ return;
+
+ if(!isValid())
+ return;
+
+ // Optimization: Use ID3v2 size and only skip that
+
+ long nextPageOffset = find("fLaC");
+
+ if(nextPageOffset < 0) {
+ debug("FLAC::File::scan() -- FLAC stream not found");
+ return;
+ }
+
+ nextPageOffset += 4;
+ d->flacStart = nextPageOffset;
+
+ seek(nextPageOffset);
+
+ ByteVector header = readBlock(4);
+
+ // Header format (from spec):
+ // <1> Last-metadata-block flag
+ // <7> BLOCK_TYPE
+ // 0 : STREAMINFO
+ // 1 : PADDING
+ // ..
+ // 4 : VORBIS_COMMENT
+ // ..
+ // <24> Length of metadata to follow
+
+ char blockType = header[0] & 0x7f;
+ bool lastBlock = header[0] & 0x80;
+ uint length = header.mid(1, 3).toUInt();
+
+ // First block should be the stream_info metadata
+ if(blockType != 0) {
+ debug("FLAC::File::scan() -- invalid FLAC stream");
+ return;
+ }
+
+ d->streamInfoData = readBlock(length);
+ nextPageOffset += length + 4;
+
+ // Search through the remaining metadata
+
+ while(!lastBlock) {
+
+ header = readBlock(4);
+ blockType = header[0] & 0x7f;
+ lastBlock = header[0] & 0x80;
+ length = header.mid(1, 3).toUInt();
+
+ // Found the vorbis-comment
+ if(blockType == 4) {
+ d->xiphCommentData = readBlock(length);
+ d->hasXiphComment = true;
+
+ }
+
+ nextPageOffset += length + 4;
+ seek(nextPageOffset);
+ }
+
+ // End of metadata, now comes the datastream
+ d->streamStart = nextPageOffset;
+ d->streamLength = File::length() - d->streamStart;
+
+ d->scanned = true;
+}
+
+long FLAC::File::findID3v1()
+{
+ if(!isValid())
+ return -1;
+
+ seek(-128, End);
+ long p = tell();
+
+ if(readBlock(3) == ID3v1::Tag::fileIdentifier())
+ return p;
+
+ return -1;
+}
+
+long FLAC::File::findID3v2()
+{
+ if(!isValid())
+ return -1;
+
+ seek(0);
+
+ if(readBlock(3) == ID3v2::Header::fileIdentifier())
+ return 0;
+
+ return -1;
+}
--- /dev/null
+/***************************************************************************
+ copyright : (C) 2003 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_FLACFILE_H
+#define TAGLIB_FLACFILE_H
+
+#include <tfile.h>
+
+#include "flacproperties.h"
+
+namespace TagLib {
+
+ class Tag;
+ namespace ID3v2 { class FrameFactory; }
+
+ //! An implementation of FLAC metadata
+
+ /*!
+ * This is implementation of FLAC metadata for non-Ogg FLAC files. At some
+ * point when Ogg / FLAC is more common there will be a similar implementation
+ * under the Ogg hiearchy.
+ *
+ * This supports ID3v1, ID3v2 and Xiph style comments as well as reading stream
+ * properties from the file.
+ */
+
+ namespace FLAC {
+
+ //! An implementation of TagLib::File with FLAC specific methods
+
+ /*!
+ * This implements and provides an interface for FLAC 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 FLAC files.
+ */
+
+ class File : public TagLib::File
+ {
+ public:
+ /*!
+ * Contructs an FLAC 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 a union of XiphComment
+ * with ID3v1 and ID3v2 tags.
+ */
+ virtual TagLib::Tag *tag() const;
+
+ /*!
+ * Returns the FLAC::Properties for this file. If no audio properties
+ * were read then this will return a null pointer.
+ */
+ virtual Properties *audioProperties() const;
+
+ /*!
+ * Save the file. This will primarily save the XiphComment, but
+ * will also keep any old ID3-tags up to date. If the file
+ * has no XiphComment, one will be constructed from the ID3-tags.
+ */
+ virtual void save();
+
+ /*!
+ * Set the ID3v2::FrameFactory to something other than the default. This
+ * can be used to specify the way that ID3v2 frames will be interpreted
+ * when
+ *
+ * \see ID3v2FrameFactory
+ */
+ void setID3v2FrameFactory(const ID3v2::FrameFactory *factory);
+
+ /*!
+ * Returns the block of data used by FLAC::Properties for parsing the
+ * stream properties.
+ */
+ ByteVector streamInfoData();
+
+ /*!
+ * Returns the length of the audio-stream, used by FLAC::Properties for
+ * calculating the bitrate.
+ */
+ long streamLength();
+
+ private:
+ File(const File &);
+ File &operator=(const File &);
+
+ void read(bool readProperties, Properties::ReadStyle propertiesStyle);
+ void scan();
+ long findID3v2();
+ long findID3v1();
+ ByteVector xiphCommentData();
+
+ class FilePrivate;
+ FilePrivate *d;
+ };
+ }
+}
+
+#endif
--- /dev/null
+/***************************************************************************
+ copyright : (C) 2003 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 "flacproperties.h"
+#include "flacfile.h"
+
+using namespace TagLib;
+
+class FLAC::Properties::PropertiesPrivate
+{
+public:
+ PropertiesPrivate(File *f, ReadStyle s) :
+ file(f),
+ style(s),
+ length(0),
+ bitrate(0),
+ sampleRate(0),
+ sampleWidth(0),
+ channels(0) {}
+
+ File *file;
+ ReadStyle style;
+ int length;
+ int bitrate;
+ int sampleRate;
+ int sampleWidth;
+ int channels;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// public members
+////////////////////////////////////////////////////////////////////////////////
+
+FLAC::Properties::Properties(File *file, ReadStyle style) : AudioProperties(style)
+{
+ d = new PropertiesPrivate(file, style);
+ read();
+}
+
+FLAC::Properties::~Properties()
+{
+ delete d;
+}
+
+int FLAC::Properties::length() const
+{
+ return d->length;
+}
+
+int FLAC::Properties::bitrate() const
+{
+ return d->bitrate;
+}
+
+int FLAC::Properties::sampleRate() const
+{
+ return d->sampleRate;
+}
+
+int FLAC::Properties::sampleWidth() const
+{
+ return d->sampleWidth;
+}
+
+int FLAC::Properties::channels() const
+{
+ return d->channels;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// private members
+////////////////////////////////////////////////////////////////////////////////
+
+void FLAC::Properties::read()
+{
+ // Get the identification header.
+
+ ByteVector data = d->file->streamInfoData();
+
+ int pos = 0;
+
+ // Minimum block size (in samples)
+ pos += 2;
+
+ // Maximum block size (in samples)
+ pos += 2;
+
+ // Minimum frame size (in bytes)
+ pos += 3;
+
+ // Maximum frame size (in bytes)
+ pos += 3;
+
+ uint flags = data.mid(pos, 4).toUInt(true);
+ d->sampleRate = flags >> 12;
+ d->channels = ((flags >> 9) & 7) + 1;
+ d->sampleWidth = ((flags >> 4) & 31) + 1;
+
+ // The last 4 bit are the most significant 4 bits for the 36bit
+ // streamlength in samples. (Audio files measured in days)
+
+ uint highlength = (((flags & 0xf) << 28) / d->sampleRate) << 4;
+ pos += 4;
+
+ d->length = (data.mid(pos, 4).toUInt(true)) / d->sampleRate + highlength;
+ pos += 4;
+
+ // Uncompressed bitrate:
+
+ // d->bitrate = ((d->sampleRate * d->channels) / 1000) * d->sampleWidth;
+
+ // Real bitrate:
+
+ d->bitrate = ((d->file->streamLength()*8L) / d->length)/1000;
+
+
+}
--- /dev/null
+/***************************************************************************
+ copyright : (C) 2003 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_FLACPROPERTIES_H
+#define TAGLIB_FLACPROPERTIES_H
+
+#include <audioproperties.h>
+
+namespace TagLib {
+
+ namespace FLAC {
+
+ class File;
+
+ //! An implementation of audio property reading for FLAC
+
+ /*!
+ * This reads the data from an FLAC stream found in the AudioProperties
+ * API.
+ */
+
+ class Properties : public AudioProperties
+ {
+ public:
+ /*!
+ * Create an instance of FLAC::Properties with the data read from the
+ * FLAC::File \a file.
+ */
+ Properties(File *file, ReadStyle style = Average);
+
+ /*!
+ * Destroys this FLAC::Properties instance.
+ */
+ virtual ~Properties();
+
+ // Reimplementations.
+
+ virtual int length() const;
+ virtual int bitrate() const;
+ virtual int sampleRate() const;
+ virtual int channels() const;
+
+ /*!
+ * Returns the sample width as read from the FLAC identification
+ * header.
+ */
+ int sampleWidth() const;
+
+ private:
+ void read();
+
+ class PropertiesPrivate;
+ PropertiesPrivate *d;
+ };
+ }
+}
+
+#endif
--- /dev/null
+/***************************************************************************
+ copyright : (C) 2003 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 DO_NOT_DOCUMENT // Tell Doxygen not to document this header
+
+#ifndef TAGLIB_FLACTAG_H
+#define TAGLIB_FLACTAG_H
+
+////////////////////////////////////////////////////////////////////////////////
+// Note that this header is not installed.
+////////////////////////////////////////////////////////////////////////////////
+
+#include <xiphcomment.h>
+#include <id3v2tag.h>
+#include <id3v1tag.h>
+
+namespace TagLib {
+
+ namespace FLAC {
+
+ /*!
+ * A union of Xiph, ID3v2 and ID3v1 tags.
+ */
+ class Tag : public TagLib::Tag
+ {
+ public:
+ Tag(Ogg::XiphComment *xiph, ID3v2::Tag *id3v2 = 0, ID3v1::Tag *id3v1 = 0) :
+ TagLib::Tag(),
+ xiph(xiph), id3v2(id3v2), id3v1(id3v1) {}
+
+ virtual String title() const {
+ if(xiph && !xiph->title().isEmpty())
+ return xiph->title();
+
+ if(id3v2 && !id3v2->title().isEmpty())
+ return id3v2->title();
+
+ if(id3v1)
+ return id3v1->title();
+
+ return String::null;
+ }
+
+ virtual String artist() const {
+ if(xiph && !xiph->artist().isEmpty())
+ return xiph->artist();
+
+ if(id3v2 && !id3v2->artist().isEmpty())
+ return id3v2->artist();
+
+ if(id3v1)
+ return id3v1->artist();
+
+ return String::null;
+ }
+
+ virtual String album() const {
+ if(xiph && !xiph->album().isEmpty())
+ return xiph->album();
+
+ if(id3v2 && !id3v2->album().isEmpty())
+ return id3v2->album();
+
+ if(id3v1)
+ return id3v1->album();
+
+ return String::null;
+ }
+
+ virtual String comment() const {
+ if(xiph && !xiph->comment().isEmpty())
+ return xiph->comment();
+
+ if(id3v2 && !id3v2->comment().isEmpty())
+ return id3v2->comment();
+
+ if(id3v1)
+ return id3v1->comment();
+
+ return String::null;
+ }
+
+ virtual String genre() const {
+ if(xiph && !xiph->genre().isEmpty())
+ return xiph->genre();
+
+ if(id3v2 && !id3v2->genre().isEmpty())
+ return id3v2->genre();
+
+ if(id3v1)
+ return id3v1->genre();
+
+ return String::null;
+ }
+
+ virtual uint year() const {
+ if(xiph && xiph->year() > 0)
+ return xiph->year();
+
+ if(id3v2 && id3v2->year() > 0)
+ return id3v2->year();
+
+ if(id3v1)
+ return id3v1->year();
+
+ return 0;
+ }
+
+ virtual uint track() const {
+ if(xiph && xiph->track() > 0)
+ return xiph->track();
+
+ if(id3v2 && id3v2->track() > 0)
+ return id3v2->track();
+
+ if(id3v1)
+ return id3v1->track();
+
+ return 0;
+ }
+
+ virtual void setTitle(const String &s) {
+ if(xiph)
+ xiph->setTitle(s);
+ if(id3v2)
+ id3v2->setTitle(s);
+ if(id3v1)
+ id3v1->setTitle(s);
+ }
+
+ virtual void setArtist(const String &s) {
+ if(xiph)
+ xiph->setArtist(s);
+ if(id3v2)
+ id3v2->setArtist(s);
+ if(id3v1)
+ id3v1->setArtist(s);
+ }
+
+ virtual void setAlbum(const String &s) {
+ if(xiph)
+ xiph->setAlbum(s);
+ if(id3v2)
+ id3v2->setAlbum(s);
+ if(id3v1)
+ id3v1->setAlbum(s);
+ }
+
+ virtual void setComment(const String &s) {
+ if(xiph)
+ xiph->setComment(s);
+ if(id3v2)
+ id3v2->setComment(s);
+ if(id3v1)
+ id3v1->setComment(s);
+ }
+
+ virtual void setGenre(const String &s) {
+ if(xiph)
+ xiph->setGenre(s);
+ if(id3v2)
+ id3v2->setGenre(s);
+ if(id3v1)
+ id3v1->setGenre(s);
+ }
+
+ virtual void setYear(uint i) {
+ if(xiph)
+ xiph->setYear(i);
+ if(id3v2)
+ id3v2->setYear(i);
+ if(id3v1)
+ id3v1->setYear(i);
+ }
+
+ virtual void setTrack(uint i) {
+ if(xiph)
+ xiph->setTrack(i);
+ if(id3v2)
+ id3v2->setTrack(i);
+ if(id3v1)
+ id3v1->setTrack(i);
+ }
+
+ private:
+ Ogg::XiphComment *xiph;
+ ID3v2::Tag *id3v2;
+ ID3v1::Tag *id3v1;
+ };
+ }
+}
+
+#endif
+#endif
--- /dev/null
+SUBDIRS = id3v1 id3v2
+INCLUDES = \
+ -I$(top_srcdir)/taglib\
+ -I$(top_srcdir)/taglib/toolkit \
+ -I$(top_srcdir)/taglib/mpeg/id3v2 -I./id3v2 \
+ -I$(top_srcdir)/taglib/mpeg/id3v1 -I./id3v1 \
+ $(all_includes)
+
+noinst_LTLIBRARIES = libmpeg.la
+
+libmpeg_la_SOURCES = mpegfile.cpp mpegproperties.cpp mpegheader.cpp xingheader.cpp
+
+taglib_include_HEADERS = mpegfile.h mpegproperties.h mpegheader.h xingheader.h
+taglib_includedir = $(includedir)/taglib
+
+libmpeg_la_LIBADD = ./id3v2/libid3v2.la ./id3v1/libid3v1.la
+
+EXTRA_DIST = $(libmpeg_la_SOURCES) $(taglib_include_HEADERS)
--- /dev/null
+INCLUDES = \
+ -I$(top_srcdir)/taglib \
+ -I$(top_srcdir)/taglib/toolkit \
+ -I$(top_srcdir)/taglib/mpeg \
+ $(all_includes)
+
+noinst_LTLIBRARIES = libid3v1.la
+
+libid3v1_la_SOURCES = id3v1tag.cpp id3v1genres.cpp
+
+taglib_include_HEADERS = id3v1tag.h id3v1genres.h
+taglib_includedir = $(includedir)/taglib
+
+EXTRA_DIST = $(libid3v1_la_SOURCES) $(taglib_include_HEADERS)
--- /dev/null
+/***************************************************************************
+ copyright : (C) 2002 by Scott Wheeler
+ email : wheeler@kde.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 "id3v1genres.h"
+
+using namespace TagLib;
+
+namespace TagLib {
+ namespace ID3v1 {
+
+ static const int genresSize = 147;
+ static const String genres[] = {
+ "Blues",
+ "Classic Rock",
+ "Country",
+ "Dance",
+ "Disco",
+ "Funk",
+ "Grunge",
+ "Hip-Hop",
+ "Jazz",
+ "Metal",
+ "New Age",
+ "Oldies",
+ "Other",
+ "Pop",
+ "R&B",
+ "Rap",
+ "Reggae",
+ "Rock",
+ "Techno",
+ "Industrial",
+ "Alternative",
+ "Ska",
+ "Death Metal",
+ "Pranks",
+ "Soundtrack",
+ "Euro-Techno",
+ "Ambient",
+ "Trip-Hop",
+ "Vocal",
+ "Jazz+Funk",
+ "Fusion",
+ "Trance",
+ "Classical",
+ "Instrumental",
+ "Acid",
+ "House",
+ "Game",
+ "Sound Clip",
+ "Gospel",
+ "Noise",
+ "Alternative Rock",
+ "Bass",
+ "Soul",
+ "Punk",
+ "Space",
+ "Meditative",
+ "Instrumental Pop",
+ "Instrumental Rock",
+ "Ethnic",
+ "Gothic",
+ "Darkwave",
+ "Techno-Industrial",
+ "Electronic",
+ "Pop-Folk",
+ "Eurodance",
+ "Dream",
+ "Southern Rock",
+ "Comedy",
+ "Cult",
+ "Gangsta",
+ "Top 40",
+ "Christian Rap",
+ "Pop/Funk",
+ "Jungle",
+ "Native American",
+ "Cabaret",
+ "New Wave",
+ "Psychadelic",
+ "Rave",
+ "Showtunes",
+ "Trailer",
+ "Lo-Fi",
+ "Tribal",
+ "Acid Punk",
+ "Acid Jazz",
+ "Polka",
+ "Retro",
+ "Musical",
+ "Rock & Roll",
+ "Hard Rock",
+ "Folk",
+ "Folk/Rock",
+ "National Folk",
+ "Swing",
+ "Fusion",
+ "Bebob",
+ "Latin",
+ "Revival",
+ "Celtic",
+ "Bluegrass",
+ "Avantgarde",
+ "Gothic Rock",
+ "Progressive Rock",
+ "Psychadelic Rock",
+ "Symphonic Rock",
+ "Slow Rock",
+ "Big Band",
+ "Chorus",
+ "Easy Listening",
+ "Acoustic",
+ "Humour",
+ "Speech",
+ "Chanson",
+ "Opera",
+ "Chamber Music",
+ "Sonata",
+ "Symphony",
+ "Booty Bass",
+ "Primus",
+ "Porn Groove",
+ "Satire",
+ "Slow Jam",
+ "Club",
+ "Tango",
+ "Samba",
+ "Folklore",
+ "Ballad",
+ "Power Ballad",
+ "Rhythmic Soul",
+ "Freestyle",
+ "Duet",
+ "Punk Rock",
+ "Drum Solo",
+ "A Capella",
+ "Euro-House",
+ "Dance Hall",
+ "Goa",
+ "Drum & Bass",
+ "Club-House",
+ "Hardcore",
+ "Terror",
+ "Indie",
+ "BritPop",
+ "Negerpunk",
+ "Polsk Punk",
+ "Beat",
+ "Christian Gangsta Rap",
+ "Heavy Metal",
+ "Black Metal",
+ "Crossover",
+ "Contemporary Christian",
+ "Christian Rock",
+ "Merengue",
+ "Salsa",
+ "Thrash Metal",
+ "Anime",
+ "Jpop",
+ "Synthpop"
+ };
+ }
+}
+
+StringList ID3v1::genreList()
+{
+ static StringList l;
+ if(l.isEmpty()) {
+ for(int i = 0; i < genresSize; i++)
+ l.append(genres[i]);
+ }
+ return l;
+}
+
+ID3v1::GenreMap ID3v1::genreMap()
+{
+ static GenreMap m;
+ if(m.isEmpty()) {
+ for(int i = 0; i < genresSize; i++)
+ m.insert(genres[i], i);
+ }
+ return m;
+}
+
+String ID3v1::genre(int i)
+{
+ if(i >= 0 && i < genresSize)
+ return genres[i];
+ return String::null;
+}
+
+int ID3v1::genreIndex(const String &name)
+{
+ if(genreMap().contains(name))
+ return genreMap()[name];
+ return 255;
+}
--- /dev/null
+/***************************************************************************
+ copyright : (C) 2003 by Scott Wheeler
+ email : wheeler@kde.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_ID3V1GENRE_H
+#define TAGLIB_ID3V1GENRE_H
+
+#include <tmap.h>
+#include <tstringlist.h>
+
+namespace TagLib {
+ namespace ID3v1 {
+
+ typedef Map<String, int> GenreMap;
+
+ /*!
+ * Returns the list of canonical ID3v1 genre names in the order that they
+ * are listed in the standard.
+ */
+ StringList genreList();
+
+ /*!
+ * A "reverse mapping" that goes from the canonical ID3v1 genre name to the
+ * respective genre number. genreMap()["Rock"] ==
+ */
+ GenreMap genreMap();
+
+ /*!
+ * Returns the name of the genre at \a index in the ID3v1 genre list. If
+ * \a index is out of range -- less than zero or greater than 146 -- a null
+ * string will be returned.
+ */
+ String genre(int index);
+
+ /*!
+ * Returns the genre index for the (case sensitive) genre \a name. If the
+ * genre is not in the list 255 (which signifies an unknown genre in ID3v1)
+ * will be returned.
+ */
+ int genreIndex(const String &name);
+ }
+}
+
+#endif
--- /dev/null
+/***************************************************************************
+ copyright : (C) 2002, 2003 by Scott Wheeler
+ email : wheeler@kde.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 <tdebug.h>
+#include <tfile.h>
+
+#include "id3v1tag.h"
+#include "id3v1genres.h"
+
+using namespace TagLib;
+using namespace ID3v1;
+
+class ID3v1::Tag::TagPrivate
+{
+public:
+ TagPrivate() : file(0), tagOffset(-1), track(0), genre(255) {}
+
+ File *file;
+ long tagOffset;
+
+ String title;
+ String artist;
+ String album;
+ String year;
+ String comment;
+ uchar track;
+ uchar genre;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// public methods
+////////////////////////////////////////////////////////////////////////////////
+
+ID3v1::Tag::Tag() : TagLib::Tag()
+{
+ d = new TagPrivate;
+}
+
+ID3v1::Tag::Tag(File *file, long tagOffset) : TagLib::Tag()
+{
+ d = new TagPrivate;
+ d->file = file;
+ d->tagOffset = tagOffset;
+
+ read();
+}
+
+ID3v1::Tag::~Tag()
+{
+ delete d;
+}
+
+ByteVector ID3v1::Tag::render() const
+{
+ ByteVector data;
+
+ data.append(fileIdentifier());
+ data.append(d->title.data(String::Latin1).resize(30));
+ data.append(d->artist.data(String::Latin1).resize(30));
+ data.append(d->album.data(String::Latin1).resize(30));
+ data.append(d->year.data(String::Latin1).resize(4));
+ data.append(d->comment.data(String::Latin1).resize(28));
+ data.append(char(0));
+ data.append(char(d->track));
+ data.append(char(d->genre));
+
+ return data;
+}
+
+ByteVector ID3v1::Tag::fileIdentifier()
+{
+ return ByteVector::fromCString("TAG");
+}
+
+String ID3v1::Tag::title() const
+{
+ return d->title;
+}
+
+String ID3v1::Tag::artist() const
+{
+ return d->artist;
+}
+
+String ID3v1::Tag::album() const
+{
+ return d->album;
+}
+
+String ID3v1::Tag::comment() const
+{
+ return d->comment;
+}
+
+String ID3v1::Tag::genre() const
+{
+ return ID3v1::genre(d->genre);
+}
+
+TagLib::uint ID3v1::Tag::year() const
+{
+ return d->year.toInt();
+}
+
+TagLib::uint ID3v1::Tag::track() const
+{
+ return d->track;
+}
+
+void ID3v1::Tag::setTitle(const String &s)
+{
+ d->title = s;
+}
+
+void ID3v1::Tag::setArtist(const String &s)
+{
+ d->artist = s;
+}
+
+void ID3v1::Tag::setAlbum(const String &s)
+{
+ d->album = s;
+}
+
+void ID3v1::Tag::setComment(const String &s)
+{
+ d->comment = s;
+}
+
+void ID3v1::Tag::setGenre(const String &s)
+{
+ d->genre = ID3v1::genreIndex(s);
+}
+
+void ID3v1::Tag::setYear(uint i)
+{
+ d->year = String::number(i);
+}
+
+void ID3v1::Tag::setTrack(uint i)
+{
+ d->track = i;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// protected methods
+////////////////////////////////////////////////////////////////////////////////
+
+void ID3v1::Tag::read()
+{
+ if(d->file && d->file->isValid()) {
+ d->file->seek(d->tagOffset);
+ // read the tag -- always 128 bytes
+ ByteVector data = d->file->readBlock(128);
+
+ // some initial sanity checking
+ if(data.size() == 128 && data.mid(0, 3) == "TAG")
+ parse(data);
+ else
+ debug("ID3v1 tag is not valid or could not be read at the specified offset.");
+ }
+}
+
+void ID3v1::Tag::parse(const ByteVector &data)
+{
+ int offset = 3;
+
+ d->title = data.mid(offset, 30);
+ offset += 30;
+
+ d->artist = data.mid(offset, 30);
+ offset += 30;
+
+ d->album = data.mid(offset, 30);
+ offset += 30;
+
+ d->year = data.mid(offset, 4);
+ offset += 4;
+
+ // Check for ID3v1.1 -- Note that ID3v1 *does not* support "track zero" -- this
+ // is not a bug in TagLib. Since a zeroed byte is what we would expect to
+ // indicate the end of a C-String, specifically the comment string, a value of
+ // zero must be assumed to be just that.
+
+ if(data[offset + 28] == 0 && data[offset + 29] != 0) {
+ // ID3v1.1 detected
+
+ d->comment = data.mid(offset, 28);
+ d->track = uchar(data[offset + 29]);
+ }
+ else
+ d->comment = data.mid(offset, 30);
+
+ offset += 30;
+
+ d->genre = uchar(data[offset]);
+}
--- /dev/null
+/***************************************************************************
+ copyright : (C) 2002, 2003 by Scott Wheeler
+ email : wheeler@kde.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_ID3V1TAG_H
+#define TAGLIB_ID3V1TAG_H
+
+#include <tag.h>
+#include <tbytevector.h>
+
+namespace TagLib {
+
+ class File;
+
+ //! An ID3v1 implementation
+
+ namespace ID3v1 {
+
+ //! The main class in the ID3v1 implementation
+
+ /*!
+ * This is an implementation of the ID3v1 format. ID3v1 is both the simplist
+ * and most common of tag formats but is rather limited. Because of its
+ * pervasiveness and the way that applications have been written around the
+ * fields that it provides, the generic TagLib::Tag API is a mirror of what is
+ * provided by ID3v1.
+ *
+ * \note Most fields are truncated to a maximum of 28-30 bytes. The
+ * truncation happens automatically when the tag is rendered.
+ */
+
+ class Tag : public TagLib::Tag
+ {
+ public:
+ /*!
+ * Create an ID3v1 tag with default values.
+ */
+ Tag();
+
+ /*!
+ * Create an ID3v1 tag and parse the data in \a file starting 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 "TAG" suitable for usage in locating the tag in a
+ * file.
+ */
+ static ByteVector fileIdentifier();
+
+ // 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);
+
+ protected:
+ /*!
+ * Reads from the file specified in the constructor.
+ */
+ void read();
+ /*!
+ * Pareses the body of the tag in \a data.
+ */
+ void parse(const ByteVector &data);
+
+ private:
+ Tag(const Tag &);
+ Tag &operator=(const Tag &);
+
+ class TagPrivate;
+ TagPrivate *d;
+ };
+ }
+}
+
+#endif
--- /dev/null
+SUBDIRS = frames
+INCLUDES = \
+ -I$(top_srcdir)/taglib \
+ -I$(top_srcdir)/taglib/toolkit \
+ -I$(top_srcdir)/taglib/mpeg \
+ -I$(top_srcdir)/taglib/mpeg/id3v1 \
+ $(all_includes)
+
+noinst_LTLIBRARIES = libid3v2.la
+
+libid3v2_la_SOURCES = \
+ id3v2framefactory.cpp id3v2synchdata.cpp id3v2tag.cpp \
+ id3v2header.cpp id3v2frame.cpp id3v2footer.cpp \
+ id3v2extendedheader.cpp
+
+taglib_include_HEADERS = \
+ id3v2extendedheader.h id3v2frame.h id3v2header.h \
+ id3v2synchdata.h id3v2footer.h id3v2framefactory.h id3v2tag.h
+
+taglib_includedir = $(includedir)/taglib
+libid3v2_la_LIBADD = ./frames/libframes.la
+
+EXTRA_DIST = $(libid3v2_la_SOURCES) $(taglib_include_HEADERS) id3v2.4.0-frames.txt id3v2.4.0-structure.txt
--- /dev/null
+INCLUDES = \
+ -I$(top_srcdir)/taglib/toolkit \
+ -I$(top_srcdir)/taglib/mpeg/id3v2 \
+ $(all_includes)
+
+noinst_LTLIBRARIES = libframes.la
+
+libframes_la_SOURCES = unknownframe.cpp textidentificationframe.cpp commentsframe.cpp
+
+taglib_include_HEADERS = unknownframe.h textidentificationframe.h commentsframe.h
+taglib_includedir = $(includedir)/taglib
+
+EXTRA_DIST = $(libframes_la_SOURCES) $(taglib_include_HEADERS)
--- /dev/null
+/***************************************************************************
+ copyright : (C) 2002, 2003 by Scott Wheeler
+ email : wheeler@kde.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 <tbytevectorlist.h>
+#include <tdebug.h>
+
+#include "commentsframe.h"
+
+using namespace TagLib;
+using namespace ID3v2;
+
+class CommentsFrame::CommentsFramePrivate
+{
+public:
+ CommentsFramePrivate() : textEncoding(String::Latin1) {}
+ String::Type textEncoding;
+ ByteVector language;
+ String description;
+ String text;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// public members
+////////////////////////////////////////////////////////////////////////////////
+
+CommentsFrame::CommentsFrame(String::Type encoding) : Frame("COMM")
+{
+ d = new CommentsFramePrivate();
+ d->textEncoding = encoding;
+}
+
+CommentsFrame::CommentsFrame(const ByteVector &data) : Frame(data)
+{
+ d = new CommentsFramePrivate();
+ parseFields(data.mid(Header::size(), size()));
+}
+
+CommentsFrame::~CommentsFrame()
+{
+ delete d;
+}
+
+String CommentsFrame::toString() const
+{
+ return d->text;
+}
+
+ByteVector CommentsFrame::language() const
+{
+ return d->language;
+}
+
+String CommentsFrame::description() const
+{
+ return d->description;
+}
+
+String CommentsFrame::text() const
+{
+ return d->text;
+}
+
+void CommentsFrame::setLanguage(const ByteVector &languageEncoding)
+{
+ d->language = languageEncoding.mid(0, 3);
+}
+
+void CommentsFrame::setDescription(const String &s)
+{
+ d->description = s;
+}
+
+void CommentsFrame::setText(const String &s)
+{
+ d->text = s;
+}
+
+
+String::Type CommentsFrame::textEncoding() const
+{
+ return d->textEncoding;
+}
+
+void CommentsFrame::setTextEncoding(String::Type encoding)
+{
+ d->textEncoding = encoding;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// protected members
+////////////////////////////////////////////////////////////////////////////////
+
+void CommentsFrame::parseFields(const ByteVector &data)
+{
+ if(data.size() < 5) {
+ debug("A comment frame must contain at least 5 bytes.");
+ return;
+ }
+
+ d->textEncoding = String::Type(data[0]);
+ d->language = data.mid(1, 3);
+
+ int byteAlign = d->textEncoding == String::Latin1 || d->textEncoding == String::UTF8 ? 1 : 2;
+
+ ByteVectorList l = ByteVectorList::split(data.mid(4), textDelimiter(d->textEncoding), byteAlign);
+
+ if(l.size() == 2) {
+ d->description = String(l.front(), d->textEncoding);
+ d->text = String(l.back(), d->textEncoding);
+ }
+}
+
+ByteVector CommentsFrame::renderFields() const
+{
+ ByteVector v;
+
+ v.append(char(d->textEncoding));
+ v.append(d->language);
+ v.append(d->description.data(d->textEncoding));
+ v.append(textDelimiter(d->textEncoding));
+ v.append(d->text.data(d->textEncoding));
+
+ return v;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// private members
+////////////////////////////////////////////////////////////////////////////////
+
+CommentsFrame::CommentsFrame(const ByteVector &data, Header *h) : Frame(h)
+{
+ d = new CommentsFramePrivate();
+ parseFields(data.mid(Header::size(), size()));
+}
--- /dev/null
+/***************************************************************************
+ copyright : (C) 2002, 2003 by Scott Wheeler
+ email : wheeler@kde.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_COMMENTSFRAME_H
+#define TAGLIB_COMMENTSFRAME_H
+
+#include <id3v2frame.h>
+
+namespace TagLib {
+
+ namespace ID3v2 {
+
+ //! An implementation of ID3v2 comments
+
+ /*!
+ * This implements the ID3v2 comment format. An ID3v2 comment concists of
+ * a language encoding, a description and a single text field.
+ */
+
+ class CommentsFrame : public Frame
+ {
+ friend class FrameFactory;
+
+ public:
+ /*!
+ * Construct an empty comment frame that will use the text encoding
+ * \a encoding.
+ */
+ explicit CommentsFrame(String::Type encoding = String::Latin1);
+
+ /*!
+ * Construct a comment based on the data in \a data.
+ */
+ explicit CommentsFrame(const ByteVector &data);
+
+ /*!
+ * Destroys this CommentFrame instance.
+ */
+ virtual ~CommentsFrame();
+
+ /*!
+ * Returns the text of this comment.
+ *
+ * \see text()
+ */
+ virtual String toString() const;
+
+ /*!
+ * Returns the language encoding as a 3 byte encoding as specified by
+ * <a href="http://en.wikipedia.org/wiki/ISO_639">ISO-639-2</a>.
+ *
+ * \note Most taggers simply ignore this value.
+ *
+ * \see setLanguage()
+ */
+ ByteVector language() const;
+
+ /*!
+ * Returns the description of this comment.
+ *
+ * \note Most taggers simply ignore this value.
+ *
+ * \see setDescription()
+ */
+ String description() const;
+
+ /*!
+ * Returns the text of this comment.
+ *
+ * \see setText()
+ */
+ String text() const;
+
+ /*!
+ * Set the language using the 3 byte language code from
+ * <a href="http://en.wikipedia.org/wiki/ISO_639">ISO-639-2</a> to
+ * \a languageCode.
+ *
+ * \see language()
+ */
+ void setLanguage(const ByteVector &languageCode);
+
+ /*!
+ * Sets the description of the comment to \a s.
+ *
+ * \see decription()
+ */
+ void setDescription(const String &s);
+
+ /*!
+ * Sets the text portion of the comment to \a s.
+ *
+ * \see text()
+ */
+ virtual void setText(const String &s);
+
+ /*!
+ * Returns the text encoding that will be used in rendering this frame.
+ * This defaults to the type that was either specified in the constructor
+ * or read from the frame when parsed.
+ *
+ * \see setTextEncoding()
+ * \see render()
+ */
+ String::Type textEncoding() const;
+
+ /*!
+ * Sets the text encoding to be used when rendering this frame to
+ * \a encoding.
+ *
+ * \see textEncoding()
+ * \see render()
+ */
+ void setTextEncoding(String::Type encoding);
+
+ protected:
+ // Reimplementations.
+
+ virtual void parseFields(const ByteVector &data);
+ virtual ByteVector renderFields() const;
+
+ private:
+ /*!
+ * The constructor used by the FrameFactory.
+ */
+ CommentsFrame(const ByteVector &data, Header *h);
+ CommentsFrame(const CommentsFrame &);
+ CommentsFrame &operator=(const CommentsFrame &);
+
+ class CommentsFramePrivate;
+ CommentsFramePrivate *d;
+ };
+
+ }
+}
+#endif
--- /dev/null
+/***************************************************************************
+ copyright : (C) 2002, 2003 by Scott Wheeler
+ email : wheeler@kde.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 <tbytevectorlist.h>
+
+#include "textidentificationframe.h"
+
+using namespace TagLib;
+using namespace ID3v2;
+
+class TextIdentificationFrame::TextIdentificationFramePrivate
+{
+public:
+ TextIdentificationFramePrivate() : textEncoding(String::Latin1) {}
+ String::Type textEncoding;
+ StringList fieldList;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// public members
+////////////////////////////////////////////////////////////////////////////////
+
+TextIdentificationFrame::TextIdentificationFrame(const ByteVector &type, String::Type encoding)
+ : Frame(type)
+{
+ d = new TextIdentificationFramePrivate;
+ d->textEncoding = encoding;
+}
+
+TextIdentificationFrame::TextIdentificationFrame(const ByteVector &data)
+ : Frame(data)
+{
+ d = new TextIdentificationFramePrivate;
+ setData(data);
+}
+
+TextIdentificationFrame::~TextIdentificationFrame()
+{
+ delete d;
+}
+
+void TextIdentificationFrame::setText(const StringList &l)
+{
+ d->fieldList = l;
+}
+
+void TextIdentificationFrame::setText(const String &s)
+{
+ d->fieldList = s;
+}
+
+String TextIdentificationFrame::toString() const
+{
+ return d->fieldList.toString();
+}
+
+StringList TextIdentificationFrame::fieldList() const
+{
+ return d->fieldList;
+}
+
+String::Type TextIdentificationFrame::textEncoding() const
+{
+ return d->textEncoding;
+}
+
+void TextIdentificationFrame::setTextEncoding(String::Type encoding)
+{
+ d->textEncoding = encoding;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// protected members
+////////////////////////////////////////////////////////////////////////////////
+
+void TextIdentificationFrame::parseFields(const ByteVector &data)
+{
+ // read the string data type (the first byte of the field data)
+
+ d->textEncoding = String::Type(data[0]);
+
+ // split the byte array into chunks based on the string type (two byte delimiter
+ // for unicode encodings)
+
+ int byteAlign = d->textEncoding == String::Latin1 || d->textEncoding == String::UTF8 ? 1 : 2;
+
+ ByteVectorList l = ByteVectorList::split(data.mid(1), textDelimiter(d->textEncoding), byteAlign);
+
+ d->fieldList.clear();
+
+ // append those split values to the list and make sure that the new string's
+ // type is the same specified for this frame
+
+ for(ByteVectorList::Iterator it = l.begin(); it != l.end(); it++) {
+ String s(*it, d->textEncoding);
+ d->fieldList.append(s);
+ }
+}
+
+ByteVector TextIdentificationFrame::renderFields() const
+{
+ ByteVector v;
+
+ if(d->fieldList.size() > 0) {
+
+ v.append(char(d->textEncoding));
+
+ for(StringList::Iterator it = d->fieldList.begin(); it != d->fieldList.end(); it++) {
+
+ // Since the field list is null delimited, if this is not the first
+ // element in the list, append the appropriate delimiter for this
+ // encoding.
+
+ if(it != d->fieldList.begin())
+ v.append(textDelimiter(d->textEncoding));
+
+ v.append((*it).data(d->textEncoding));
+ }
+ }
+
+ return v;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// private members
+////////////////////////////////////////////////////////////////////////////////
+
+TextIdentificationFrame::TextIdentificationFrame(const ByteVector &data, Header *h) : Frame(h)
+{
+ d = new TextIdentificationFramePrivate;
+ parseFields(data.mid(Header::size(), size()));
+}
--- /dev/null
+/***************************************************************************
+ copyright : (C) 2002, 2003 by Scott Wheeler
+ email : wheeler@kde.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_TEXTIDENTIFICATIONFRAME_H
+#define TAGLIB_TEXTIDENTIFICATIONFRAME_H
+
+#include <tstringlist.h>
+
+#include <id3v2frame.h>
+
+namespace TagLib {
+
+ namespace ID3v2 {
+
+ //! An ID3v2 text identification frame implementation
+
+ /*!
+ * This is an implementation of the most common type of ID3v2 frame -- text
+ * identification frames. There are a number of variations on this. Those
+ * enumerated in the ID3v2.4 standard are:
+ *
+ * <ul>
+ * <li><b>TALB</b> Album/Movie/Show title</li>
+ * <li><b>TBPM</b> BPM (beats per minute)</li>
+ * <li><b>TCOM</b> Composer</li>
+ * <li><b>TCON</b> Content type</li>
+ * <li><b>TCOP</b> Copyright message</li>
+ * <li><b>TDEN</b> Encoding time</li>
+ * <li><b>TDLY</b> Playlist delay</li>
+ * <li><b>TDOR</b> Original release time</li>
+ * <li><b>TDRC</b> Recording time</li>
+ * <li><b>TDRL</b> Release time</li>
+ * <li><b>TDTG</b> Tagging time</li>
+ * <li><b>TENC</b> Encoded by</li>
+ * <li><b>TEXT</b> Lyricist/Text writer</li>
+ * <li><b>TFLT</b> File type</li>
+ * <li><b>TIPL</b> Involved people list</li>
+ * <li><b>TIT1</b> Content group description</li>
+ * <li><b>TIT2</b> Title/songname/content description</li>
+ * <li><b>TIT3</b> Subtitle/Description refinement</li>
+ * <li><b>TKEY</b> Initial key</li>
+ * <li><b>TLAN</b> Language(s)</li>
+ * <li><b>TLEN</b> Length</li>
+ * <li><b>TMCL</b> Musician credits list</li>
+ * <li><b>TMED</b> Media type</li>
+ * <li><b>TMOO</b> Mood</li>
+ * <li><b>TOAL</b> Original album/movie/show title</li>
+ * <li><b>TOFN</b> Original filename</li>
+ * <li><b>TOLY</b> Original lyricist(s)/text writer(s)</li>
+ * <li><b>TOPE</b> Original artist(s)/performer(s)</li>
+ * <li><b>TOWN</b> File owner/licensee</li>
+ * <li><b>TPE1</b> Lead performer(s)/Soloist(s)</li>
+ * <li><b>TPE2</b> Band/orchestra/accompaniment</li>
+ * <li><b>TPE3</b> Conductor/performer refinement</li>
+ * <li><b>TPE4</b> Interpreted, remixed, or otherwise modified by</li>
+ * <li><b>TPOS</b> Part of a set</li>
+ * <li><b>TPRO</b> Produced notice</li>
+ * <li><b>TPUB</b> Publisher</li>
+ * <li><b>TRCK</b> Track number/Position in set</li>
+ * <li><b>TRSN</b> Internet radio station name</li>
+ * <li><b>TRSO</b> Internet radio station owner</li>
+ * <li><b>TSOA</b> Album sort order</li>
+ * <li><b>TSOP</b> Performer sort order</li>
+ * <li><b>TSOT</b> Title sort order</li>
+ * <li><b>TSRC</b> ISRC (international standard recording code)</li>
+ * <li><b>TSSE</b> Software/Hardware and settings used for encoding</li>
+ * <li><b>TSST</b> Set subtitle</li>
+ * </ul>
+ *
+ * The ID3v2 Frames document gives a description of each of these formats
+ * and the expected order of strings in each. ID3v2::Header::frameID() can
+ * be used to determine the frame type.
+ */
+
+ class TextIdentificationFrame : public Frame
+ {
+ friend class FrameFactory;
+
+ public:
+ /*!
+ * Construct an empty frame of type \a type. Uses \a encoding as the
+ * default text encoding.
+ *
+ * \note In this case you must specify the text encoding as it
+ * resolves the ambiguity between constructors.
+ */
+ TextIdentificationFrame(const ByteVector &type, String::Type encoding);
+
+ /*!
+ * This is a dual purpose constructor. \a data can either be binary data
+ * that should be parsed or (at a minimum) the frame ID.
+ */
+ explicit TextIdentificationFrame(const ByteVector &data);
+
+ /*!
+ * Destroys this TextIdentificationFrame instance.
+ */
+ virtual ~TextIdentificationFrame();
+
+ /*!
+ * Text identification frames are a list of string fields.
+ *
+ * This function will accept either a StringList or a String (using the
+ * StringList constructor that accepts a single String).
+ *
+ * \note This will not change the text encoding of the frame even if the
+ * strings passed in are not of the same encoding. Please use
+ * setEncoding(s.type()) if you wish to change the encoding of the frame.
+ */
+ void setText(const StringList &l);
+
+ // Reimplementations.
+
+ virtual void setText(const String &s);
+ virtual String toString() const;
+
+ /*!
+ * Returns the text encoding that will be used in rendering this frame.
+ * This defaults to the type that was either specified in the constructor
+ * or read from the frame when parsed.
+ *
+ * \see setTextEncoding()
+ * \see render()
+ */
+ String::Type textEncoding() const;
+
+ /*!
+ * Sets the text encoding to be used when rendering this frame to
+ * \a encoding.
+ *
+ * \see textEncoding()
+ * \see render()
+ */
+ void setTextEncoding(String::Type encoding);
+
+ /*!
+ * Returns a list of the strings in this frame.
+ */
+ StringList fieldList() const;
+
+ protected:
+ // Reimplementations.
+
+ virtual void parseFields(const ByteVector &data);
+ virtual ByteVector renderFields() const;
+
+ private:
+ /*!
+ * The constructor used by the FrameFactory.
+ */
+ TextIdentificationFrame(const ByteVector &data, Header *h);
+ TextIdentificationFrame(const TextIdentificationFrame &);
+ TextIdentificationFrame &operator=(const TextIdentificationFrame &);
+
+ class TextIdentificationFramePrivate;
+ TextIdentificationFramePrivate *d;
+ };
+
+ }
+}
+#endif
--- /dev/null
+/***************************************************************************
+ copyright : (C) 2002 by Scott Wheeler
+ email : wheeler@kde.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 "unknownframe.h"
+
+using namespace TagLib;
+using namespace ID3v2;
+
+class UnknownFrame::UnknownFramePrivate
+{
+public:
+ ByteVector fieldData;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// public members
+////////////////////////////////////////////////////////////////////////////////
+
+UnknownFrame::UnknownFrame(const ByteVector &data) : Frame(data)
+{
+ d = new UnknownFramePrivate();
+ setData(data);
+}
+
+UnknownFrame::~UnknownFrame()
+{
+ delete d;
+}
+
+String UnknownFrame::toString() const
+{
+ return String::null;
+}
+
+ByteVector UnknownFrame::data() const
+{
+ return d->fieldData;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// protected members
+////////////////////////////////////////////////////////////////////////////////
+
+void UnknownFrame::parseFields(const ByteVector &data)
+{
+ d->fieldData = data;
+}
+
+ByteVector UnknownFrame::renderFields() const
+{
+ return d->fieldData;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// private members
+////////////////////////////////////////////////////////////////////////////////
+
+UnknownFrame::UnknownFrame(const ByteVector &data, Header *h) : Frame(h)
+{
+ d = new UnknownFramePrivate;
+ parseFields(data.mid(Header::size(), size()));
+}
--- /dev/null
+/***************************************************************************
+ copyright : (C) 2002 by Scott Wheeler
+ email : wheeler@kde.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_UNKNOWNFRAME_H
+#define TAGLIB_UNKNOWNFRAME_H
+
+#include <id3v2frame.h>
+
+namespace TagLib {
+
+ namespace ID3v2 {
+
+ //! A frame type \e unknown to TagLib.
+
+ /*!
+ * This class represents a frame type not known (or more often simply
+ * unimplemented) in TagLib. This is here provide a basic API for
+ * manipulating the binary data of unknown frames and to provide a means
+ * of rendering such \e unknown frames.
+ *
+ * Please note that a cleaner way of handling frame types that TagLib
+ * does not understand is to subclass ID3v2::Frame and ID3v2::FrameFactory
+ * to have your frame type supported through the standard ID3v2 mechanism.
+ */
+
+ class UnknownFrame : public Frame
+ {
+ friend class FrameFactory;
+
+ public:
+ UnknownFrame(const ByteVector &data);
+ virtual ~UnknownFrame();
+
+ virtual String toString() const;
+
+ /*!
+ * Returns the field data (everything but the header) for this frame.
+ */
+ ByteVector data() const;
+
+ protected:
+ virtual void parseFields(const ByteVector &data);
+ virtual ByteVector renderFields() const;
+
+ private:
+ UnknownFrame(const ByteVector &data, Header *h);
+ UnknownFrame(const UnknownFrame &);
+ UnknownFrame &operator=(const UnknownFrame &);
+
+ class UnknownFramePrivate;
+ UnknownFramePrivate *d;
+ };
+
+ }
+}
+#endif
--- /dev/null
+$Id$
+
+Informal standard M. Nilsson
+Document: id3v2.4.0-frames.txt 1st November 2000
+
+
+ ID3 tag version 2.4.0 - Native Frames
+
+Status of this document
+
+ This document is an informal standard and replaces the ID3v2.3.0
+ standard [ID3v2]. A formal standard will use another revision number
+ even if the content is identical to document. The contents in this
+ document may change for clarifications but never for added or altered
+ functionallity.
+
+ Distribution of this document is unlimited.
+
+
+Abstract
+
+ This document describes the frames natively supported by ID3v2.4.0,
+ which is a revised version of the ID3v2 informal standard [ID3v2.3.0]
+ version 2.3.0. The ID3v2 offers a flexible way of storing audio meta
+ information within audio file itself. The information may be
+ technical information, such as equalisation curves, as well as title,
+ performer, copyright etc.
+
+ ID3v2.4.0 is meant to be as close as possible to ID3v2.3.0 in order
+ to allow for implementations to be revised as easily as possible.
+
+
+1. Table of contents
+
+ 2. Conventions in this document
+ 3. Default flags
+ 4. Declared ID3v2 frames
+ 4.1. Unique file identifier
+ 4.2. Text information frames
+ 4.2.1. Identification frames
+ 4.2.2. Involved persons frames
+ 4.2.3. Derived and subjective properties frames
+ 4.2.4. Rights and license frames
+ 4.2.5. Other text frames
+ 4.2.6. User defined text information frame
+ 4.3. URL link frames
+ 4.3.1. URL link frames - details
+ 4.3.2. User defined URL link frame
+ 4.4. Music CD Identifier
+ 4.5. Event timing codes
+ 4.6. MPEG location lookup table
+ 4.7. Synced tempo codes
+ 4.8. Unsynchronised lyrics/text transcription
+ 4.9. Synchronised lyrics/text
+ 4.10. Comments
+ 4.11. Relative volume adjustment (2)
+ 4.12. Equalisation (2)
+ 4.13. Reverb
+ 4.14. Attached picture
+ 4.15. General encapsulated object
+ 4.16. Play counter
+ 4.17. Popularimeter
+ 4.18. Recommended buffer size
+ 4.19. Audio encryption
+ 4.20. Linked information
+ 4.21. Position synchronisation frame
+ 4.22. Terms of use
+ 4.23. Ownership frame
+ 4.24. Commercial frame
+ 4.25. Encryption method registration
+ 4.26. Group identification registration
+ 4.27. Private frame
+ 4.28. Signature frame
+ 4.29. Seek frame
+ 4.30. Audio seek point index
+ 5. Copyright
+ 6. References
+ 7. Appendix
+ A. Appendix A - Genre List from ID3v1
+ 8. Author's Address
+
+
+2. Conventions in this document
+
+ Text within "" is a text string exactly as it appears in a tag.
+ Numbers preceded with $ are hexadecimal and numbers preceded with %
+ are binary. $xx is used to indicate a byte with unknown content. %x
+ is used to indicate a bit with unknown content. The most significant
+ bit (MSB) of a byte is called 'bit 7' and the least significant bit
+ (LSB) is called 'bit 0'.
+
+ A tag is the whole tag described the ID3v2 main structure document
+ [ID3v2-strct]. A frame is a block of information in the tag. The tag
+ consists of a header, frames and optional padding. A field is a piece
+ of information; one value, a string etc. A numeric string is a string
+ that consists of the characters "0123456789" only.
+
+ The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
+ "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this
+ document are to be interpreted as described in RFC 2119 [KEYWORDS].
+
+
+3. Default flags
+
+ The default settings for the frames described in this document can be
+ divided into the following classes. The flags may be set differently
+ if found more suitable by the software.
+
+ 1. Discarded if tag is altered, discarded if file is altered.
+
+ None.
+
+ 2. Discarded if tag is altered, preserved if file is altered.
+
+ None.
+
+ 3. Preserved if tag is altered, discarded if file is altered.
+
+ ASPI, AENC, ETCO, EQU2, MLLT, POSS, SEEK, SYLT, SYTC, RVA2, TENC,
+ TLEN
+
+ 4. Preserved if tag is altered, preserved if file is altered.
+
+ The rest of the frames.
+
+
+4. Declared ID3v2 frames
+
+ The following frames are declared in this draft.
+
+ 4.19 AENC Audio encryption
+ 4.14 APIC Attached picture
+ 4.30 ASPI Audio seek point index
+
+ 4.10 COMM Comments
+ 4.24 COMR Commercial frame
+
+ 4.25 ENCR Encryption method registration
+ 4.12 EQU2 Equalisation (2)
+ 4.5 ETCO Event timing codes
+
+ 4.15 GEOB General encapsulated object
+ 4.26 GRID Group identification registration
+
+ 4.20 LINK Linked information
+
+ 4.4 MCDI Music CD identifier
+ 4.6 MLLT MPEG location lookup table
+
+ 4.23 OWNE Ownership frame
+
+ 4.27 PRIV Private frame
+ 4.16 PCNT Play counter
+ 4.17 POPM Popularimeter
+ 4.21 POSS Position synchronisation frame
+
+ 4.18 RBUF Recommended buffer size
+ 4.11 RVA2 Relative volume adjustment (2)
+ 4.13 RVRB Reverb
+
+ 4.29 SEEK Seek frame
+ 4.28 SIGN Signature frame
+ 4.9 SYLT Synchronised lyric/text
+ 4.7 SYTC Synchronised tempo codes
+
+ 4.2.1 TALB Album/Movie/Show title
+ 4.2.3 TBPM BPM (beats per minute)
+ 4.2.2 TCOM Composer
+ 4.2.3 TCON Content type
+ 4.2.4 TCOP Copyright message
+ 4.2.5 TDEN Encoding time
+ 4.2.5 TDLY Playlist delay
+ 4.2.5 TDOR Original release time
+ 4.2.5 TDRC Recording time
+ 4.2.5 TDRL Release time
+ 4.2.5 TDTG Tagging time
+ 4.2.2 TENC Encoded by
+ 4.2.2 TEXT Lyricist/Text writer
+ 4.2.3 TFLT File type
+ 4.2.2 TIPL Involved people list
+ 4.2.1 TIT1 Content group description
+ 4.2.1 TIT2 Title/songname/content description
+ 4.2.1 TIT3 Subtitle/Description refinement
+ 4.2.3 TKEY Initial key
+ 4.2.3 TLAN Language(s)
+ 4.2.3 TLEN Length
+ 4.2.2 TMCL Musician credits list
+ 4.2.3 TMED Media type
+ 4.2.3 TMOO Mood
+ 4.2.1 TOAL Original album/movie/show title
+ 4.2.5 TOFN Original filename
+ 4.2.2 TOLY Original lyricist(s)/text writer(s)
+ 4.2.2 TOPE Original artist(s)/performer(s)
+ 4.2.4 TOWN File owner/licensee
+ 4.2.2 TPE1 Lead performer(s)/Soloist(s)
+ 4.2.2 TPE2 Band/orchestra/accompaniment
+ 4.2.2 TPE3 Conductor/performer refinement
+ 4.2.2 TPE4 Interpreted, remixed, or otherwise modified by
+ 4.2.1 TPOS Part of a set
+ 4.2.4 TPRO Produced notice
+ 4.2.4 TPUB Publisher
+ 4.2.1 TRCK Track number/Position in set
+ 4.2.4 TRSN Internet radio station name
+ 4.2.4 TRSO Internet radio station owner
+ 4.2.5 TSOA Album sort order
+ 4.2.5 TSOP Performer sort order
+ 4.2.5 TSOT Title sort order
+ 4.2.1 TSRC ISRC (international standard recording code)
+ 4.2.5 TSSE Software/Hardware and settings used for encoding
+ 4.2.1 TSST Set subtitle
+ 4.2.2 TXXX User defined text information frame
+
+ 4.1 UFID Unique file identifier
+ 4.22 USER Terms of use
+ 4.8 USLT Unsynchronised lyric/text transcription
+
+ 4.3.1 WCOM Commercial information
+ 4.3.1 WCOP Copyright/Legal information
+ 4.3.1 WOAF Official audio file webpage
+ 4.3.1 WOAR Official artist/performer webpage
+ 4.3.1 WOAS Official audio source webpage
+ 4.3.1 WORS Official Internet radio station homepage
+ 4.3.1 WPAY Payment
+ 4.3.1 WPUB Publishers official webpage
+ 4.3.2 WXXX User defined URL link frame
+
+
+4.1. Unique file identifier
+
+ This frame's purpose is to be able to identify the audio file in a
+ database, that may provide more information relevant to the content.
+ Since standardisation of such a database is beyond this document, all
+ UFID frames begin with an 'owner identifier' field. It is a null-
+ terminated string with a URL [URL] containing an email address, or a
+ link to a location where an email address can be found, that belongs
+ to the organisation responsible for this specific database
+ implementation. Questions regarding the database should be sent to
+ the indicated email address. The URL should not be used for the
+ actual database queries. The string
+ "http://www.id3.org/dummy/ufid.html" should be used for tests. The
+ 'Owner identifier' must be non-empty (more than just a termination).
+ The 'Owner identifier' is then followed by the actual identifier,
+ which may be up to 64 bytes. There may be more than one "UFID" frame
+ in a tag, but only one with the same 'Owner identifier'.
+
+ <Header for 'Unique file identifier', ID: "UFID">
+ Owner identifier <text string> $00
+ Identifier <up to 64 bytes binary data>
+
+
+4.2. Text information frames
+
+ The text information frames are often the most important frames,
+ containing information like artist, album and more. There may only be
+ one text information frame of its kind in an tag. All text
+ information frames supports multiple strings, stored as a null
+ separated list, where null is reperesented by the termination code
+ for the charater encoding. All text frame identifiers begin with "T".
+ Only text frame identifiers begin with "T", with the exception of the
+ "TXXX" frame. All the text information frames have the following
+ format:
+
+ <Header for 'Text information frame', ID: "T000" - "TZZZ",
+ excluding "TXXX" described in 4.2.6.>
+ Text encoding $xx
+ Information <text string(s) according to encoding>
+
+
+4.2.1. Identification frames
+
+ TIT1
+ The 'Content group description' frame is used if the sound belongs to
+ a larger category of sounds/music. For example, classical music is
+ often sorted in different musical sections (e.g. "Piano Concerto",
+ "Weather - Hurricane").
+
+ TIT2
+ The 'Title/Songname/Content description' frame is the actual name of
+ the piece (e.g. "Adagio", "Hurricane Donna").
+
+ TIT3
+ The 'Subtitle/Description refinement' frame is used for information
+ directly related to the contents title (e.g. "Op. 16" or "Performed
+ live at Wembley").
+
+ TALB
+ The 'Album/Movie/Show title' frame is intended for the title of the
+ recording (or source of sound) from which the audio in the file is
+ taken.
+
+ TOAL
+ The 'Original album/movie/show title' frame is intended for the title
+ of the original recording (or source of sound), if for example the
+ music in the file should be a cover of a previously released song.
+
+ TRCK
+ The 'Track number/Position in set' frame is a numeric string
+ containing the order number of the audio-file on its original
+ recording. This MAY be extended with a "/" character and a numeric
+ string containing the total number of tracks/elements on the original
+ recording. E.g. "4/9".
+
+ TPOS
+ The 'Part of a set' frame is a numeric string that describes which
+ part of a set the audio came from. This frame is used if the source
+ described in the "TALB" frame is divided into several mediums, e.g. a
+ double CD. The value MAY be extended with a "/" character and a
+ numeric string containing the total number of parts in the set. E.g.
+ "1/2".
+
+ TSST
+ The 'Set subtitle' frame is intended for the subtitle of the part of
+ a set this track belongs to.
+
+ TSRC
+ The 'ISRC' frame should contain the International Standard Recording
+ Code [ISRC] (12 characters).
+
+
+4.2.2. Involved persons frames
+
+ TPE1
+ The 'Lead artist/Lead performer/Soloist/Performing group' is
+ used for the main artist.
+
+ TPE2
+ The 'Band/Orchestra/Accompaniment' frame is used for additional
+ information about the performers in the recording.
+
+ TPE3
+ The 'Conductor' frame is used for the name of the conductor.
+
+ TPE4
+ The 'Interpreted, remixed, or otherwise modified by' frame contains
+ more information about the people behind a remix and similar
+ interpretations of another existing piece.
+
+ TOPE
+ The 'Original artist/performer' frame is intended for the performer
+ of the original recording, if for example the music in the file
+ should be a cover of a previously released song.
+
+ TEXT
+ The 'Lyricist/Text writer' frame is intended for the writer of the
+ text or lyrics in the recording.
+
+ TOLY
+ The 'Original lyricist/text writer' frame is intended for the
+ text writer of the original recording, if for example the music in
+ the file should be a cover of a previously released song.
+
+ TCOM
+ The 'Composer' frame is intended for the name of the composer.
+
+ TMCL
+ The 'Musician credits list' is intended as a mapping between
+ instruments and the musician that played it. Every odd field is an
+ instrument and every even is an artist or a comma delimited list of
+ artists.
+
+ TIPL
+ The 'Involved people list' is very similar to the musician credits
+ list, but maps between functions, like producer, and names.
+
+ TENC
+ The 'Encoded by' frame contains the name of the person or
+ organisation that encoded the audio file. This field may contain a
+ copyright message, if the audio file also is copyrighted by the
+ encoder.
+
+
+4.2.3. Derived and subjective properties frames
+
+ TBPM
+ The 'BPM' frame contains the number of beats per minute in the
+ main part of the audio. The BPM is an integer and represented as a
+ numerical string.
+
+ TLEN
+ The 'Length' frame contains the length of the audio file in
+ milliseconds, represented as a numeric string.
+
+ TKEY
+ The 'Initial key' frame contains the musical key in which the sound
+ starts. It is represented as a string with a maximum length of three
+ characters. The ground keys are represented with "A","B","C","D","E",
+ "F" and "G" and halfkeys represented with "b" and "#". Minor is
+ represented as "m", e.g. "Dbm" $00. Off key is represented with an
+ "o" only.
+
+ TLAN
+ The 'Language' frame should contain the languages of the text or
+ lyrics spoken or sung in the audio. The language is represented with
+ three characters according to ISO-639-2 [ISO-639-2]. If more than one
+ language is used in the text their language codes should follow
+ according to the amount of their usage, e.g. "eng" $00 "sve" $00.
+
+ TCON
+ The 'Content type', which ID3v1 was stored as a one byte numeric
+ value only, is now a string. You may use one or several of the ID3v1
+ types as numerical strings, or, since the category list would be
+ impossible to maintain with accurate and up to date categories,
+ define your own. Example: "21" $00 "Eurodisco" $00
+
+ You may also use any of the following keywords:
+
+ RX Remix
+ CR Cover
+
+ TFLT
+ The 'File type' frame indicates which type of audio this tag defines.
+ The following types and refinements are defined:
+
+ MIME MIME type follows
+ MPG MPEG Audio
+ /1 MPEG 1/2 layer I
+ /2 MPEG 1/2 layer II
+ /3 MPEG 1/2 layer III
+ /2.5 MPEG 2.5
+ /AAC Advanced audio compression
+ VQF Transform-domain Weighted Interleave Vector Quantisation
+ PCM Pulse Code Modulated audio
+
+ but other types may be used, but not for these types though. This is
+ used in a similar way to the predefined types in the "TMED" frame,
+ but without parentheses. If this frame is not present audio type is
+ assumed to be "MPG".
+
+ TMED
+ The 'Media type' frame describes from which media the sound
+ originated. This may be a text string or a reference to the
+ predefined media types found in the list below. Example:
+ "VID/PAL/VHS" $00.
+
+ DIG Other digital media
+ /A Analogue transfer from media
+
+ ANA Other analogue media
+ /WAC Wax cylinder
+ /8CA 8-track tape cassette
+
+ CD CD
+ /A Analogue transfer from media
+ /DD DDD
+ /AD ADD
+ /AA AAD
+
+ LD Laserdisc
+
+ TT Turntable records
+ /33 33.33 rpm
+ /45 45 rpm
+ /71 71.29 rpm
+ /76 76.59 rpm
+ /78 78.26 rpm
+ /80 80 rpm
+
+ MD MiniDisc
+ /A Analogue transfer from media
+
+ DAT DAT
+ /A Analogue transfer from media
+ /1 standard, 48 kHz/16 bits, linear
+ /2 mode 2, 32 kHz/16 bits, linear
+ /3 mode 3, 32 kHz/12 bits, non-linear, low speed
+ /4 mode 4, 32 kHz/12 bits, 4 channels
+ /5 mode 5, 44.1 kHz/16 bits, linear
+ /6 mode 6, 44.1 kHz/16 bits, 'wide track' play
+
+ DCC DCC
+ /A Analogue transfer from media
+
+ DVD DVD
+ /A Analogue transfer from media
+
+ TV Television
+ /PAL PAL
+ /NTSC NTSC
+ /SECAM SECAM
+
+ VID Video
+ /PAL PAL
+ /NTSC NTSC
+ /SECAM SECAM
+ /VHS VHS
+ /SVHS S-VHS
+ /BETA BETAMAX
+
+ RAD Radio
+ /FM FM
+ /AM AM
+ /LW LW
+ /MW MW
+
+ TEL Telephone
+ /I ISDN
+
+ MC MC (normal cassette)
+ /4 4.75 cm/s (normal speed for a two sided cassette)
+ /9 9.5 cm/s
+ /I Type I cassette (ferric/normal)
+ /II Type II cassette (chrome)
+ /III Type III cassette (ferric chrome)
+ /IV Type IV cassette (metal)
+
+ REE Reel
+ /9 9.5 cm/s
+ /19 19 cm/s
+ /38 38 cm/s
+ /76 76 cm/s
+ /I Type I cassette (ferric/normal)
+ /II Type II cassette (chrome)
+ /III Type III cassette (ferric chrome)
+ /IV Type IV cassette (metal)
+
+ TMOO
+ The 'Mood' frame is intended to reflect the mood of the audio with a
+ few keywords, e.g. "Romantic" or "Sad".
+
+
+4.2.4. Rights and license frames
+
+ TCOP
+ The 'Copyright message' frame, in which the string must begin with a
+ year and a space character (making five characters), is intended for
+ the copyright holder of the original sound, not the audio file
+ itself. The absence of this frame means only that the copyright
+ information is unavailable or has been removed, and must not be
+ interpreted to mean that the audio is public domain. Every time this
+ field is displayed the field must be preceded with "Copyright " (C) "
+ ", where (C) is one character showing a C in a circle.
+
+ TPRO
+ The 'Produced notice' frame, in which the string must begin with a
+ year and a space character (making five characters), is intended for
+ the production copyright holder of the original sound, not the audio
+ file itself. The absence of this frame means only that the production
+ copyright information is unavailable or has been removed, and must
+ not be interpreted to mean that the audio is public domain. Every
+ time this field is displayed the field must be preceded with
+ "Produced " (P) " ", where (P) is one character showing a P in a
+ circle.
+
+ TPUB
+ The 'Publisher' frame simply contains the name of the label or
+ publisher.
+
+ TOWN
+ The 'File owner/licensee' frame contains the name of the owner or
+ licensee of the file and it's contents.
+
+ TRSN
+ The 'Internet radio station name' frame contains the name of the
+ internet radio station from which the audio is streamed.
+
+ TRSO
+ The 'Internet radio station owner' frame contains the name of the
+ owner of the internet radio station from which the audio is
+ streamed.
+
+4.2.5. Other text frames
+
+ TOFN
+ The 'Original filename' frame contains the preferred filename for the
+ file, since some media doesn't allow the desired length of the
+ filename. The filename is case sensitive and includes its suffix.
+
+ TDLY
+ The 'Playlist delay' defines the numbers of milliseconds of silence
+ that should be inserted before this audio. The value zero indicates
+ that this is a part of a multifile audio track that should be played
+ continuously.
+
+ TDEN
+ The 'Encoding time' frame contains a timestamp describing when the
+ audio was encoded. Timestamp format is described in the ID3v2
+ structure document [ID3v2-strct].
+
+ TDOR
+ The 'Original release time' frame contains a timestamp describing
+ when the original recording of the audio was released. Timestamp
+ format is described in the ID3v2 structure document [ID3v2-strct].
+
+ TDRC
+ The 'Recording time' frame contains a timestamp describing when the
+ audio was recorded. Timestamp format is described in the ID3v2
+ structure document [ID3v2-strct].
+
+ TDRL
+ The 'Release time' frame contains a timestamp describing when the
+ audio was first released. Timestamp format is described in the ID3v2
+ structure document [ID3v2-strct].
+
+ TDTG
+ The 'Tagging time' frame contains a timestamp describing then the
+ audio was tagged. Timestamp format is described in the ID3v2
+ structure document [ID3v2-strct].
+
+ TSSE
+ The 'Software/Hardware and settings used for encoding' frame
+ includes the used audio encoder and its settings when the file was
+ encoded. Hardware refers to hardware encoders, not the computer on
+ which a program was run.
+
+ TSOA
+ The 'Album sort order' frame defines a string which should be used
+ instead of the album name (TALB) for sorting purposes. E.g. an album
+ named "A Soundtrack" might preferably be sorted as "Soundtrack".
+
+ TSOP
+ The 'Performer sort order' frame defines a string which should be
+ used instead of the performer (TPE2) for sorting purposes.
+
+ TSOT
+ The 'Title sort order' frame defines a string which should be used
+ instead of the title (TIT2) for sorting purposes.
+
+
+4.2.6. User defined text information frame
+
+ This frame is intended for one-string text information concerning the
+ audio file in a similar way to the other "T"-frames. The frame body
+ consists of a description of the string, represented as a terminated
+ string, followed by the actual string. There may be more than one
+ "TXXX" frame in each tag, but only one with the same description.
+
+ <Header for 'User defined text information frame', ID: "TXXX">
+ Text encoding $xx
+ Description <text string according to encoding> $00 (00)
+ Value <text string according to encoding>
+
+
+4.3. URL link frames
+
+ With these frames dynamic data such as webpages with touring
+ information, price information or plain ordinary news can be added to
+ the tag. There may only be one URL [URL] link frame of its kind in an
+ tag, except when stated otherwise in the frame description. If the
+ text string is followed by a string termination, all the following
+ information should be ignored and not be displayed. All URL link
+ frame identifiers begins with "W". Only URL link frame identifiers
+ begins with "W", except for "WXXX". All URL link frames have the
+ following format:
+
+ <Header for 'URL link frame', ID: "W000" - "WZZZ", excluding "WXXX"
+ described in 4.3.2.>
+ URL <text string>
+
+
+4.3.1. URL link frames - details
+
+ WCOM
+ The 'Commercial information' frame is a URL pointing at a webpage
+ with information such as where the album can be bought. There may be
+ more than one "WCOM" frame in a tag, but not with the same content.
+
+ WCOP
+ The 'Copyright/Legal information' frame is a URL pointing at a
+ webpage where the terms of use and ownership of the file is
+ described.
+
+ WOAF
+ The 'Official audio file webpage' frame is a URL pointing at a file
+ specific webpage.
+
+ WOAR
+ The 'Official artist/performer webpage' frame is a URL pointing at
+ the artists official webpage. There may be more than one "WOAR" frame
+ in a tag if the audio contains more than one performer, but not with
+ the same content.
+
+ WOAS
+ The 'Official audio source webpage' frame is a URL pointing at the
+ official webpage for the source of the audio file, e.g. a movie.
+
+ WORS
+ The 'Official Internet radio station homepage' contains a URL
+ pointing at the homepage of the internet radio station.
+
+ WPAY
+ The 'Payment' frame is a URL pointing at a webpage that will handle
+ the process of paying for this file.
+
+ WPUB
+ The 'Publishers official webpage' frame is a URL pointing at the
+ official webpage for the publisher.
+
+
+4.3.2. User defined URL link frame
+
+ This frame is intended for URL [URL] links concerning the audio file
+ in a similar way to the other "W"-frames. The frame body consists
+ of a description of the string, represented as a terminated string,
+ followed by the actual URL. The URL is always encoded with ISO-8859-1
+ [ISO-8859-1]. There may be more than one "WXXX" frame in each tag,
+ but only one with the same description.
+
+ <Header for 'User defined URL link frame', ID: "WXXX">
+ Text encoding $xx
+ Description <text string according to encoding> $00 (00)
+ URL <text string>
+
+
+4.4. Music CD identifier
+
+ This frame is intended for music that comes from a CD, so that the CD
+ can be identified in databases such as the CDDB [CDDB]. The frame
+ consists of a binary dump of the Table Of Contents, TOC, from the CD,
+ which is a header of 4 bytes and then 8 bytes/track on the CD plus 8
+ bytes for the 'lead out', making a maximum of 804 bytes. The offset
+ to the beginning of every track on the CD should be described with a
+ four bytes absolute CD-frame address per track, and not with absolute
+ time. When this frame is used the presence of a valid "TRCK" frame is
+ REQUIRED, even if the CD's only got one track. It is recommended that
+ this frame is always added to tags originating from CDs. There may
+ only be one "MCDI" frame in each tag.
+
+ <Header for 'Music CD identifier', ID: "MCDI">
+ CD TOC <binary data>
+
+
+4.5. Event timing codes
+
+ This frame allows synchronisation with key events in the audio. The
+ header is:
+
+ <Header for 'Event timing codes', ID: "ETCO">
+ Time stamp format $xx
+
+ Where time stamp format is:
+
+ $01 Absolute time, 32 bit sized, using MPEG [MPEG] frames as unit
+ $02 Absolute time, 32 bit sized, using milliseconds as unit
+
+ Absolute time means that every stamp contains the time from the
+ beginning of the file.
+
+ Followed by a list of key events in the following format:
+
+ Type of event $xx
+ Time stamp $xx (xx ...)
+
+ The 'Time stamp' is set to zero if directly at the beginning of the
+ sound or after the previous event. All events MUST be sorted in
+ chronological order. The type of event is as follows:
+
+ $00 padding (has no meaning)
+ $01 end of initial silence
+ $02 intro start
+ $03 main part start
+ $04 outro start
+ $05 outro end
+ $06 verse start
+ $07 refrain start
+ $08 interlude start
+ $09 theme start
+ $0A variation start
+ $0B key change
+ $0C time change
+ $0D momentary unwanted noise (Snap, Crackle & Pop)
+ $0E sustained noise
+ $0F sustained noise end
+ $10 intro end
+ $11 main part end
+ $12 verse end
+ $13 refrain end
+ $14 theme end
+ $15 profanity
+ $16 profanity end
+
+ $17-$DF reserved for future use
+
+ $E0-$EF not predefined synch 0-F
+
+ $F0-$FC reserved for future use
+
+ $FD audio end (start of silence)
+ $FE audio file ends
+ $FF one more byte of events follows (all the following bytes with
+ the value $FF have the same function)
+
+ Terminating the start events such as "intro start" is OPTIONAL. The
+ 'Not predefined synch's ($E0-EF) are for user events. You might want
+ to synchronise your music to something, like setting off an explosion
+ on-stage, activating a screensaver etc.
+
+ There may only be one "ETCO" frame in each tag.
+
+
+4.6. MPEG location lookup table
+
+ To increase performance and accuracy of jumps within a MPEG [MPEG]
+ audio file, frames with time codes in different locations in the file
+ might be useful. This ID3v2 frame includes references that the
+ software can use to calculate positions in the file. After the frame
+ header follows a descriptor of how much the 'frame counter' should be
+ increased for every reference. If this value is two then the first
+ reference points out the second frame, the 2nd reference the 4th
+ frame, the 3rd reference the 6th frame etc. In a similar way the
+ 'bytes between reference' and 'milliseconds between reference' points
+ out bytes and milliseconds respectively.
+
+ Each reference consists of two parts; a certain number of bits, as
+ defined in 'bits for bytes deviation', that describes the difference
+ between what is said in 'bytes between reference' and the reality and
+ a certain number of bits, as defined in 'bits for milliseconds
+ deviation', that describes the difference between what is said in
+ 'milliseconds between reference' and the reality. The number of bits
+ in every reference, i.e. 'bits for bytes deviation'+'bits for
+ milliseconds deviation', must be a multiple of four. There may only
+ be one "MLLT" frame in each tag.
+
+ <Header for 'Location lookup table', ID: "MLLT">
+ MPEG frames between reference $xx xx
+ Bytes between reference $xx xx xx
+ Milliseconds between reference $xx xx xx
+ Bits for bytes deviation $xx
+ Bits for milliseconds dev. $xx
+
+ Then for every reference the following data is included;
+
+ Deviation in bytes %xxx....
+ Deviation in milliseconds %xxx....
+
+
+4.7. Synchronised tempo codes
+
+ For a more accurate description of the tempo of a musical piece, this
+ frame might be used. After the header follows one byte describing
+ which time stamp format should be used. Then follows one or more
+ tempo codes. Each tempo code consists of one tempo part and one time
+ part. The tempo is in BPM described with one or two bytes. If the
+ first byte has the value $FF, one more byte follows, which is added
+ to the first giving a range from 2 - 510 BPM, since $00 and $01 is
+ reserved. $00 is used to describe a beat-free time period, which is
+ not the same as a music-free time period. $01 is used to indicate one
+ single beat-stroke followed by a beat-free period.
+
+ The tempo descriptor is followed by a time stamp. Every time the
+ tempo in the music changes, a tempo descriptor may indicate this for
+ the player. All tempo descriptors MUST be sorted in chronological
+ order. The first beat-stroke in a time-period is at the same time as
+ the beat description occurs. There may only be one "SYTC" frame in
+ each tag.
+
+ <Header for 'Synchronised tempo codes', ID: "SYTC">
+ Time stamp format $xx
+ Tempo data <binary data>
+
+ Where time stamp format is:
+
+ $01 Absolute time, 32 bit sized, using MPEG [MPEG] frames as unit
+ $02 Absolute time, 32 bit sized, using milliseconds as unit
+
+ Absolute time means that every stamp contains the time from the
+ beginning of the file.
+
+
+4.8. Unsynchronised lyrics/text transcription
+
+ This frame contains the lyrics of the song or a text transcription of
+ other vocal activities. The head includes an encoding descriptor and
+ a content descriptor. The body consists of the actual text. The
+ 'Content descriptor' is a terminated string. If no descriptor is
+ entered, 'Content descriptor' is $00 (00) only. Newline characters
+ are allowed in the text. There may be more than one 'Unsynchronised
+ lyrics/text transcription' frame in each tag, but only one with the
+ same language and content descriptor.
+
+ <Header for 'Unsynchronised lyrics/text transcription', ID: "USLT">
+ Text encoding $xx
+ Language $xx xx xx
+ Content descriptor <text string according to encoding> $00 (00)
+ Lyrics/text <full text string according to encoding>
+
+
+4.9. Synchronised lyrics/text
+
+ This is another way of incorporating the words, said or sung lyrics,
+ in the audio file as text, this time, however, in sync with the
+ audio. It might also be used to describing events e.g. occurring on a
+ stage or on the screen in sync with the audio. The header includes a
+ content descriptor, represented with as terminated text string. If no
+ descriptor is entered, 'Content descriptor' is $00 (00) only.
+
+ <Header for 'Synchronised lyrics/text', ID: "SYLT">
+ Text encoding $xx
+ Language $xx xx xx
+ Time stamp format $xx
+ Content type $xx
+ Content descriptor <text string according to encoding> $00 (00)
+
+ Content type: $00 is other
+ $01 is lyrics
+ $02 is text transcription
+ $03 is movement/part name (e.g. "Adagio")
+ $04 is events (e.g. "Don Quijote enters the stage")
+ $05 is chord (e.g. "Bb F Fsus")
+ $06 is trivia/'pop up' information
+ $07 is URLs to webpages
+ $08 is URLs to images
+
+ Time stamp format:
+
+ $01 Absolute time, 32 bit sized, using MPEG [MPEG] frames as unit
+ $02 Absolute time, 32 bit sized, using milliseconds as unit
+
+ Absolute time means that every stamp contains the time from the
+ beginning of the file.
+
+ The text that follows the frame header differs from that of the
+ unsynchronised lyrics/text transcription in one major way. Each
+ syllable (or whatever size of text is considered to be convenient by
+ the encoder) is a null terminated string followed by a time stamp
+ denoting where in the sound file it belongs. Each sync thus has the
+ following structure:
+
+ Terminated text to be synced (typically a syllable)
+ Sync identifier (terminator to above string) $00 (00)
+ Time stamp $xx (xx ...)
+
+ The 'time stamp' is set to zero or the whole sync is omitted if
+ located directly at the beginning of the sound. All time stamps
+ should be sorted in chronological order. The sync can be considered
+ as a validator of the subsequent string.
+
+ Newline characters are allowed in all "SYLT" frames and MUST be used
+ after every entry (name, event etc.) in a frame with the content type
+ $03 - $04.
+
+ A few considerations regarding whitespace characters: Whitespace
+ separating words should mark the beginning of a new word, thus
+ occurring in front of the first syllable of a new word. This is also
+ valid for new line characters. A syllable followed by a comma should
+ not be broken apart with a sync (both the syllable and the comma
+ should be before the sync).
+
+ An example: The "USLT" passage
+
+ "Strangers in the night" $0A "Exchanging glances"
+
+ would be "SYLT" encoded as:
+
+ "Strang" $00 xx xx "ers" $00 xx xx " in" $00 xx xx " the" $00 xx xx
+ " night" $00 xx xx 0A "Ex" $00 xx xx "chang" $00 xx xx "ing" $00 xx
+ xx "glan" $00 xx xx "ces" $00 xx xx
+
+ There may be more than one "SYLT" frame in each tag, but only one
+ with the same language and content descriptor.
+
+
+4.10. Comments
+
+ This frame is intended for any kind of full text information that
+ does not fit in any other frame. It consists of a frame header
+ followed by encoding, language and content descriptors and is ended
+ with the actual comment as a text string. Newline characters are
+ allowed in the comment text string. There may be more than one
+ comment frame in each tag, but only one with the same language and
+ content descriptor.
+
+ <Header for 'Comment', ID: "COMM">
+ Text encoding $xx
+ Language $xx xx xx
+ Short content descrip. <text string according to encoding> $00 (00)
+ The actual text <full text string according to encoding>
+
+
+4.11. Relative volume adjustment (2)
+
+ This is a more subjective frame than the previous ones. It allows the
+ user to say how much he wants to increase/decrease the volume on each
+ channel when the file is played. The purpose is to be able to align
+ all files to a reference volume, so that you don't have to change the
+ volume constantly. This frame may also be used to balance adjust the
+ audio. The volume adjustment is encoded as a fixed point decibel
+ value, 16 bit signed integer representing (adjustment*512), giving
+ +/- 64 dB with a precision of 0.001953125 dB. E.g. +2 dB is stored as
+ $04 00 and -2 dB is $FC 00. There may be more than one "RVA2" frame
+ in each tag, but only one with the same identification string.
+
+ <Header for 'Relative volume adjustment (2)', ID: "RVA2">
+ Identification <text string> $00
+
+ The 'identification' string is used to identify the situation and/or
+ device where this adjustment should apply. The following is then
+ repeated for every channel
+
+ Type of channel $xx
+ Volume adjustment $xx xx
+ Bits representing peak $xx
+ Peak volume $xx (xx ...)
+
+
+ Type of channel: $00 Other
+ $01 Master volume
+ $02 Front right
+ $03 Front left
+ $04 Back right
+ $05 Back left
+ $06 Front centre
+ $07 Back centre
+ $08 Subwoofer
+
+ Bits representing peak can be any number between 0 and 255. 0 means
+ that there is no peak volume field. The peak volume field is always
+ padded to whole bytes, setting the most significant bits to zero.
+
+
+4.12. Equalisation (2)
+
+ This is another subjective, alignment frame. It allows the user to
+ predefine an equalisation curve within the audio file. There may be
+ more than one "EQU2" frame in each tag, but only one with the same
+ identification string.
+
+ <Header of 'Equalisation (2)', ID: "EQU2">
+ Interpolation method $xx
+ Identification <text string> $00
+
+ The 'interpolation method' describes which method is preferred when
+ an interpolation between the adjustment point that follows. The
+ following methods are currently defined:
+
+ $00 Band
+ No interpolation is made. A jump from one adjustment level to
+ another occurs in the middle between two adjustment points.
+ $01 Linear
+ Interpolation between adjustment points is linear.
+
+ The 'identification' string is used to identify the situation and/or
+ device where this adjustment should apply. The following is then
+ repeated for every adjustment point
+
+ Frequency $xx xx
+ Volume adjustment $xx xx
+
+ The frequency is stored in units of 1/2 Hz, giving it a range from 0
+ to 32767 Hz.
+
+ The volume adjustment is encoded as a fixed point decibel value, 16
+ bit signed integer representing (adjustment*512), giving +/- 64 dB
+ with a precision of 0.001953125 dB. E.g. +2 dB is stored as $04 00
+ and -2 dB is $FC 00.
+
+ Adjustment points should be ordered by frequency and one frequency
+ should only be described once in the frame.
+
+
+4.13. Reverb
+
+ Yet another subjective frame, with which you can adjust echoes of
+ different kinds. Reverb left/right is the delay between every bounce
+ in ms. Reverb bounces left/right is the number of bounces that should
+ be made. $FF equals an infinite number of bounces. Feedback is the
+ amount of volume that should be returned to the next echo bounce. $00
+ is 0%, $FF is 100%. If this value were $7F, there would be 50% volume
+ reduction on the first bounce, 50% of that on the second and so on.
+ Left to left means the sound from the left bounce to be played in the
+ left speaker, while left to right means sound from the left bounce to
+ be played in the right speaker.
+
+ 'Premix left to right' is the amount of left sound to be mixed in the
+ right before any reverb is applied, where $00 id 0% and $FF is 100%.
+ 'Premix right to left' does the same thing, but right to left.
+ Setting both premix to $FF would result in a mono output (if the
+ reverb is applied symmetric). There may only be one "RVRB" frame in
+ each tag.
+
+ <Header for 'Reverb', ID: "RVRB">
+ Reverb left (ms) $xx xx
+ Reverb right (ms) $xx xx
+ Reverb bounces, left $xx
+ Reverb bounces, right $xx
+ Reverb feedback, left to left $xx
+ Reverb feedback, left to right $xx
+ Reverb feedback, right to right $xx
+ Reverb feedback, right to left $xx
+ Premix left to right $xx
+ Premix right to left $xx
+
+
+4.14. Attached picture
+
+ This frame contains a picture directly related to the audio file.
+ Image format is the MIME type and subtype [MIME] for the image. In
+ the event that the MIME media type name is omitted, "image/" will be
+ implied. The "image/png" [PNG] or "image/jpeg" [JFIF] picture format
+ should be used when interoperability is wanted. Description is a
+ short description of the picture, represented as a terminated
+ text string. There may be several pictures attached to one file, each
+ in their individual "APIC" frame, but only one with the same content
+ descriptor. There may only be one picture with the picture type
+ declared as picture type $01 and $02 respectively. There is the
+ possibility to put only a link to the image file by using the 'MIME
+ type' "-->" and having a complete URL [URL] instead of picture data.
+ The use of linked files should however be used sparingly since there
+ is the risk of separation of files.
+
+ <Header for 'Attached picture', ID: "APIC">
+ Text encoding $xx
+ MIME type <text string> $00
+ Picture type $xx
+ Description <text string according to encoding> $00 (00)
+ Picture data <binary data>
+
+
+ Picture type: $00 Other
+ $01 32x32 pixels 'file icon' (PNG only)
+ $02 Other file icon
+ $03 Cover (front)
+ $04 Cover (back)
+ $05 Leaflet page
+ $06 Media (e.g. label side of CD)
+ $07 Lead artist/lead performer/soloist
+ $08 Artist/performer
+ $09 Conductor
+ $0A Band/Orchestra
+ $0B Composer
+ $0C Lyricist/text writer
+ $0D Recording Location
+ $0E During recording
+ $0F During performance
+ $10 Movie/video screen capture
+ $11 A bright coloured fish
+ $12 Illustration
+ $13 Band/artist logotype
+ $14 Publisher/Studio logotype
+
+
+4.15. General encapsulated object
+
+ In this frame any type of file can be encapsulated. After the header,
+ 'Frame size' and 'Encoding' follows 'MIME type' [MIME] represented as
+ as a terminated string encoded with ISO 8859-1 [ISO-8859-1]. The
+ filename is case sensitive and is encoded as 'Encoding'. Then follows
+ a content description as terminated string, encoded as 'Encoding'.
+ The last thing in the frame is the actual object. The first two
+ strings may be omitted, leaving only their terminations. MIME type is
+ always an ISO-8859-1 text string. There may be more than one "GEOB"
+ frame in each tag, but only one with the same content descriptor.
+
+ <Header for 'General encapsulated object', ID: "GEOB">
+ Text encoding $xx
+ MIME type <text string> $00
+ Filename <text string according to encoding> $00 (00)
+ Content description <text string according to encoding> $00 (00)
+ Encapsulated object <binary data>
+
+
+4.16. Play counter
+
+ This is simply a counter of the number of times a file has been
+ played. The value is increased by one every time the file begins to
+ play. There may only be one "PCNT" frame in each tag. When the
+ counter reaches all one's, one byte is inserted in front of the
+ counter thus making the counter eight bits bigger. The counter must
+ be at least 32-bits long to begin with.
+
+ <Header for 'Play counter', ID: "PCNT">
+ Counter $xx xx xx xx (xx ...)
+
+
+4.17. Popularimeter
+
+ The purpose of this frame is to specify how good an audio file is.
+ Many interesting applications could be found to this frame such as a
+ playlist that features better audio files more often than others or
+ it could be used to profile a person's taste and find other 'good'
+ files by comparing people's profiles. The frame contains the email
+ address to the user, one rating byte and a four byte play counter,
+ intended to be increased with one for every time the file is played.
+ The email is a terminated string. The rating is 1-255 where 1 is
+ worst and 255 is best. 0 is unknown. If no personal counter is wanted
+ it may be omitted. When the counter reaches all one's, one byte is
+ inserted in front of the counter thus making the counter eight bits
+ bigger in the same away as the play counter ("PCNT"). There may be
+ more than one "POPM" frame in each tag, but only one with the same
+ email address.
+
+ <Header for 'Popularimeter', ID: "POPM">
+ Email to user <text string> $00
+ Rating $xx
+ Counter $xx xx xx xx (xx ...)
+
+
+4.18. Recommended buffer size
+
+ Sometimes the server from which an audio file is streamed is aware of
+ transmission or coding problems resulting in interruptions in the
+ audio stream. In these cases, the size of the buffer can be
+ recommended by the server using this frame. If the 'embedded info
+ flag' is true (1) then this indicates that an ID3 tag with the
+ maximum size described in 'Buffer size' may occur in the audio
+ stream. In such case the tag should reside between two MPEG [MPEG]
+ frames, if the audio is MPEG encoded. If the position of the next tag
+ is known, 'offset to next tag' may be used. The offset is calculated
+ from the end of tag in which this frame resides to the first byte of
+ the header in the next. This field may be omitted. Embedded tags are
+ generally not recommended since this could render unpredictable
+ behaviour from present software/hardware.
+
+ For applications like streaming audio it might be an idea to embed
+ tags into the audio stream though. If the clients connects to
+ individual connections like HTTP and there is a possibility to begin
+ every transmission with a tag, then this tag should include a
+ 'recommended buffer size' frame. If the client is connected to a
+ arbitrary point in the stream, such as radio or multicast, then the
+ 'recommended buffer size' frame SHOULD be included in every tag.
+
+ The 'Buffer size' should be kept to a minimum. There may only be one
+ "RBUF" frame in each tag.
+
+ <Header for 'Recommended buffer size', ID: "RBUF">
+ Buffer size $xx xx xx
+ Embedded info flag %0000000x
+ Offset to next tag $xx xx xx xx
+
+
+4.19. Audio encryption
+
+ This frame indicates if the actual audio stream is encrypted, and by
+ whom. Since standardisation of such encryption scheme is beyond this
+ document, all "AENC" frames begin with a terminated string with a
+ URL containing an email address, or a link to a location where an
+ email address can be found, that belongs to the organisation
+ responsible for this specific encrypted audio file. Questions
+ regarding the encrypted audio should be sent to the email address
+ specified. If a $00 is found directly after the 'Frame size' and the
+ audio file indeed is encrypted, the whole file may be considered
+ useless.
+
+ After the 'Owner identifier', a pointer to an unencrypted part of the
+ audio can be specified. The 'Preview start' and 'Preview length' is
+ described in frames. If no part is unencrypted, these fields should
+ be left zeroed. After the 'preview length' field follows optionally a
+ data block required for decryption of the audio. There may be more
+ than one "AENC" frames in a tag, but only one with the same 'Owner
+ identifier'.
+
+ <Header for 'Audio encryption', ID: "AENC">
+ Owner identifier <text string> $00
+ Preview start $xx xx
+ Preview length $xx xx
+ Encryption info <binary data>
+
+
+4.20. Linked information
+
+ To keep information duplication as low as possible this frame may be
+ used to link information from another ID3v2 tag that might reside in
+ another audio file or alone in a binary file. It is RECOMMENDED that
+ this method is only used when the files are stored on a CD-ROM or
+ other circumstances when the risk of file separation is low. The
+ frame contains a frame identifier, which is the frame that should be
+ linked into this tag, a URL [URL] field, where a reference to the
+ file where the frame is given, and additional ID data, if needed.
+ Data should be retrieved from the first tag found in the file to
+ which this link points. There may be more than one "LINK" frame in a
+ tag, but only one with the same contents. A linked frame is to be
+ considered as part of the tag and has the same restrictions as if it
+ was a physical part of the tag (i.e. only one "RVRB" frame allowed,
+ whether it's linked or not).
+
+ <Header for 'Linked information', ID: "LINK">
+ Frame identifier $xx xx xx xx
+ URL <text string> $00
+ ID and additional data <text string(s)>
+
+ Frames that may be linked and need no additional data are "ASPI",
+ "ETCO", "EQU2", "MCID", "MLLT", "OWNE", "RVA2", "RVRB", "SYTC", the
+ text information frames and the URL link frames.
+
+ The "AENC", "APIC", "GEOB" and "TXXX" frames may be linked with
+ the content descriptor as additional ID data.
+
+ The "USER" frame may be linked with the language field as additional
+ ID data.
+
+ The "PRIV" frame may be linked with the owner identifier as
+ additional ID data.
+
+ The "COMM", "SYLT" and "USLT" frames may be linked with three bytes
+ of language descriptor directly followed by a content descriptor as
+ additional ID data.
+
+
+4.21. Position synchronisation frame
+
+ This frame delivers information to the listener of how far into the
+ audio stream he picked up; in effect, it states the time offset from
+ the first frame in the stream. The frame layout is:
+
+ <Head for 'Position synchronisation', ID: "POSS">
+ Time stamp format $xx
+ Position $xx (xx ...)
+
+ Where time stamp format is:
+
+ $01 Absolute time, 32 bit sized, using MPEG frames as unit
+ $02 Absolute time, 32 bit sized, using milliseconds as unit
+
+ and position is where in the audio the listener starts to receive,
+ i.e. the beginning of the next frame. If this frame is used in the
+ beginning of a file the value is always 0. There may only be one
+ "POSS" frame in each tag.
+
+
+4.22. Terms of use frame
+
+ This frame contains a brief description of the terms of use and
+ ownership of the file. More detailed information concerning the legal
+ terms might be available through the "WCOP" frame. Newlines are
+ allowed in the text. There may be more than one 'Terms of use' frame
+ in a tag, but only one with the same 'Language'.
+
+ <Header for 'Terms of use frame', ID: "USER">
+ Text encoding $xx
+ Language $xx xx xx
+ The actual text <text string according to encoding>
+
+
+4.23. Ownership frame
+
+ The ownership frame might be used as a reminder of a made transaction
+ or, if signed, as proof. Note that the "USER" and "TOWN" frames are
+ good to use in conjunction with this one. The frame begins, after the
+ frame ID, size and encoding fields, with a 'price paid' field. The
+ first three characters of this field contains the currency used for
+ the transaction, encoded according to ISO 4217 [ISO-4217] alphabetic
+ currency code. Concatenated to this is the actual price paid, as a
+ numerical string using "." as the decimal separator. Next is an 8
+ character date string (YYYYMMDD) followed by a string with the name
+ of the seller as the last field in the frame. There may only be one
+ "OWNE" frame in a tag.
+
+ <Header for 'Ownership frame', ID: "OWNE">
+ Text encoding $xx
+ Price paid <text string> $00
+ Date of purch. <text string>
+ Seller <text string according to encoding>
+
+
+4.24. Commercial frame
+
+ This frame enables several competing offers in the same tag by
+ bundling all needed information. That makes this frame rather complex
+ but it's an easier solution than if one tries to achieve the same
+ result with several frames. The frame begins, after the frame ID,
+ size and encoding fields, with a price string field. A price is
+ constructed by one three character currency code, encoded according
+ to ISO 4217 [ISO-4217] alphabetic currency code, followed by a
+ numerical value where "." is used as decimal separator. In the price
+ string several prices may be concatenated, separated by a "/"
+ character, but there may only be one currency of each type.
+
+ The price string is followed by an 8 character date string in the
+ format YYYYMMDD, describing for how long the price is valid. After
+ that is a contact URL, with which the user can contact the seller,
+ followed by a one byte 'received as' field. It describes how the
+ audio is delivered when bought according to the following list:
+
+ $00 Other
+ $01 Standard CD album with other songs
+ $02 Compressed audio on CD
+ $03 File over the Internet
+ $04 Stream over the Internet
+ $05 As note sheets
+ $06 As note sheets in a book with other sheets
+ $07 Music on other media
+ $08 Non-musical merchandise
+
+ Next follows a terminated string with the name of the seller followed
+ by a terminated string with a short description of the product. The
+ last thing is the ability to include a company logotype. The first of
+ them is the 'Picture MIME type' field containing information about
+ which picture format is used. In the event that the MIME media type
+ name is omitted, "image/" will be implied. Currently only "image/png"
+ and "image/jpeg" are allowed. This format string is followed by the
+ binary picture data. This two last fields may be omitted if no
+ picture is attached. There may be more than one 'commercial frame' in
+ a tag, but no two may be identical.
+
+ <Header for 'Commercial frame', ID: "COMR">
+ Text encoding $xx
+ Price string <text string> $00
+ Valid until <text string>
+ Contact URL <text string> $00
+ Received as $xx
+ Name of seller <text string according to encoding> $00 (00)
+ Description <text string according to encoding> $00 (00)
+ Picture MIME type <string> $00
+ Seller logo <binary data>
+
+
+4.25. Encryption method registration
+
+ To identify with which method a frame has been encrypted the
+ encryption method must be registered in the tag with this frame. The
+ 'Owner identifier' is a null-terminated string with a URL [URL]
+ containing an email address, or a link to a location where an email
+ address can be found, that belongs to the organisation responsible
+ for this specific encryption method. Questions regarding the
+ encryption method should be sent to the indicated email address. The
+ 'Method symbol' contains a value that is associated with this method
+ throughout the whole tag, in the range $80-F0. All other values are
+ reserved. The 'Method symbol' may optionally be followed by
+ encryption specific data. There may be several "ENCR" frames in a tag
+ but only one containing the same symbol and only one containing the
+ same owner identifier. The method must be used somewhere in the tag.
+ See the description of the frame encryption flag in the ID3v2
+ structure document [ID3v2-strct] for more information.
+
+ <Header for 'Encryption method registration', ID: "ENCR">
+ Owner identifier <text string> $00
+ Method symbol $xx
+ Encryption data <binary data>
+
+
+4.26. Group identification registration
+
+ This frame enables grouping of otherwise unrelated frames. This can
+ be used when some frames are to be signed. To identify which frames
+ belongs to a set of frames a group identifier must be registered in
+ the tag with this frame. The 'Owner identifier' is a null-terminated
+ string with a URL [URL] containing an email address, or a link to a
+ location where an email address can be found, that belongs to the
+ organisation responsible for this grouping. Questions regarding the
+ grouping should be sent to the indicated email address. The 'Group
+ symbol' contains a value that associates the frame with this group
+ throughout the whole tag, in the range $80-F0. All other values are
+ reserved. The 'Group symbol' may optionally be followed by some group
+ specific data, e.g. a digital signature. There may be several "GRID"
+ frames in a tag but only one containing the same symbol and only one
+ containing the same owner identifier. The group symbol must be used
+ somewhere in the tag. See the description of the frame grouping flag
+ in the ID3v2 structure document [ID3v2-strct] for more information.
+
+ <Header for 'Group ID registration', ID: "GRID">
+ Owner identifier <text string> $00
+ Group symbol $xx
+ Group dependent data <binary data>
+
+
+4.27. Private frame
+
+ This frame is used to contain information from a software producer
+ that its program uses and does not fit into the other frames. The
+ frame consists of an 'Owner identifier' string and the binary data.
+ The 'Owner identifier' is a null-terminated string with a URL [URL]
+ containing an email address, or a link to a location where an email
+ address can be found, that belongs to the organisation responsible
+ for the frame. Questions regarding the frame should be sent to the
+ indicated email address. The tag may contain more than one "PRIV"
+ frame but only with different contents.
+
+ <Header for 'Private frame', ID: "PRIV">
+ Owner identifier <text string> $00
+ The private data <binary data>
+
+
+4.28. Signature frame
+
+ This frame enables a group of frames, grouped with the 'Group
+ identification registration', to be signed. Although signatures can
+ reside inside the registration frame, it might be desired to store
+ the signature elsewhere, e.g. in watermarks. There may be more than
+ one 'signature frame' in a tag, but no two may be identical.
+
+ <Header for 'Signature frame', ID: "SIGN">
+ Group symbol $xx
+ Signature <binary data>
+
+
+4.29. Seek frame
+
+ This frame indicates where other tags in a file/stream can be found.
+ The 'minimum offset to next tag' is calculated from the end of this
+ tag to the beginning of the next. There may only be one 'seek frame'
+ in a tag.
+
+ <Header for 'Seek frame', ID: "SEEK">
+ Minimum offset to next tag $xx xx xx xx
+
+
+4.30. Audio seek point index
+
+ Audio files with variable bit rates are intrinsically difficult to
+ deal with in the case of seeking within the file. The ASPI frame
+ makes seeking easier by providing a list a seek points within the
+ audio file. The seek points are a fractional offset within the audio
+ data, providing a starting point from which to find an appropriate
+ point to start decoding. The presence of an ASPI frame requires the
+ existence of a TLEN frame, indicating the duration of the file in
+ milliseconds. There may only be one 'audio seek point index' frame in
+ a tag.
+
+ <Header for 'Seek Point Index', ID: "ASPI">
+ Indexed data start (S) $xx xx xx xx
+ Indexed data length (L) $xx xx xx xx
+ Number of index points (N) $xx xx
+ Bits per index point (b) $xx
+
+ Then for every index point the following data is included;
+
+ Fraction at index (Fi) $xx (xx)
+
+ 'Indexed data start' is a byte offset from the beginning of the file.
+ 'Indexed data length' is the byte length of the audio data being
+ indexed. 'Number of index points' is the number of index points, as
+ the name implies. The recommended number is 100. 'Bits per index
+ point' is 8 or 16, depending on the chosen precision. 8 bits works
+ well for short files (less than 5 minutes of audio), while 16 bits is
+ advantageous for long files. 'Fraction at index' is the numerator of
+ the fraction representing a relative position in the data. The
+ denominator is 2 to the power of b.
+
+ Here are the algorithms to be used in the calculation. The known data
+ must be the offset of the start of the indexed data (S), the offset
+ of the end of the indexed data (E), the number of index points (N),
+ the offset at index i (Oi). We calculate the fraction at index i
+ (Fi).
+
+ Oi is the offset of the frame whose start is soonest after the point
+ for which the time offset is (i/N * duration).
+
+ The frame data should be calculated as follows:
+
+ Fi = Oi/L * 2^b (rounded down to the nearest integer)
+
+ Offset calculation should be calculated as follows from data in the
+ frame:
+
+ Oi = (Fi/2^b)*L (rounded up to the nearest integer)
+
+
+5. Copyright
+
+ Copyright (C) Martin Nilsson 2000. All Rights Reserved.
+
+ This document and translations of it may be copied and furnished to
+ others, and derivative works that comment on or otherwise explain it
+ or assist in its implementation may be prepared, copied, published
+ and distributed, in whole or in part, without restriction of any
+ kind, provided that a reference to this document is included on all
+ such copies and derivative works. However, this document itself may
+ not be modified in any way and reissued as the original document.
+
+ The limited permissions granted above are perpetual and will not be
+ revoked.
+
+ This document and the information contained herein is provided on an
+ "AS IS" basis and THE AUTHORS DISCLAIMS ALL WARRANTIES, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF
+ THE INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED
+ WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+
+
+6. References
+
+ [CDDB] Compact Disc Data Base
+
+ <url:http://www.cddb.com>
+
+ [ID3v2.3.0] Martin Nilsson, "ID3v2 informal standard".
+
+ <url:http://www.id3.org/id3v2.3.0.txt>
+
+ [ID3v2-strct] Martin Nilsson,
+ "ID3 tag version 2.4.0 - Main Structure"
+
+ <url:http//www.id3.org/id3v2.4.0-structure.txt>
+
+ [ISO-639-2] ISO/FDIS 639-2.
+ Codes for the representation of names of languages, Part 2: Alpha-3
+ code. Technical committee / subcommittee: TC 37 / SC 2
+
+ [ISO-4217] ISO 4217:1995.
+ Codes for the representation of currencies and funds.
+ Technical committee / subcommittee: TC 68
+
+ [ISO-8859-1] ISO/IEC DIS 8859-1.
+ 8-bit single-byte coded graphic character sets, Part 1: Latin
+ alphabet No. 1. Technical committee / subcommittee: JTC 1 / SC 2
+
+ [ISRC] ISO 3901:1986
+ International Standard Recording Code (ISRC).
+ Technical committee / subcommittee: TC 46 / SC 9
+
+ [JFIF] JPEG File Interchange Format, version 1.02
+
+ <url:http://www.w3.org/Graphics/JPEG/jfif.txt>
+
+ [KEYWORDS] S. Bradner, 'Key words for use in RFCs to Indicate
+ Requirement Levels', RFC 2119, March 1997.
+
+ <url:ftp://ftp.isi.edu/in-notes/rfc2119.txt>
+
+ [MIME] Freed, N. and N. Borenstein, "Multipurpose Internet Mail
+ Extensions (MIME) Part One: Format of Internet Message Bodies",
+ RFC 2045, November 1996.
+
+ <url:ftp://ftp.isi.edu/in-notes/rfc2045.txt>
+
+ [MPEG] ISO/IEC 11172-3:1993.
+ Coding of moving pictures and associated audio for digital storage
+ media at up to about 1,5 Mbit/s, Part 3: Audio.
+ Technical committee / subcommittee: JTC 1 / SC 29
+ and
+ ISO/IEC 13818-3:1995
+ Generic coding of moving pictures and associated audio information,
+ Part 3: Audio.
+ Technical committee / subcommittee: JTC 1 / SC 29
+ and
+ ISO/IEC DIS 13818-3
+ Generic coding of moving pictures and associated audio information,
+ Part 3: Audio (Revision of ISO/IEC 13818-3:1995)
+
+
+ [PNG] Portable Network Graphics, version 1.0
+
+ <url:http://www.w3.org/TR/REC-png-multi.html>
+
+ [URL] T. Berners-Lee, L. Masinter & M. McCahill, "Uniform Resource
+ Locators (URL).", RFC 1738, December 1994.
+
+ <url:ftp://ftp.isi.edu/in-notes/rfc1738.txt>
+
+ [ZLIB] P. Deutsch, Aladdin Enterprises & J-L. Gailly, "ZLIB
+ Compressed
+ Data Format Specification version 3.3", RFC 1950, May 1996.
+
+ <url:ftp://ftp.isi.edu/in-notes/rfc1950.txt>
+
+
+7. Appendix
+
+
+A. Appendix A - Genre List from ID3v1
+
+ The following genres is defined in ID3v1
+
+ 0.Blues
+ 1.Classic Rock
+ 2.Country
+ 3.Dance
+ 4.Disco
+ 5.Funk
+ 6.Grunge
+ 7.Hip-Hop
+ 8.Jazz
+ 9.Metal
+ 10.New Age
+ 11.Oldies
+ 12.Other
+ 13.Pop
+ 14.R&B
+ 15.Rap
+ 16.Reggae
+ 17.Rock
+ 18.Techno
+ 19.Industrial
+ 20.Alternative
+ 21.Ska
+ 22.Death Metal
+ 23.Pranks
+ 24.Soundtrack
+ 25.Euro-Techno
+ 26.Ambient
+ 27.Trip-Hop
+ 28.Vocal
+ 29.Jazz+Funk
+ 30.Fusion
+ 31.Trance
+ 32.Classical
+ 33.Instrumental
+ 34.Acid
+ 35.House
+ 36.Game
+ 37.Sound Clip
+ 38.Gospel
+ 39.Noise
+ 40.AlternRock
+ 41.Bass
+ 42.Soul
+ 43.Punk
+ 44.Space
+ 45.Meditative
+ 46.Instrumental Pop
+ 47.Instrumental Rock
+ 48.Ethnic
+ 49.Gothic
+ 50.Darkwave
+ 51.Techno-Industrial
+ 52.Electronic
+ 53.Pop-Folk
+ 54.Eurodance
+ 55.Dream
+ 56.Southern Rock
+ 57.Comedy
+ 58.Cult
+ 59.Gangsta
+ 60.Top 40
+ 61.Christian Rap
+ 62.Pop/Funk
+ 63.Jungle
+ 64.Native American
+ 65.Cabaret
+ 66.New Wave
+ 67.Psychadelic
+ 68.Rave
+ 69.Showtunes
+ 70.Trailer
+ 71.Lo-Fi
+ 72.Tribal
+ 73.Acid Punk
+ 74.Acid Jazz
+ 75.Polka
+ 76.Retro
+ 77.Musical
+ 78.Rock & Roll
+ 79.Hard Rock
+
+
+8. Author's Address
+
+ Written by
+
+ Martin Nilsson
+ Rydsvägen 246 C. 30
+ SE-584 34 Linköping
+ Sweden
+
+ Email: nilsson@id3.org
--- /dev/null
+
+Informal standard M. Nilsson
+Document: id3v2.4.0-structure.txt 16 September 2001
+
+
+ ID3 tag version 2.4.0 - Main Structure
+
+Status of this document
+
+ This document is an informal standard and replaces the ID3v2.3.0
+ standard [ID3v2]. A formal standard will use another revision number
+ even if the content is identical to document. The contents in this
+ document may change for clarifications but never for added or altered
+ functionallity.
+
+ Distribution of this document is unlimited.
+
+
+Abstract
+
+ This document describes the main structure of ID3v2.4.0, which is a
+ revised version of the ID3v2 informal standard [ID3v2] version
+ 2.3.0. The ID3v2 offers a flexible way of storing audio meta
+ information within the audio file itself. The information may be
+ technical information, such as equalisation curves, as well as
+ title, performer, copyright etc.
+
+ ID3v2.4.0 is meant to be as close as possible to ID3v2.3.0 in order
+ to allow for implementations to be revised as easily as possible.
+
+
+1. Table of contents
+
+ Status of this document
+ Abstract
+ 1. Table of contents
+ 2. Conventions in this document
+ 2. Standard overview
+ 3. ID3v2 overview
+ 3.1. ID3v2 header
+ 3.2. ID3v2 extended header
+ 3.3. Padding
+ 3.4. ID3v2 footer
+ 4. ID3v2 frames overview
+ 4.1. Frame header flags
+ 4.1.1. Frame status flags
+ 4.1.2. Frame format flags
+ 5. Tag location
+ 6. Unsynchronisation
+ 6.1. The unsynchronisation scheme
+ 6.2. Synchsafe integers
+ 7. Copyright
+ 8. References
+ 9. Author's Address
+
+
+2. Conventions in this document
+
+ Text within "" is a text string exactly as it appears in a tag.
+ Numbers preceded with $ are hexadecimal and numbers preceded with %
+ are binary. $xx is used to indicate a byte with unknown content. %x
+ is used to indicate a bit with unknown content. The most significant
+ bit (MSB) of a byte is called 'bit 7' and the least significant bit
+ (LSB) is called 'bit 0'.
+
+ A tag is the whole tag described in this document. A frame is a block
+ of information in the tag. The tag consists of a header, frames and
+ optional padding. A field is a piece of information; one value, a
+ string etc. A numeric string is a string that consists of the
+ characters "0123456789" only.
+
+ The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
+ "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this
+ document are to be interpreted as described in RFC 2119 [KEYWORDS].
+
+
+3. ID3v2 overview
+
+ ID3v2 is a general tagging format for audio, which makes it possible
+ to store meta data about the audio inside the audio file itself. The
+ ID3 tag described in this document is mainly targeted at files
+ encoded with MPEG-1/2 layer I, MPEG-1/2 layer II, MPEG-1/2 layer III
+ and MPEG-2.5, but may work with other types of encoded audio or as a
+ stand alone format for audio meta data.
+
+ ID3v2 is designed to be as flexible and expandable as possible to
+ meet new meta information needs that might arise. To achieve that
+ ID3v2 is constructed as a container for several information blocks,
+ called frames, whose format need not be known to the software that
+ encounters them. At the start of every frame is an unique and
+ predefined identifier, a size descriptor that allows software to skip
+ unknown frames and a flags field. The flags describes encoding
+ details and if the frame should remain in the tag, should it be
+ unknown to the software, if the file is altered.
+
+ The bitorder in ID3v2 is most significant bit first (MSB). The
+ byteorder in multibyte numbers is most significant byte first (e.g.
+ $12345678 would be encoded $12 34 56 78), also known as big endian
+ and network byte order.
+
+ Overall tag structure:
+
+ +-----------------------------+
+ | Header (10 bytes) |
+ +-----------------------------+
+ | Extended Header |
+ | (variable length, OPTIONAL) |
+ +-----------------------------+
+ | Frames (variable length) |
+ +-----------------------------+
+ | Padding |
+ | (variable length, OPTIONAL) |
+ +-----------------------------+
+ | Footer (10 bytes, OPTIONAL) |
+ +-----------------------------+
+
+ In general, padding and footer are mutually exclusive. See details in
+ sections 3.3, 3.4 and 5.
+
+
+3.1. ID3v2 header
+
+ The first part of the ID3v2 tag is the 10 byte tag header, laid out
+ as follows:
+
+ ID3v2/file identifier "ID3"
+ ID3v2 version $04 00
+ ID3v2 flags %abcd0000
+ ID3v2 size 4 * %0xxxxxxx
+
+ The first three bytes of the tag are always "ID3", to indicate that
+ this is an ID3v2 tag, directly followed by the two version bytes. The
+ first byte of ID3v2 version is its major version, while the second
+ byte is its revision number. In this case this is ID3v2.4.0. All
+ revisions are backwards compatible while major versions are not. If
+ software with ID3v2.4.0 and below support should encounter version
+ five or higher it should simply ignore the whole tag. Version or
+ revision will never be $FF.
+
+ The version is followed by the ID3v2 flags field, of which currently
+ four flags are used.
+
+
+ a - Unsynchronisation
+
+ Bit 7 in the 'ID3v2 flags' indicates whether or not
+ unsynchronisation is applied on all frames (see section 6.1 for
+ details); a set bit indicates usage.
+
+
+ b - Extended header
+
+ The second bit (bit 6) indicates whether or not the header is
+ followed by an extended header. The extended header is described in
+ section 3.2. A set bit indicates the presence of an extended
+ header.
+
+
+ c - Experimental indicator
+
+ The third bit (bit 5) is used as an 'experimental indicator'. This
+ flag SHALL always be set when the tag is in an experimental stage.
+
+
+ d - Footer present
+
+ Bit 4 indicates that a footer (section 3.4) is present at the very
+ end of the tag. A set bit indicates the presence of a footer.
+
+
+ All the other flags MUST be cleared. If one of these undefined flags
+ are set, the tag might not be readable for a parser that does not
+ know the flags function.
+
+ The ID3v2 tag size is stored as a 32 bit synchsafe integer (section
+ 6.2), making a total of 28 effective bits (representing up to 256MB).
+
+ The ID3v2 tag size is the sum of the byte length of the extended
+ header, the padding and the frames after unsynchronisation. If a
+ footer is present this equals to ('total size' - 20) bytes, otherwise
+ ('total size' - 10) bytes.
+
+ An ID3v2 tag can be detected with the following pattern:
+ $49 44 33 yy yy xx zz zz zz zz
+ Where yy is less than $FF, xx is the 'flags' byte and zz is less than
+ $80.
+
+
+3.2. Extended header
+
+ The extended header contains information that can provide further
+ insight in the structure of the tag, but is not vital to the correct
+ parsing of the tag information; hence the extended header is
+ optional.
+
+ Extended header size 4 * %0xxxxxxx
+ Number of flag bytes $01
+ Extended Flags $xx
+
+ Where the 'Extended header size' is the size of the whole extended
+ header, stored as a 32 bit synchsafe integer. An extended header can
+ thus never have a size of fewer than six bytes.
+
+ The extended flags field, with its size described by 'number of flag
+ bytes', is defined as:
+
+ %0bcd0000
+
+ Each flag that is set in the extended header has data attached, which
+ comes in the order in which the flags are encountered (i.e. the data
+ for flag 'b' comes before the data for flag 'c'). Unset flags cannot
+ have any attached data. All unknown flags MUST be unset and their
+ corresponding data removed when a tag is modified.
+
+ Every set flag's data starts with a length byte, which contains a
+ value between 0 and 127 ($00 - $7f), followed by data that has the
+ field length indicated by the length byte. If a flag has no attached
+ data, the value $00 is used as length byte.
+
+
+ b - Tag is an update
+
+ If this flag is set, the present tag is an update of a tag found
+ earlier in the present file or stream. If frames defined as unique
+ are found in the present tag, they are to override any
+ corresponding ones found in the earlier tag. This flag has no
+ corresponding data.
+
+ Flag data length $00
+
+ c - CRC data present
+
+ If this flag is set, a CRC-32 [ISO-3309] data is included in the
+ extended header. The CRC is calculated on all the data between the
+ header and footer as indicated by the header's tag length field,
+ minus the extended header. Note that this includes the padding (if
+ there is any), but excludes the footer. The CRC-32 is stored as an
+ 35 bit synchsafe integer, leaving the upper four bits always
+ zeroed.
+
+ Flag data length $05
+ Total frame CRC 5 * %0xxxxxxx
+
+ d - Tag restrictions
+
+ For some applications it might be desired to restrict a tag in more
+ ways than imposed by the ID3v2 specification. Note that the
+ presence of these restrictions does not affect how the tag is
+ decoded, merely how it was restricted before encoding. If this flag
+ is set the tag is restricted as follows:
+
+ Flag data length $01
+ Restrictions %ppqrrstt
+
+ p - Tag size restrictions
+
+ 00 No more than 128 frames and 1 MB total tag size.
+ 01 No more than 64 frames and 128 KB total tag size.
+ 10 No more than 32 frames and 40 KB total tag size.
+ 11 No more than 32 frames and 4 KB total tag size.
+
+ q - Text encoding restrictions
+
+ 0 No restrictions
+ 1 Strings are only encoded with ISO-8859-1 [ISO-8859-1] or
+ UTF-8 [UTF-8].
+
+ r - Text fields size restrictions
+
+ 00 No restrictions
+ 01 No string is longer than 1024 characters.
+ 10 No string is longer than 128 characters.
+ 11 No string is longer than 30 characters.
+
+ Note that nothing is said about how many bytes is used to
+ represent those characters, since it is encoding dependent. If a
+ text frame consists of more than one string, the sum of the
+ strungs is restricted as stated.
+
+ s - Image encoding restrictions
+
+ 0 No restrictions
+ 1 Images are encoded only with PNG [PNG] or JPEG [JFIF].
+
+ t - Image size restrictions
+
+ 00 No restrictions
+ 01 All images are 256x256 pixels or smaller.
+ 10 All images are 64x64 pixels or smaller.
+ 11 All images are exactly 64x64 pixels, unless required
+ otherwise.
+
+
+3.3. Padding
+
+ It is OPTIONAL to include padding after the final frame (at the end
+ of the ID3 tag), making the size of all the frames together smaller
+ than the size given in the tag header. A possible purpose of this
+ padding is to allow for adding a few additional frames or enlarge
+ existing frames within the tag without having to rewrite the entire
+ file. The value of the padding bytes must be $00. A tag MUST NOT have
+ any padding between the frames or between the tag header and the
+ frames. Furthermore it MUST NOT have any padding when a tag footer is
+ added to the tag.
+
+
+3.4. ID3v2 footer
+
+ To speed up the process of locating an ID3v2 tag when searching from
+ the end of a file, a footer can be added to the tag. It is REQUIRED
+ to add a footer to an appended tag, i.e. a tag located after all
+ audio data. The footer is a copy of the header, but with a different
+ identifier.
+
+ ID3v2 identifier "3DI"
+ ID3v2 version $04 00
+ ID3v2 flags %abcd0000
+ ID3v2 size 4 * %0xxxxxxx
+
+
+4. ID3v2 frame overview
+
+ All ID3v2 frames consists of one frame header followed by one or more
+ fields containing the actual information. The header is always 10
+ bytes and laid out as follows:
+
+ Frame ID $xx xx xx xx (four characters)
+ Size 4 * %0xxxxxxx
+ Flags $xx xx
+
+ The frame ID is made out of the characters capital A-Z and 0-9.
+ Identifiers beginning with "X", "Y" and "Z" are for experimental
+ frames and free for everyone to use, without the need to set the
+ experimental bit in the tag header. Bear in mind that someone else
+ might have used the same identifier as you. All other identifiers are
+ either used or reserved for future use.
+
+ The frame ID is followed by a size descriptor containing the size of
+ the data in the final frame, after encryption, compression and
+ unsynchronisation. The size is excluding the frame header ('total
+ frame size' - 10 bytes) and stored as a 32 bit synchsafe integer.
+
+ In the frame header the size descriptor is followed by two flag
+ bytes. These flags are described in section 4.1.
+
+ There is no fixed order of the frames' appearance in the tag,
+ although it is desired that the frames are arranged in order of
+ significance concerning the recognition of the file. An example of
+ such order: UFID, TIT2, MCDI, TRCK ...
+
+ A tag MUST contain at least one frame. A frame must be at least 1
+ byte big, excluding the header.
+
+ If nothing else is said, strings, including numeric strings and URLs
+ [URL], are represented as ISO-8859-1 [ISO-8859-1] characters in the
+ range $20 - $FF. Such strings are represented in frame descriptions
+ as <text string>, or <full text string> if newlines are allowed. If
+ nothing else is said newline character is forbidden. In ISO-8859-1 a
+ newline is represented, when allowed, with $0A only.
+
+ Frames that allow different types of text encoding contains a text
+ encoding description byte. Possible encodings:
+
+ $00 ISO-8859-1 [ISO-8859-1]. Terminated with $00.
+ $01 UTF-16 [UTF-16] encoded Unicode [UNICODE] with BOM. All
+ strings in the same frame SHALL have the same byteorder.
+ Terminated with $00 00.
+ $02 UTF-16BE [UTF-16] encoded Unicode [UNICODE] without BOM.
+ Terminated with $00 00.
+ $03 UTF-8 [UTF-8] encoded Unicode [UNICODE]. Terminated with $00.
+
+ Strings dependent on encoding are represented in frame descriptions
+ as <text string according to encoding>, or <full text string
+ according to encoding> if newlines are allowed. Any empty strings of
+ type $01 which are NULL-terminated may have the Unicode BOM followed
+ by a Unicode NULL ($FF FE 00 00 or $FE FF 00 00).
+
+ The timestamp fields are based on a subset of ISO 8601. When being as
+ precise as possible the format of a time string is
+ yyyy-MM-ddTHH:mm:ss (year, "-", month, "-", day, "T", hour (out of
+ 24), ":", minutes, ":", seconds), but the precision may be reduced by
+ removing as many time indicators as wanted. Hence valid timestamps
+ are
+ yyyy, yyyy-MM, yyyy-MM-dd, yyyy-MM-ddTHH, yyyy-MM-ddTHH:mm and
+ yyyy-MM-ddTHH:mm:ss. All time stamps are UTC. For durations, use
+ the slash character as described in 8601, and for multiple non-
+ contiguous dates, use multiple strings, if allowed by the frame
+ definition.
+
+ The three byte language field, present in several frames, is used to
+ describe the language of the frame's content, according to ISO-639-2
+ [ISO-639-2]. The language should be represented in lower case. If the
+ language is not known the string "XXX" should be used.
+
+ All URLs [URL] MAY be relative, e.g. "picture.png", "../doc.txt".
+
+ If a frame is longer than it should be, e.g. having more fields than
+ specified in this document, that indicates that additions to the
+ frame have been made in a later version of the ID3v2 standard. This
+ is reflected by the revision number in the header of the tag.
+
+
+4.1. Frame header flags
+
+ In the frame header the size descriptor is followed by two flag
+ bytes. All unused flags MUST be cleared. The first byte is for
+ 'status messages' and the second byte is a format description. If an
+ unknown flag is set in the first byte the frame MUST NOT be changed
+ without that bit cleared. If an unknown flag is set in the second
+ byte the frame is likely to not be readable. Some flags in the second
+ byte indicates that extra information is added to the header. These
+ fields of extra information is ordered as the flags that indicates
+ them. The flags field is defined as follows (l and o left out because
+ ther resemblence to one and zero):
+
+ %0abc0000 %0h00kmnp
+
+ Some frame format flags indicate that additional information fields
+ are added to the frame. This information is added after the frame
+ header and before the frame data in the same order as the flags that
+ indicates them. I.e. the four bytes of decompressed size will precede
+ the encryption method byte. These additions affects the 'frame size'
+ field, but are not subject to encryption or compression.
+
+ The default status flags setting for a frame is, unless stated
+ otherwise, 'preserved if tag is altered' and 'preserved if file is
+ altered', i.e. %00000000.
+
+
+4.1.1. Frame status flags
+
+ a - Tag alter preservation
+
+ This flag tells the tag parser what to do with this frame if it is
+ unknown and the tag is altered in any way. This applies to all
+ kinds of alterations, including adding more padding and reordering
+ the frames.
+
+ 0 Frame should be preserved.
+ 1 Frame should be discarded.
+
+
+ b - File alter preservation
+
+ This flag tells the tag parser what to do with this frame if it is
+ unknown and the file, excluding the tag, is altered. This does not
+ apply when the audio is completely replaced with other audio data.
+
+ 0 Frame should be preserved.
+ 1 Frame should be discarded.
+
+
+ c - Read only
+
+ This flag, if set, tells the software that the contents of this
+ frame are intended to be read only. Changing the contents might
+ break something, e.g. a signature. If the contents are changed,
+ without knowledge of why the frame was flagged read only and
+ without taking the proper means to compensate, e.g. recalculating
+ the signature, the bit MUST be cleared.
+
+
+4.1.2. Frame format flags
+
+ h - Grouping identity
+
+ This flag indicates whether or not this frame belongs in a group
+ with other frames. If set, a group identifier byte is added to the
+ frame. Every frame with the same group identifier belongs to the
+ same group.
+
+ 0 Frame does not contain group information
+ 1 Frame contains group information
+
+
+ k - Compression
+
+ This flag indicates whether or not the frame is compressed.
+ A 'Data Length Indicator' byte MUST be included in the frame.
+
+ 0 Frame is not compressed.
+ 1 Frame is compressed using zlib [zlib] deflate method.
+ If set, this requires the 'Data Length Indicator' bit
+ to be set as well.
+
+
+ m - Encryption
+
+ This flag indicates whether or not the frame is encrypted. If set,
+ one byte indicating with which method it was encrypted will be
+ added to the frame. See description of the ENCR frame for more
+ information about encryption method registration. Encryption
+ should be done after compression. Whether or not setting this flag
+ requires the presence of a 'Data Length Indicator' depends on the
+ specific algorithm used.
+
+ 0 Frame is not encrypted.
+ 1 Frame is encrypted.
+
+ n - Unsynchronisation
+
+ This flag indicates whether or not unsynchronisation was applied
+ to this frame. See section 6 for details on unsynchronisation.
+ If this flag is set all data from the end of this header to the
+ end of this frame has been unsynchronised. Although desirable, the
+ presence of a 'Data Length Indicator' is not made mandatory by
+ unsynchronisation.
+
+ 0 Frame has not been unsynchronised.
+ 1 Frame has been unsyrchronised.
+
+ p - Data length indicator
+
+ This flag indicates that a data length indicator has been added to
+ the frame. The data length indicator is the value one would write
+ as the 'Frame length' if all of the frame format flags were
+ zeroed, represented as a 32 bit synchsafe integer.
+
+ 0 There is no Data Length Indicator.
+ 1 A data length Indicator has been added to the frame.
+
+
+5. Tag location
+
+ The default location of an ID3v2 tag is prepended to the audio so
+ that players can benefit from the information when the data is
+ streamed. It is however possible to append the tag, or make a
+ prepend/append combination. When deciding upon where an unembedded
+ tag should be located, the following order of preference SHOULD be
+ considered.
+
+ 1. Prepend the tag.
+
+ 2. Prepend a tag with all vital information and add a second tag at
+ the end of the file, before tags from other tagging systems. The
+ first tag is required to have a SEEK frame.
+
+ 3. Add a tag at the end of the file, before tags from other tagging
+ systems.
+
+ In case 2 and 3 the tag can simply be appended if no other known tags
+ are present. The suggested method to find ID3v2 tags are:
+
+ 1. Look for a prepended tag using the pattern found in section 3.1.
+
+ 2. If a SEEK frame was found, use its values to guide further
+ searching.
+
+ 3. Look for a tag footer, scanning from the back of the file.
+
+ For every new tag that is found, the old tag should be discarded
+ unless the update flag in the extended header (section 3.2) is set.
+
+
+6. Unsynchronisation
+
+ The only purpose of unsynchronisation is to make the ID3v2 tag as
+ compatible as possible with existing software and hardware. There is
+ no use in 'unsynchronising' tags if the file is only to be processed
+ only by ID3v2 aware software and hardware. Unsynchronisation is only
+ useful with tags in MPEG 1/2 layer I, II and III, MPEG 2.5 and AAC
+ files.
+
+
+6.1. The unsynchronisation scheme
+
+ Whenever a false synchronisation is found within the tag, one zeroed
+ byte is inserted after the first false synchronisation byte. The
+ format of synchronisations that should be altered by ID3 encoders is
+ as follows:
+
+ %11111111 111xxxxx
+
+ and should be replaced with:
+
+ %11111111 00000000 111xxxxx
+
+ This has the side effect that all $FF 00 combinations have to be
+ altered, so they will not be affected by the decoding process.
+ Therefore all the $FF 00 combinations have to be replaced with the
+ $FF 00 00 combination during the unsynchronisation.
+
+ To indicate usage of the unsynchronisation, the unsynchronisation
+ flag in the frame header should be set. This bit MUST be set if the
+ frame was altered by the unsynchronisation and SHOULD NOT be set if
+ unaltered. If all frames in the tag are unsynchronised the
+ unsynchronisation flag in the tag header SHOULD be set. It MUST NOT
+ be set if the tag has a frame which is not unsynchronised.
+
+ Assume the first byte of the audio to be $FF. The special case when
+ the last byte of the last frame is $FF and no padding nor footer is
+ used will then introduce a false synchronisation. This can be solved
+ by adding a footer, adding padding or unsynchronising the frame and
+ add $00 to the end of the frame data, thus adding more byte to the
+ frame size than a normal unsynchronisation would. Although not
+ preferred, it is allowed to apply the last method on all frames
+ ending with $FF.
+
+ It is preferred that the tag is either completely unsynchronised or
+ not unsynchronised at all. A completely unsynchronised tag has no
+ false synchonisations in it, as defined above, and does not end with
+ $FF. A completely non-unsynchronised tag contains no unsynchronised
+ frames, and thus the unsynchronisation flag in the header is cleared.
+
+ Do bear in mind, that if compression or encryption is used, the
+ unsynchronisation scheme MUST be applied afterwards. When decoding an
+ unsynchronised frame, the unsynchronisation scheme MUST be reversed
+ first, encryption and decompression afterwards.
+
+
+6.2. Synchsafe integers
+
+ In some parts of the tag it is inconvenient to use the
+ unsychronisation scheme because the size of unsynchronised data is
+ not known in advance, which is particularly problematic with size
+ descriptors. The solution in ID3v2 is to use synchsafe integers, in
+ which there can never be any false synchs. Synchsafe integers are
+ integers that keep its highest bit (bit 7) zeroed, making seven bits
+ out of eight available. Thus a 32 bit synchsafe integer can store 28
+ bits of information.
+
+ Example:
+
+ 255 (%11111111) encoded as a 16 bit synchsafe integer is 383
+ (%00000001 01111111).
+
+
+7. Copyright
+
+ Copyright (C) Martin Nilsson 2000. All Rights Reserved.
+
+ This document and translations of it may be copied and furnished to
+ others, and derivative works that comment on or otherwise explain it
+ or assist in its implementation may be prepared, copied, published
+ and distributed, in whole or in part, without restriction of any
+ kind, provided that a reference to this document is included on all
+ such copies and derivative works. However, this document itself may
+ not be modified in any way and reissued as the original document.
+
+ The limited permissions granted above are perpetual and will not be
+ revoked.
+
+ This document and the information contained herein is provided on an
+ 'AS IS' basis and THE AUTHORS DISCLAIMS ALL WARRANTIES, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF
+ THE INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED
+ WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+
+
+8. References
+
+ [ID3v2] Martin Nilsson, 'ID3v2 informal standard'.
+
+ <url:http://www.id3.org/id3v2.3.0.txt>
+
+ [ISO-639-2] ISO/FDIS 639-2.
+ 'Codes for the representation of names of languages, Part 2: Alpha-3
+ code.' Technical committee / subcommittee: TC 37 / SC 2
+
+ [ISO-3309] ISO 3309
+ 'Information Processing Systems--Data Communication High-Level Data
+ Link Control Procedure--Frame Structure', IS 3309, October 1984, 3rd
+ Edition.
+
+ [ISO-8859-1] ISO/IEC DIS 8859-1.
+ '8-bit single-byte coded graphic character sets, Part 1: Latin
+ alphabet No. 1.' Technical committee / subcommittee: JTC 1 / SC 2
+
+ [JFIF] 'JPEG File Interchange Format, version 1.02'
+
+ <url:http://www.w3.org/Graphics/JPEG/jfif.txt>
+
+ [KEYWORDS] S. Bradner, 'Key words for use in RFCs to Indicate
+ Requirement Levels', RFC 2119, March 1997.
+
+ <url:ftp://ftp.isi.edu/in-notes/rfc2119.txt>
+
+ [MPEG] ISO/IEC 11172-3:1993.
+ 'Coding of moving pictures and associated audio for digital storage
+ media at up to about 1,5 Mbit/s, Part 3: Audio.'
+ Technical committee / subcommittee: JTC 1 / SC 29
+ and
+ ISO/IEC 13818-3:1995
+ 'Generic coding of moving pictures and associated audio information,
+ Part 3: Audio.'
+ Technical committee / subcommittee: JTC 1 / SC 29
+ and
+ ISO/IEC DIS 13818-3
+ 'Generic coding of moving pictures and associated audio information,
+ Part 3: Audio (Revision of ISO/IEC 13818-3:1995)'
+
+ [PNG] 'Portable Network Graphics, version 1.0'
+
+ <url:http://www.w3.org/TR/REC-png-multi.html>
+
+ [UNICODE] The Unicode Consortium,
+ 'The Unicode Standard Version 3.0', ISBN 0-201-61633-5.
+
+ <url:http://www.unicode.org/unicode/standard/versions/Unicode3.0.htm>
+
+ [URL] T. Berners-Lee, L. Masinter & M. McCahill, 'Uniform Resource
+ Locators (URL)', RFC 1738, December 1994.
+
+ <url:ftp://ftp.isi.edu/in-notes/rfc1738.txt>
+
+ [UTF-8] F. Yergeau, 'UTF-8, a transformation format of ISO 10646',
+ RFC 2279, January 1998.
+
+ <url:ftp://ftp.isi.edu/in-notes/rfc2279.txt>
+
+ [UTF-16] F. Yergeau, 'UTF-16, an encoding of ISO 10646', RFC 2781,
+ February 2000.
+
+ <url:ftp://ftp.isi.edu/in-notes/rfc2781.txt>
+
+ [ZLIB] P. Deutsch, Aladdin Enterprises & J-L. Gailly, 'ZLIB
+ Compressed Data Format Specification version 3.3', RFC 1950,
+ May 1996.
+
+ <url:ftp://ftp.isi.edu/in-notes/rfc1950.txt>
+
+
+9. Author's Address
+
+ Written by
+
+ Martin Nilsson
+ Rydsvägen 246 C. 30
+ SE-584 34 Linköping
+ Sweden
+
+ Email: nilsson@id3.org
+
--- /dev/null
+/***************************************************************************
+ copyright : (C) 2002, 2003 by Scott Wheeler
+ email : wheeler@kde.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 "id3v2extendedheader.h"
+#include "id3v2synchdata.h"
+
+using namespace TagLib;
+using namespace ID3v2;
+
+class ExtendedHeader::ExtendedHeaderPrivate
+{
+public:
+ ExtendedHeaderPrivate() : size(0) {}
+
+ uint size;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// public methods
+////////////////////////////////////////////////////////////////////////////////
+
+ExtendedHeader::ExtendedHeader()
+{
+ d = new ExtendedHeaderPrivate();
+}
+
+ExtendedHeader::~ExtendedHeader()
+{
+ delete d;
+}
+
+TagLib::uint ExtendedHeader::size() const
+{
+ return d->size;
+}
+
+void ExtendedHeader::setData(const ByteVector &data)
+{
+ parse(data);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// protected members
+////////////////////////////////////////////////////////////////////////////////
+
+void ExtendedHeader::parse(const ByteVector &data)
+{
+ d->size = SynchData::toUInt(data.mid(0, 4)); // (structure 3.2 "Extended header size")
+}
--- /dev/null
+/***************************************************************************
+ copyright : (C) 2002, 2003 by Scott Wheeler
+ email : wheeler@kde.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_ID3V2EXTENDEDHEADER_H
+#define TAGLIB_ID3V2EXTENDEDHEADER_H
+
+#include <tbytevector.h>
+#include <taglib.h>
+
+namespace TagLib {
+
+ namespace ID3v2 {
+
+ //! ID3v2 extended header implementation
+
+ /*!
+ * This class implements ID3v2 extended headers. It attempts to follow,
+ * both semantically and programatically, the structure specified in
+ * the ID3v2 standard. The API is based on the properties of ID3v2 extended
+ * headers specified there. If any of the terms used in this documentation
+ * are unclear please check the specification in the linked section.
+ * (Structure, <a href="id3v2-structure.html#3.2">3.2</a>)
+ */
+
+ class ExtendedHeader
+ {
+ public:
+ /*!
+ * Constructs an empty ID3v2 extended header.
+ */
+ ExtendedHeader();
+
+ /*!
+ * Destroys the extended header.
+ */
+ virtual ~ExtendedHeader();
+
+ /*!
+ * Returns the size of the extended header. This is variable for the
+ * extended header.
+ */
+ uint size() const;
+
+ /*!
+ * Sets the data that will be used as the extended header. Since the
+ * length is not known before the extended header has been parsed, this
+ * should just be a pointer to the first byte of the extended header. It
+ * will determine the length internally and make that available through
+ * size().
+ */
+ void setData(const ByteVector &data);
+
+ protected:
+ /*!
+ * Called by setData() to parse the extended header data. It makes this
+ * information available through the public API.
+ */
+ void parse(const ByteVector &data);
+
+ private:
+ ExtendedHeader(const ExtendedHeader &);
+ ExtendedHeader &operator=(const ExtendedHeader &);
+
+ class ExtendedHeaderPrivate;
+ ExtendedHeaderPrivate *d;
+ };
+
+ }
+}
+#endif
--- /dev/null
+/***************************************************************************
+ copyright : (C) 2002, 2003 by Scott Wheeler
+ email : wheeler@kde.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 "id3v2footer.h"
+#include "id3v2header.h"
+
+using namespace TagLib;
+using namespace ID3v2;
+
+class Footer::FooterPrivate
+{
+public:
+ static const uint size = 10;
+};
+
+Footer::Footer()
+{
+
+}
+
+Footer::~Footer()
+{
+
+}
+
+const unsigned int Footer::size()
+{
+ return FooterPrivate::size;
+}
+
+ByteVector Footer::render(const Header *header) const
+{
+ ByteVector headerData = header->render();
+ headerData[0] = '3';
+ headerData[1] = 'D';
+ headerData[2] = 'I';
+ return headerData;
+}
--- /dev/null
+/***************************************************************************
+ copyright : (C) 2002, 2003 by Scott Wheeler
+ email : wheeler@kde.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_ID3V2FOOTER_H
+#define TAGLIB_ID3V2FOOTER_H
+
+#include <tbytevector.h>
+
+namespace TagLib {
+
+ namespace ID3v2 {
+
+ class Header;
+
+ //! ID3v2 footer implementation
+
+ /*!
+ * Per the ID3v2 specification, the tag's footer is just a copy of the
+ * information in the header. As such there is no API for reading the
+ * data from the header, it can just as easily be done from the header.
+ *
+ * In fact, at this point, TagLib does not even parse the footer since
+ * it is not useful internally. However, if the flag to include a footer
+ * has been set in the ID3v2::Tag, TagLib will render a footer.
+ */
+
+ class Footer
+ {
+ public:
+ /*!
+ * Constructs an empty ID3v2 footer.
+ */
+ Footer();
+ /*!
+ * Destroys the footer.
+ */
+ virtual ~Footer();
+
+ /*!
+ * Returns the size of the footer. Presently this is always 10 bytes.
+ */
+ static const unsigned int size();
+
+ /*!
+ * Renders the footer based on the data in \a header.
+ */
+ ByteVector render(const Header *header) const;
+
+ private:
+ Footer(const Footer &);
+ Footer &operator=(const Footer &);
+
+ class FooterPrivate;
+ FooterPrivate *d;
+ };
+
+ }
+}
+#endif
--- /dev/null
+/***************************************************************************
+ copyright : (C) 2002, 2003 by Scott Wheeler
+ email : wheeler@kde.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 <tdebug.h>
+
+#include "id3v2frame.h"
+#include "id3v2synchdata.h"
+
+using namespace TagLib;
+using namespace ID3v2;
+
+class Frame::FramePrivate
+{
+public:
+ FramePrivate() {
+ header = 0;
+ }
+
+ ~FramePrivate() {
+ delete header;
+ }
+
+ Frame::Header *header;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// static methods
+////////////////////////////////////////////////////////////////////////////////
+
+TagLib::uint Frame::headerSize()
+{
+ return Header::size();
+}
+
+ByteVector Frame::textDelimiter(String::Type t)
+{
+ ByteVector d = char(0);
+ if(t == String::UTF16 || t == String::UTF16BE)
+ d.append(char(0));
+ return d;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// public members
+////////////////////////////////////////////////////////////////////////////////
+
+Frame::~Frame()
+{
+ delete d;
+}
+
+ByteVector Frame::frameID() const
+{
+ if(d->header)
+ return d->header->frameID();
+ else
+ return ByteVector::null;
+}
+
+TagLib::uint Frame::size() const
+{
+ if(d->header)
+ return d->header->frameSize();
+ else
+ return 0;
+}
+
+void Frame::setData(const ByteVector &data)
+{
+ parse(data);
+}
+
+void Frame::setText(const String &)
+{
+
+}
+
+ByteVector Frame::render() const
+{
+ ByteVector fieldData = renderFields();
+ d->header->setFrameSize(fieldData.size());
+ ByteVector headerData = d->header->render();
+
+ return headerData + fieldData;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// protected members
+////////////////////////////////////////////////////////////////////////////////
+
+Frame::Frame(const ByteVector &data)
+{
+ d = new FramePrivate();
+ d->header = new Header(data);
+}
+
+Frame::Frame(Header *h)
+{
+ d = new FramePrivate();
+ d->header = h;
+}
+
+Frame::Header *Frame::header() const
+{
+ return d->header;
+}
+
+void Frame::setHeader(Header *h, bool deleteCurrent)
+{
+ if(deleteCurrent)
+ delete d->header;
+
+ d->header = h;
+}
+
+void Frame::parse(const ByteVector &data)
+{
+ if(d->header)
+ d->header->setData(data);
+ else
+ d->header = new Header(data);
+
+ // size() is the lenght of the field data
+ parseFields(data.mid(Header::size(), size()));
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Frame::Header class
+////////////////////////////////////////////////////////////////////////////////
+
+class Frame::Header::HeaderPrivate
+{
+public:
+ HeaderPrivate() : frameSize(0) {}
+
+ ByteVector frameID;
+ uint frameSize;
+ static const unsigned int size = 10;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// static members (Frame::Header)
+////////////////////////////////////////////////////////////////////////////////
+
+TagLib::uint Frame::Header::size()
+{
+ return HeaderPrivate::size;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// public members (Frame::Header)
+////////////////////////////////////////////////////////////////////////////////
+
+Frame::Header::Header(const ByteVector &data, bool synchSafeInts)
+{
+ d = new HeaderPrivate;
+ setData(data, synchSafeInts);
+}
+
+Frame::Header::~Header()
+{
+ delete d;
+}
+
+void Frame::Header::setData(const ByteVector &data, bool synchSafeInts)
+{
+ if(data.size() < 4) {
+ debug("You must at least specify a frame ID.");
+ return;
+ }
+
+ // set the frame ID -- the first four bytes
+
+ d->frameID = data.mid(0, 4);
+
+ // If the full header information was not passed in, do not continue to the
+ // steps to parse the frame size and flags.
+
+ if(data.size() < 10) {
+ d->frameSize = 0;
+ return;
+ }
+
+ // Set the size -- the frame size is the four bytes starting at byte four in
+ // the frame header (structure 4)
+
+ if(synchSafeInts)
+ d->frameSize = SynchData::toUInt(data.mid(4, 4));
+ else
+ d->frameSize = data.mid(4, 4).toUInt();
+
+ // read flags
+ // ...
+}
+
+ByteVector Frame::Header::frameID() const
+{
+ return d->frameID;
+}
+
+void Frame::Header::setFrameID(const ByteVector &id)
+{
+ d->frameID = id.mid(0, 4);
+}
+
+TagLib::uint Frame::Header::frameSize() const
+{
+ return d->frameSize;
+}
+
+void Frame::Header::setFrameSize(uint size)
+{
+ d->frameSize = size;
+}
+
+ByteVector Frame::Header::render() const
+{
+ ByteVector flags(2, char(0)); // just blank for the moment
+
+ ByteVector v = d->frameID + SynchData::fromUInt(d->frameSize) + flags;
+
+ return v;
+}
--- /dev/null
+/***************************************************************************
+ copyright : (C) 2002, 2003 by Scott Wheeler
+ email : wheeler@kde.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_ID3V2FRAME_H
+#define TAGLIB_ID3V2FRAME_H
+
+#include <tstring.h>
+#include <tbytevector.h>
+
+namespace TagLib {
+
+ namespace ID3v2 {
+
+ class FrameFactory;
+
+ //! ID3v2 frame implementation
+
+ /*!
+ * This class is the main ID3v2 frame implementation. In ID3v2, a tag is
+ * split between a collection of frames (which are in turn split into fields
+ * (Structure, <a href="id3v2-structure.html#4">4</a>)
+ * (<a href="id3v2-frames.html">Frames</a>). This class provides an API for
+ * gathering information about and modifying ID3v2 frames. Funtionallity
+ * specific to a given frame type is handed in one of the many subclasses.
+ */
+
+ class Frame
+ {
+ friend class FrameFactory;
+
+ public:
+ /*!
+ * Destroys this Frame instance.
+ */
+ virtual ~Frame();
+
+ /*!
+ * Returns the Frame ID (Structure, <a href="id3v2-structure.html#4">4</a>)
+ * (Frames, <a href="id3v2-frames.html#4">4</a>)
+ */
+ ByteVector frameID() const;
+
+ /*!
+ * Returns the size of the frame.
+ */
+ uint size() const;
+
+ /*!
+ * Returns the size of the frame header
+ */
+ static uint headerSize();
+
+ /*!
+ * Sets the data that will be used as the frame. Since the length is not
+ * known before the frame has been parsed, this should just be a pointer to
+ * the first byte of the frame. It will determine the length internally
+ * and make that available through size().
+ */
+ void setData(const ByteVector &data);
+
+ /*!
+ * Set the text of frame in the sanest way possible. This should only be
+ * reimplemented in frames where there is some logical mapping to text.
+ *
+ * \note If the frame type supports multiple text encodings, this will not
+ * change the text encoding of the frame; the string will be converted to
+ * that frame's encoding. Please use the specific APIs of the frame types
+ * to set the encoding if that is desired.
+ */
+ virtual void setText(const String &text);
+
+ /*!
+ * This returns the textual representation of the data in the frame.
+ * Subclasses must reimplement this method to provide a string
+ * representation of the frame's data.
+ */
+ virtual String toString() const = 0;
+
+ /*!
+ * Render the frame back to its binary format in a ByteVector.
+ */
+ ByteVector render() const;
+
+ /*!
+ * Returns the text delimiter that is used between fields for the string
+ * type \a t.
+ */
+ static ByteVector textDelimiter(String::Type t);
+
+ protected:
+ class Header;
+
+ /*!
+ * Constructs an ID3v2 frame using \a data to read the header information.
+ * All other processing of \a data should be handled in a subclass.
+ *
+ * \note This need not contain anything more than a frame ID, but
+ * \e must constain at least that.
+ */
+ explicit Frame(const ByteVector &data);
+
+ /*!
+ * This creates an Frame using the header \a h.
+ *
+ * The ownership of this header will be assigned to the frame and the
+ * header will be deleted when the frame is destroyed.
+ */
+ Frame(Header *h);
+
+ /*!
+ * Returns a pointer to the frame header.
+ */
+ Header *header() const;
+
+ /*!
+ * Sets the header to \a h. If \a deleteCurrent is true, this will free
+ * the memory of the current header.
+ *
+ * The ownership of this header will be assigned to the frame and the
+ * header will be deleted when the frame is destroyed.
+ */
+ void setHeader(Header *h, bool deleteCurrent = true);
+
+ /*!
+ * Called by setData() to parse the frame data. It makes this information
+ * available through the public API.
+ */
+ void parse(const ByteVector &data);
+
+ /*!
+ * Called by parse() to parse the field data. It makes this information
+ * available through the public API. This must be overridden by the
+ * subclasses.
+ */
+ virtual void parseFields(const ByteVector &data) = 0;
+
+ /*!
+ * Render the field data back to a binary format in a ByteVector. This
+ * must be overridden by subclasses.
+ */
+ virtual ByteVector renderFields() const = 0;
+
+ private:
+ Frame(const Frame &);
+ Frame &operator=(const Frame &);
+
+ class FramePrivate;
+ FramePrivate *d;
+ };
+
+ //! ID3v2 frame header implementation
+
+ /*!
+ * The ID3v2 Frame Header (Structure, <a href="id3v2-structure.html#4">4</a>)
+ *
+ * Every ID3v2::Frame has an associated header that gives some general
+ * properties of the frame and also makes it possible to identify the frame
+ * type.
+ *
+ * As such when reading an ID3v2 tag ID3v2::FrameFactory first creates the
+ * frame headers and then creates the appropriate Frame subclass based on
+ * the type and attaches the header.
+ */
+
+ class Frame::Header
+ {
+ public:
+ /*!
+ * Construct a Frame Header based on \a data. \a data must at least
+ * contain a 4 byte frame ID, and optionally can contain flag data and the
+ * frame size. i.e. Just the frame id -- "TALB" -- is a valid value.
+ */
+ explicit Header(const ByteVector &data, bool synchSafeInts = true);
+
+ /*!
+ * Destroys this Header instance.
+ */
+ virtual ~Header();
+
+ /*!
+ * Sets the data for the Header.
+ */
+ void setData(const ByteVector &data, bool synchSafeInts = true);
+
+ /*!
+ * Returns the Frame ID (Structure, <a href="id3v2-structure.html#4">4</a>)
+ * (Frames, <a href="id3v2-frames.html#4">4</a>)
+ */
+ ByteVector frameID() const;
+
+ /*!
+ * Sets the frame's ID to \a id. Only the first four bytes of \a id will
+ * be used.
+ *
+ * \warning This method should in general be avoided. It exists simply to
+ * provide a mechanism for transforming frames from a deprecated frame type
+ * to a newer one -- i.e. TYER to TDRC from ID3v2.3 to ID3v2.4.
+ */
+ void setFrameID(const ByteVector &id);
+
+ /*!
+ * Returns the size of the frame data portion, as set when setData() was
+ * called or set explicity via setFrameSize().
+ */
+ uint frameSize() const;
+
+ /*!
+ * Sets the size of the frame data portion.
+ */
+ void setFrameSize(uint size);
+
+ /*!
+ * Returns the size of the frame header in bytes. Currently this is
+ * always 10.
+ */
+ static uint size();
+
+ /*!
+ * Render the Header back to binary format in a ByteVector.
+ */
+ ByteVector render() const;
+
+ private:
+ Header(const Header &);
+ Header &operator=(const Header &);
+
+ class HeaderPrivate;
+ HeaderPrivate *d;
+ };
+
+ }
+}
+
+#endif
--- /dev/null
+/***************************************************************************
+ copyright : (C) 2002, 2003 by Scott Wheeler
+ email : wheeler@kde.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 <tdebug.h>
+
+#include "id3v2framefactory.h"
+
+#include "frames/unknownframe.h"
+#include "frames/textidentificationframe.h"
+#include "frames/commentsframe.h"
+
+using namespace TagLib;
+using namespace ID3v2;
+
+class FrameFactory::FrameFactoryPrivate
+{
+public:
+ FrameFactoryPrivate() :
+ defaultEncoding(String::Latin1),
+ useDefaultEncoding(false) {}
+
+ String::Type defaultEncoding;
+ bool useDefaultEncoding;
+};
+
+FrameFactory *FrameFactory::factory = 0;
+
+////////////////////////////////////////////////////////////////////////////////
+// public members
+////////////////////////////////////////////////////////////////////////////////
+
+FrameFactory *FrameFactory::instance()
+{
+ if(!factory)
+ factory = new FrameFactory;
+ return factory;
+}
+
+Frame *FrameFactory::createFrame(const ByteVector &data, bool synchSafeInts) const
+{
+ Frame::Header *header = new Frame::Header(data, synchSafeInts);
+
+ TagLib::ByteVector frameID = header->frameID();
+
+ // A quick sanity check -- make sure that the frameID is 4 uppercase Latin1
+ // characters. Also make sure that there is data in the frame.
+
+ if(!frameID.size() == 4 || header->frameSize() <= 0)
+ return 0;
+
+ for(ByteVector::ConstIterator it = frameID.begin(); it != frameID.end(); it++) {
+ if( (*it < 'A' || *it > 'Z') && (*it < '1' || *it > '9') ) {
+ delete header;
+ return 0;
+ }
+ }
+
+ if(!updateFrame(header)) {
+ delete header;
+ return 0;
+ }
+
+ frameID = header->frameID();
+
+ // This is where things get necissarily nasty. Here we determine which
+ // Frame subclass (or if none is found simply an Frame) based
+ // on the frame ID. Since there are a lot of possibilities, that means
+ // a lot of if blocks.
+
+ // Text Identification (frames 4.2)
+
+ if(frameID.startsWith("T") && frameID != "TXXX") {
+ TextIdentificationFrame *f = new TextIdentificationFrame(data, header);
+ if(d->useDefaultEncoding)
+ f->setTextEncoding(d->defaultEncoding);
+ return f;
+ }
+
+ // Comments (frames 4.10)
+
+ if(frameID == "COMM") {
+ CommentsFrame *f = new CommentsFrame(data, header);
+ if(d->useDefaultEncoding)
+ f->setTextEncoding(d->defaultEncoding);
+ return f;
+ }
+
+ return new UnknownFrame(data, header);
+}
+
+String::Type FrameFactory::defaultTextEncoding() const
+{
+ return d->defaultEncoding;
+}
+
+void FrameFactory::setDefaultTextEncoding(String::Type encoding)
+{
+ d->useDefaultEncoding = true;
+ d->defaultEncoding = encoding;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// protected members
+////////////////////////////////////////////////////////////////////////////////
+
+FrameFactory::FrameFactory()
+{
+ d = new FrameFactoryPrivate;
+}
+
+FrameFactory::~FrameFactory()
+{
+ delete d;
+}
+
+bool FrameFactory::updateFrame(Frame::Header *header) const
+{
+ TagLib::ByteVector frameID = header->frameID();
+ if(frameID == "EQUA" ||
+ frameID == "RVAD" ||
+ frameID == "TIME" ||
+ frameID == "TRDA" ||
+ frameID == "TSIZ")
+ {
+ debug("ID3v2.4 no longer supports the frame type " + String(frameID) +
+ ". It will be discarded from the tag.");
+ return false;
+ }
+
+ convertFrame("TDAT", "TRDC", header);
+ convertFrame("TORY", "TDOR", header);
+ convertFrame("TYER", "TRDC", header);
+
+ return true;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// private members
+////////////////////////////////////////////////////////////////////////////////
+
+void FrameFactory::convertFrame(const ByteVector &from, const ByteVector &to,
+ Frame::Header *header) const
+{
+ if(header->frameID() != from)
+ return;
+
+ // debug("ID3v2.4 no longer supports the frame type " + String(from) + " It has" +
+ // "been converted to the type " + String(to) + ".");
+
+ header->setFrameID(to);
+}
--- /dev/null
+/***************************************************************************
+ copyright : (C) 2002, 2003 by Scott Wheeler
+ email : wheeler@kde.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_ID3V2FRAMEFACTORY_H
+#define TAGLIB_ID3V2FRAMEFACTORY_H
+
+#include <tbytevector.h>
+#include "id3v2frame.h"
+
+namespace TagLib {
+
+ namespace ID3v2 {
+
+ //! A factory for creating ID3v2 frames
+
+ /*!
+ * This factory abstracts away the frame creation process and instantiates
+ * the appropriate ID3v2::Frame subclasses based on the contents of the
+ * data.
+ *
+ * Reimplementing this factory is the key to adding support for frame types
+ * not directly supported by TagLib to your application. To do so you would
+ * subclass this factory reimplement createFrame(). Then by setting your
+ * factory to be the default factory in ID3v2::Tag constructor or with
+ * MPEG::File::setID3v2FrameFactory() you can implement behavior that will
+ * allow for new ID3v2::Frame subclasses (also provided by you) to be used.
+ *
+ * This implements both <i>abstract factory</i> and <i>singleton</i> patterns
+ * of which more information is available on the web and in software design
+ * textbooks (Notably <i>Design Patters</i>).
+ */
+
+ class FrameFactory
+ {
+ public:
+ static FrameFactory *instance();
+ /*!
+ * Create a frame based on \a data. \a synchSafeInts should only be set
+ * false if we are parsing an old tag (v2.3 or older) that does not support
+ * synchsafe ints.
+ */
+ Frame *createFrame(const ByteVector &data, bool synchSafeInts = true) const;
+
+ /*!
+ * Returns the default text encoding for text frames. If setTextEncoding()
+ * has not been explicitly called this will only be used for new text
+ * frames. However, if this value has been set explicitly all frames will be
+ * converted to this type (unless it's explitly set differently for the
+ * individual frame) when being rendered.
+ *
+ * \see setDefaultTextEncoding()
+ */
+ String::Type defaultTextEncoding() const;
+
+ /*!
+ * Set the default text encoding for all text frames that are created to
+ * \a encoding. If no value is set the frames with either default to the
+ * encoding type that was parsed and new frames default to Latin1.
+ *
+ * \see defaultTextEncoding()
+ */
+ void setDefaultTextEncoding(String::Type encoding);
+
+ protected:
+ /*!
+ * Constructs a frame factory. Because this is a singleton this method is
+ * protected, but may be used for subclasses.
+ */
+ FrameFactory();
+
+ /*!
+ * Destroys the frame factory. In most cases this will never be called (as
+ * is typical of singletons).
+ */
+ virtual ~FrameFactory();
+
+ /*!
+ * This method checks for compliance to the current ID3v2 standard (2.4)
+ * and does nothing in the common case. However if a frame is found that
+ * is not compatible with the current standard, this method either updates
+ * the frame or indicates that it should be discarded.
+ *
+ * This method with return true (with or without changes to the frame) if
+ * this frame should be kept or false if it should be discarded.
+ *
+ * See the id3v2.4.0-changes.txt document for further information.
+ */
+ virtual bool updateFrame(Frame::Header *header) const;
+
+ private:
+ FrameFactory(const FrameFactory &);
+ FrameFactory &operator=(const FrameFactory &);
+
+ /*!
+ * This method is used internally to convert a frame from ID \a from to ID
+ * \a to. If the frame matches the \a from pattern and converts the frame
+ * ID in the \a header or simply does nothing if the frame ID does not match.
+ */
+ void convertFrame(const ByteVector &from, const ByteVector &to,
+ Frame::Header *header) const;
+
+ static FrameFactory *factory;
+
+ class FrameFactoryPrivate;
+ FrameFactoryPrivate *d;
+ };
+
+ }
+}
+
+#endif
--- /dev/null
+/***************************************************************************
+ copyright : (C) 2002, 2003 by Scott Wheeler
+ email : wheeler@kde.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 <iostream>
+#include <bitset>
+
+#include <tstring.h>
+#include <tdebug.h>
+
+#include "id3v2header.h"
+#include "id3v2footer.h"
+#include "id3v2synchdata.h"
+
+using namespace TagLib;
+using namespace ID3v2;
+
+class Header::HeaderPrivate
+{
+public:
+ HeaderPrivate() : majorVersion(0),
+ revisionNumber(0),
+ unsynchronisation(false),
+ extendedHeader(false),
+ experimentalIndicator(false),
+ footerPresent(false),
+ tagSize(0) {}
+
+ ~HeaderPrivate() {}
+
+ uint majorVersion;
+ uint revisionNumber;
+
+ bool unsynchronisation;
+ bool extendedHeader;
+ bool experimentalIndicator;
+ bool footerPresent;
+
+ uint tagSize;
+
+ static const uint size = 10;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// static members
+////////////////////////////////////////////////////////////////////////////////
+
+TagLib::uint Header::size()
+{
+ return HeaderPrivate::size;
+}
+
+ByteVector Header::fileIdentifier()
+{
+ return ByteVector::fromCString("ID3");
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// public members
+////////////////////////////////////////////////////////////////////////////////
+
+Header::Header()
+{
+ d = new HeaderPrivate();
+}
+
+Header::Header(const ByteVector &data)
+{
+ d = new HeaderPrivate();
+ parse(data);
+}
+
+Header::~Header()
+{
+ delete d;
+}
+
+TagLib::uint Header::majorVersion() const
+{
+ return d->majorVersion;
+}
+
+TagLib::uint Header::revisionNumber() const
+{
+ return d->revisionNumber;
+}
+
+bool Header::unsynchronisation() const
+{
+ return d->unsynchronisation;
+}
+
+bool Header::extendedHeader() const
+{
+ return d->extendedHeader;
+}
+
+bool Header::experimentalIndicator() const
+{
+ return d->experimentalIndicator;
+}
+
+bool Header::footerPresent() const
+{
+ return d->footerPresent;
+}
+
+TagLib::uint Header::tagSize() const
+{
+ return d->tagSize;
+}
+
+TagLib::uint Header::completeTagSize() const
+{
+ if(d->footerPresent)
+ return d->tagSize + d->size + Footer::size();
+ else
+ return d->tagSize + d->size;
+}
+
+void Header::setTagSize(uint s)
+{
+ d->tagSize = s;
+}
+
+void Header::setData(const ByteVector &data)
+{
+ parse(data);
+}
+
+ByteVector Header::render() const
+{
+ ByteVector v;
+
+ // add the file identifier -- "ID3"
+ v.append(fileIdentifier());
+
+ // add the version number -- we always render a 2.4.0 tag regardless of what
+ // the tag originally was.
+
+ v.append(char(4));
+ v.append(char(0));
+
+ // render and add the flags
+ std::bitset<8> flags;
+
+ flags[7] = d->unsynchronisation;
+ flags[6] = d->extendedHeader;
+ flags[5] = d->experimentalIndicator;
+ flags[4] = d->footerPresent;
+
+ v.append(char(flags.to_ulong()));
+
+ // add the size
+ v.append(SynchData::fromUInt(d->tagSize));
+
+ return v;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// protected members
+////////////////////////////////////////////////////////////////////////////////
+
+void Header::parse(const ByteVector &data)
+{
+ if(data.size() < size())
+ return;
+
+
+ // do some sanity checking -- even in ID3v2.3.0 and less the tag size is a
+ // synch-safe integer, so all bytes must be less than 128. If this is not
+ // true then this is an invalid tag.
+
+ // note that we're doing things a little out of order here -- the size is
+ // later in the bytestream than the version
+
+ ByteVector sizeData = data.mid(6, 4);
+
+ if(sizeData.size() != 4) {
+ d->tagSize = 0;
+ debug("TagLib::ID3v2::Header::parse() - The tag size as read was 0 bytes!");
+ return;
+ }
+
+ for(ByteVector::Iterator it = sizeData.begin(); it != sizeData.end(); it++) {
+ if(uchar(*it) >= 128) {
+ d->tagSize = 0;
+ debug("TagLib::ID3v2::Header::parse() - One of the size bytes in the id3v2 header was greater than the allowed 128.");
+ return;
+ }
+ }
+
+ // The first three bytes, data[0..2], are the File Identifier, "ID3". (structure 3.1 "file identifier")
+
+ // Read the version number from the fourth and fifth bytes.
+ d->majorVersion = data[3]; // (structure 3.1 "major version")
+ d->revisionNumber = data[4]; // (structure 3.1 "revision number")
+
+ // Read the flags, the first four bits of the sixth byte.
+ std::bitset<8> flags(data[5]);
+
+ d->unsynchronisation = flags[7]; // (structure 3.1.a)
+ d->extendedHeader = flags[6]; // (structure 3.1.b)
+ d->experimentalIndicator = flags[5]; // (structure 3.1.c)
+ d->footerPresent = flags[4]; // (structure 3.1.d)
+
+ // Get the size from the remaining four bytes (read above)
+
+ d->tagSize = SynchData::toUInt(sizeData); // (structure 3.1 "size")
+}
--- /dev/null
+/***************************************************************************
+ copyright : (C) 2002, 2003 by Scott Wheeler
+ email : wheeler@kde.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_ID3V2HEADER_H
+#define TAGLIB_ID3V2HEADER_H
+
+#include <tbytevector.h>
+
+namespace TagLib {
+
+ namespace ID3v2 {
+
+ //! An implementation of ID3v2 headers
+
+ /*!
+ * This class implements ID3v2 headers. It attempts to follow, both
+ * semantically and programatically, the structure specified in
+ * the ID3v2 standard. The API is based on the properties of ID3v2 headers
+ * specified there. If any of the terms used in this documentation are
+ * unclear please check the specification in the linked section.
+ * (Structure, <a href="id3v2-structure.html#3.1">3.1</a>)
+ */
+
+ class Header
+ {
+ public:
+ /*!
+ * Constructs an empty ID3v2 header.
+ */
+ Header();
+
+ /*!
+ * Constructs an ID3v2 header based on \a data. parse() is called
+ * immediately.
+ */
+ Header(const ByteVector &data);
+
+ /*!
+ * Destroys the header.
+ */
+ virtual ~Header();
+
+ /*!
+ * Returns the major version number. (Note: This is the 4, not the 2 in
+ * ID3v2.4.0. The 2 is implied.)
+ */
+ uint majorVersion() const;
+
+ /*!
+ * Returns the revision number. (Note: This is the 0, not the 4 in
+ * ID3v2.4.0. The 2 is implied.)
+ */
+ uint revisionNumber() const;
+
+ /*!
+ * Returns true if unsynchronisation has been applied to all frames.
+ */
+ bool unsynchronisation() const;
+
+ /*!
+ * Returns true if an extended header is present in the tag.
+ */
+ bool extendedHeader() const;
+
+ /*!
+ * Returns true if the experimental indicator flag is set.
+ */
+ bool experimentalIndicator() const;
+
+ /*!
+ * Returns true if a footer is present in the tag.
+ */
+ bool footerPresent() const;
+ /*!
+ * Returns the tag size in bytes. This is the size of the frame content.
+ * The size of the \e entire tag will be this plus the header size (10
+ * bytes) and, if present, the footer size (potentially another 10 bytes).
+ *
+ * \note This is the value as read from the header to which TagLib attempts
+ * to provide an API to; it was not a design decision on the part of TagLib
+ * to not include the mentioned portions of the tag in the \e size.
+ *
+ * \see completeTagSize()
+ */
+ uint tagSize() const;
+
+ /*!
+ * Returns the tag size, including the header and, if present, the footer
+ * size.
+ *
+ * \see tagSize()
+ */
+ uint completeTagSize() const;
+
+ /*!
+ * Set the tag size to \a s.
+ * \see tagSize()
+ */
+ void setTagSize(uint s);
+
+ /*!
+ * Returns the size of the header. Presently this is always 10 bytes.
+ */
+ static uint size();
+
+ /*!
+ * Returns the string used to identify and ID3v2 tag inside of a file.
+ * Presently this is always "ID3".
+ */
+ static ByteVector fileIdentifier();
+
+ /*!
+ * Sets the data that will be used as the extended header. 10 bytes,
+ * starting from \a data will be used.
+ */
+ void setData(const ByteVector &data);
+
+ /*!
+ * Renders the Header back to binary format.
+ */
+ ByteVector render() const;
+
+ protected:
+ /*!
+ * Called by setData() to parse the header data. It makes this information
+ * available through the public API.
+ */
+ void parse(const ByteVector &data);
+
+ private:
+ Header(const Header &);
+ Header &operator=(const Header &);
+
+ class HeaderPrivate;
+ HeaderPrivate *d;
+ };
+
+ }
+}
+
+#endif
--- /dev/null
+/***************************************************************************
+ copyright : (C) 2002, 2003 by Scott Wheeler
+ email : wheeler@kde.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 <iostream>
+
+#include "id3v2synchdata.h"
+
+using namespace TagLib;
+using namespace ID3v2;
+
+TagLib::uint SynchData::toUInt(const ByteVector &data)
+{
+ uint sum = 0;
+ int last = data.size() > 4 ? 3 : data.size() - 1;
+
+ for(int i = 0; i <= last; i++)
+ sum |= (data[i] & 0x7f) << ((last - i) * 7);
+
+ return sum;
+}
+
+ByteVector SynchData::fromUInt(uint value)
+{
+ ByteVector v(4, 0);
+
+ for(int i = 0; i < 4; i++)
+ v[i] = uchar(value >> ((3 - i) * 7) & 0x7f);
+
+ return v;
+}
--- /dev/null
+/***************************************************************************
+ copyright : (C) 2002, 2003 by Scott Wheeler
+ email : wheeler@kde.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_ID3V2SYNCHDATA_H
+#define TAGLIB_ID3V2SYNCHDATA_H
+
+#include <tbytevector.h>
+#include <taglib.h>
+
+namespace TagLib {
+
+ namespace ID3v2 {
+
+ //! A few functions for ID3v2 synch safe integer conversion
+
+ /*!
+ * In the ID3v2.4 standard most integer values are encoded as "synch safe"
+ * integers which are encoded in such a way that they will not give false
+ * MPEG syncs and confuse MPEG decoders. This namespace provides some
+ * methods for converting to and from these values to ByteVectors for
+ * things rendering and parsing ID3v2 data.
+ */
+
+ namespace SynchData
+ {
+ /*!
+ * This returns the unsigned integer value of \a data where \a data is a
+ * ByteVector that contains a \e synchsafe integer (Structure,
+ * <a href="id3v2-structure.html#6.2">6.2</a>). The default \a length of
+ * 4 is used if another value is not specified.
+ */
+ uint toUInt(const ByteVector &data);
+
+ /*!
+ * Returns a 4 byte (32 bit) synchsafe integer based on \a value.
+ */
+ ByteVector fromUInt(uint value);
+ }
+
+ }
+}
+
+#endif
--- /dev/null
+/***************************************************************************
+ copyright : (C) 2002, 2003 by Scott Wheeler
+ email : wheeler@kde.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 <tfile.h>
+#include <tdebug.h>
+
+#include "id3v2tag.h"
+#include "id3v2header.h"
+#include "id3v2extendedheader.h"
+#include "id3v2footer.h"
+
+#include "id3v1genres.h"
+
+#include "frames/textidentificationframe.h"
+#include "frames/commentsframe.h"
+
+using namespace TagLib;
+using namespace ID3v2;
+
+class ID3v2::Tag::TagPrivate
+{
+public:
+ TagPrivate() : file(0), tagOffset(-1), extendedHeader(0), footer(0), paddingSize(0)
+ {
+ frameList.setAutoDelete(true);
+ }
+ ~TagPrivate()
+ {
+ delete extendedHeader;
+ delete footer;
+ }
+
+ File *file;
+ long tagOffset;
+ const FrameFactory *factory;
+
+ Header header;
+ ExtendedHeader *extendedHeader;
+ Footer *footer;
+
+ int paddingSize;
+
+ FrameListMap frameListMap;
+ FrameList frameList;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// public members
+////////////////////////////////////////////////////////////////////////////////
+
+ID3v2::Tag::Tag() : TagLib::Tag()
+{
+ d = new TagPrivate;
+ d->factory = FrameFactory::instance();
+}
+
+ID3v2::Tag::Tag(File *file, long tagOffset, const FrameFactory *factory) :
+ TagLib::Tag()
+{
+ d = new TagPrivate;
+
+ d->file = file;
+ d->tagOffset = tagOffset;
+ d->factory = factory;
+
+ read();
+}
+
+ID3v2::Tag::~Tag()
+{
+ delete d;
+}
+
+
+String ID3v2::Tag::title() const
+{
+ if(!d->frameListMap["TIT2"].isEmpty())
+ return d->frameListMap["TIT2"].front()->toString();
+ return String::null;
+}
+
+String ID3v2::Tag::artist() const
+{
+ if(!d->frameListMap["TPE1"].isEmpty())
+ return d->frameListMap["TPE1"].front()->toString();
+ return String::null;
+}
+
+String ID3v2::Tag::album() const
+{
+ if(!d->frameListMap["TALB"].isEmpty())
+ return d->frameListMap["TALB"].front()->toString();
+ return String::null;
+}
+
+String ID3v2::Tag::comment() const
+{
+ if(!d->frameListMap["COMM"].isEmpty())
+ return d->frameListMap["COMM"].front()->toString();
+ return String::null;
+}
+
+String ID3v2::Tag::genre() const
+{
+ if(!d->frameListMap["TCON"].isEmpty()) {
+ String s = d->frameListMap["TCON"].front()->toString();
+
+ // ID3v2 "content type" can contain a ID3v1 genre number in parenthesis at
+ // the beginning of the field. If this is all that the field contains, do a
+ // translation from that number to the name and return that. If there is a
+ // string folloing the ID3v1 genre number, that is considered to be
+ // authoritative and we return that instead. Or finally, the field may
+ // simply be free text, in which case we just return the value.
+
+ int closing = s.find(")");
+ if(s.substr(0, 1) == "(" && closing > 0) {
+ if(closing == int(s.size() - 1))
+ return ID3v1::genre(s.substr(1, s.size() - 2).toInt());
+ else
+ return s.substr(closing + 1);
+ }
+ return s;
+ }
+ return String::null;
+}
+
+TagLib::uint ID3v2::Tag::year() const
+{
+ if(!d->frameListMap["TRDC"].isEmpty())
+ return d->frameListMap["TRDC"].front()->toString().substr(0, 4).toInt();
+ return 0;
+}
+
+TagLib::uint ID3v2::Tag::track() const
+{
+ if(!d->frameListMap["TRCK"].isEmpty())
+ return d->frameListMap["TRCK"].front()->toString().toInt();
+ return 0;
+}
+
+void ID3v2::Tag::setTitle(const String &s)
+{
+ setTextFrame("TIT2", s);
+}
+
+void ID3v2::Tag::setArtist(const String &s)
+{
+ setTextFrame("TPE1", s);
+}
+
+void ID3v2::Tag::setAlbum(const String &s)
+{
+ setTextFrame("TALB", s);
+}
+
+void ID3v2::Tag::setComment(const String &s)
+{
+ if(s.isEmpty()) {
+ removeFrames("COMM");
+ return;
+ }
+
+ if(!d->frameListMap["COMM"].isEmpty())
+ d->frameListMap["COMM"].front()->setText(s);
+ else {
+ CommentsFrame *f = new CommentsFrame(d->factory->defaultTextEncoding());
+ addFrame(f);
+ f->setText(s);
+ }
+}
+
+void ID3v2::Tag::setGenre(const String &s)
+{
+ if(s.isEmpty()) {
+ removeFrames("TCON");
+ return;
+ }
+
+ int index = ID3v1::genreIndex(s);
+
+ if(index != 255)
+ setTextFrame("TCON", "(" + String::number(index) + ")");
+ else
+ setTextFrame("TCON", s);
+}
+
+void ID3v2::Tag::setYear(uint i)
+{
+ if(i <= 0) {
+ removeFrames("TRDC");
+ return;
+ }
+ setTextFrame("TRDC", String::number(i));
+}
+
+void ID3v2::Tag::setTrack(uint i)
+{
+ if(i <= 0) {
+ removeFrames("TRCK");
+ return;
+ }
+ setTextFrame("TRCK", String::number(i));
+}
+
+bool ID3v2::Tag::isEmpty() const
+{
+ return d->frameList.isEmpty();
+}
+
+Header *ID3v2::Tag::header() const
+{
+ return &(d->header);
+}
+
+ExtendedHeader *ID3v2::Tag::extendedHeader() const
+{
+ return d->extendedHeader;
+}
+
+Footer *ID3v2::Tag::footer() const
+{
+ return d->footer;
+}
+
+const FrameListMap &ID3v2::Tag::frameListMap() const
+{
+ return d->frameListMap;
+}
+
+const FrameList &ID3v2::Tag::frameList() const
+{
+ return d->frameList;
+}
+
+void ID3v2::Tag::addFrame(Frame *frame)
+{
+ d->frameList.append(frame);
+ d->frameListMap[frame->frameID()].append(frame);
+}
+
+void ID3v2::Tag::removeFrame(Frame *frame, bool del)
+{
+ // remove the frame from the frame list
+ FrameList::Iterator it = d->frameList.find(frame);
+ d->frameList.erase(it);
+
+ // ...and from the frame list map
+ it = d->frameListMap[frame->frameID()].find(frame);
+ d->frameListMap[frame->frameID()].erase(it);
+
+ // ...and delete as desired
+ if(del)
+ delete *it;
+}
+
+void ID3v2::Tag::removeFrames(const ByteVector &id)
+{
+ FrameList l = d->frameListMap[id];
+ for(FrameList::Iterator it = l.begin(); it != l.end(); ++it)
+ removeFrame(*it, true);
+}
+
+ByteVector ID3v2::Tag::render() const
+{
+ // We need to render the "tag data" first so that we have to correct size to
+ // render in the tag's header. The "tag data" -- everything that is included
+ // in ID3v2::Header::tagSize() -- includes the extended header, frames and
+ // padding, but does not include the tag's header or footer.
+
+ ByteVector tagData;
+
+ // TODO: Render the extended header.
+
+ // Loop through the frames rendering them and adding them to the tagData.
+
+ for(FrameList::Iterator it = d->frameList.begin(); it != d->frameList.end(); it++)
+ tagData.append((*it)->render());
+
+ // Compute the amount of padding, and append that to tagData.
+
+ uint paddingSize = 0;
+ uint originalSize = d->header.tagSize();
+
+ if(tagData.size() < originalSize)
+ paddingSize = originalSize - tagData.size();
+ else
+ paddingSize = 1024;
+
+ tagData.append(ByteVector(paddingSize, char(0)));
+
+ // Set the tag size.
+ d->header.setTagSize(tagData.size());
+
+ // TODO: This should eventually include d->footer->render().
+ return d->header.render() + tagData;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// protected members
+////////////////////////////////////////////////////////////////////////////////
+
+void ID3v2::Tag::read()
+{
+ if(d->file && d->file->isOpen()) {
+
+ d->file->seek(d->tagOffset);
+ d->header.setData(d->file->readBlock(Header::size()));
+
+ // if the tag size is 0, then this is an invalid tag (tags must contain at
+ // least one frame)
+
+ if(d->header.tagSize() == 0)
+ return;
+
+ parse(d->file->readBlock(d->header.tagSize()));
+ }
+}
+
+void ID3v2::Tag::parse(const ByteVector &data)
+{
+ uint frameDataOffset = 0;
+ uint frameDataLength = data.size();
+
+ // check for extended header
+
+ if(d->header.extendedHeader()) {
+ if(!d->extendedHeader)
+ d->extendedHeader = new ExtendedHeader;
+ d->extendedHeader->setData(data);
+ if(d->extendedHeader->size() <= data.size()) {
+ frameDataOffset += d->extendedHeader->size();
+ frameDataLength -= d->extendedHeader->size();
+ }
+ }
+
+ // check for footer -- we don't actually need to parse it, as it *must*
+ // contain the same data as the header, but we do need to account for its
+ // size.
+
+ if(d->header.footerPresent() && Footer::size() <= frameDataLength)
+ frameDataLength -= Footer::size();
+
+ // parse frames
+
+ uint frameDataPosition = 0;
+
+ // Make sure that there is at least enough room in the remaining frame data for
+ // a frame header.
+
+ while(frameDataPosition < frameDataLength - Frame::headerSize()) {
+
+ // If the next data is position is 0, assume that we've hit the padding
+ // portion of the frame data.
+
+ if(data.at(frameDataPosition) == 0) {
+
+ if(d->header.footerPresent())
+ debug("Padding *and* a footer found. This is not allowed by the spec.");
+
+ d->paddingSize = frameDataLength - frameDataPosition;
+ return;
+ }
+
+ bool synchSafeInts = d->header.majorVersion() >= 4;
+
+ Frame *frame = d->factory->createFrame(data.mid(frameDataOffset + frameDataPosition),
+ synchSafeInts);
+
+ if(!frame)
+ return;
+
+ // Checks to make sure that frame parsed correctly.
+
+ if(frame->size() <= 0) {
+ delete frame;
+ return;
+ }
+
+ frameDataPosition += frame->size() + Frame::headerSize();
+ addFrame(frame);
+ }
+}
+
+void ID3v2::Tag::setTextFrame(const ByteVector &id, const String &value)
+{
+ if(value.isEmpty()) {
+ removeFrames(id);
+ return;
+ }
+
+ if(!d->frameListMap[id].isEmpty())
+ d->frameListMap[id].front()->setText(value);
+ else {
+ const String::Type encoding = d->factory->defaultTextEncoding();
+ TextIdentificationFrame *f = new TextIdentificationFrame(id, encoding);
+ addFrame(f);
+ f->setText(value);
+ }
+}
--- /dev/null
+/***************************************************************************
+ copyright : (C) 2002, 2003 by Scott Wheeler
+ email : wheeler@kde.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_ID3V2TAG_H
+#define TAGLIB_ID3V2TAG_H
+
+#include <tag.h>
+#include <tbytevector.h>
+#include <tstring.h>
+#include <tlist.h>
+#include <tmap.h>
+
+#include "id3v2framefactory.h"
+
+namespace TagLib {
+
+ class File;
+
+ //! An ID3v2 implementation
+
+ /*!
+ * This is a relatively complete and flexible framework for working with ID3v2
+ * tags.
+ *
+ * \see ID3v2::Tag
+ */
+
+ namespace ID3v2 {
+
+ class Header;
+ class ExtendedHeader;
+ class Footer;
+
+ typedef List<Frame *> FrameList;
+ typedef Map<ByteVector, FrameList> FrameListMap;
+
+ //! The main class in the ID3v2 implementation
+
+ /*!
+ * This is the main class in the ID3v2 implementation. It serves two
+ * functions. This first, as is obvious from the public API, is to provide a
+ * container for the other ID3v2 related classes. In addition, through the
+ * read() and parse() protected methods, it provides the most basic level of
+ * parsing. In these methods the ID3v2 tag is extracted from the file and
+ * split into data components.
+ *
+ * ID3v2 tags have several parts, TagLib attempts to provide an interface
+ * for them all. header(), footer() and extendedHeader() corespond to those
+ * data structures in the ID3v2 standard and the APIs for the classes that
+ * they return attempt to reflect this.
+ *
+ * Also ID3v2 tags are built up from a list of frames, which are in turn
+ * have a header and a list of fields. TagLib provides two ways of accessing
+ * the list of frames that are in a given ID3v2 tag. The first is simply
+ * via the frameList() method. This is just a list of pointers to the frames.
+ * The second is a map from the frame type -- i.e. "COMM" for comments -- and
+ * a list of frames of that type. (In some cases ID3v2 allows for multiple
+ * frames of the same type, hence this being a map to a list rather than just
+ * a map to an individual frame.)
+ *
+ * More information on the structure of frames can be found in the ID3v2::Frame
+ * class.
+ *
+ * read() and parse() pass binary data to the other ID3v2 class structures,
+ * they do not handle parsing of flags or fields, for instace. Those are
+ * handled by similar functions within those classes.
+ *
+ * \note All pointers to data structures within the tag will become invalid
+ * when the tag is destroyed.
+ *
+ * \warning Dealing with the nasty details of ID3v2 is not for the faint of
+ * heart and should not be done without much meditation on the spec. It's
+ * rather long, but if you're planning on messing with this class and others
+ * that deal with the details of ID3v2 (rather than the nice, safe, abstract
+ * TagLib::Tag and friends), it's worth your time to familiarize yourself
+ * with said spec (which is distrubuted with the TagLib sources). TagLib
+ * tries to do most of the work, but with a little luck, you can still
+ * convince it to generate invalid ID3v2 tags. The APIs for ID3v2 assume a
+ * working knowledge of ID3v2 structure. You're been warned.
+ */
+
+ class Tag : public TagLib::Tag
+ {
+ public:
+ /*!
+ * Constructs an empty ID3v2 tag.
+ *
+ * \note You must create at least one frame for this tag to be valid.
+ */
+ Tag();
+
+ /*!
+ * Constructs an ID3v2 tag read from \a file starting at \a tagOffset.
+ * \a factory specifies which FrameFactory will be used for the
+ * construction of new frames.
+ *
+ * \note You should be able to ignore the \a factory parameter in almost
+ * all situations. You would want to specify your own FrameFactory
+ * subclass in the case that you are extending TagLib to support additional
+ * frame types, which would be incorperated into your factory.
+ *
+ * \see FrameFactory
+ */
+ Tag(File *file, long tagOffset,
+ const FrameFactory *factory = FrameFactory::instance());
+
+ /*!
+ * Destroys this Tag instance.
+ */
+ virtual ~Tag();
+
+ // 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);
+
+ virtual bool isEmpty() const;
+
+ /*!
+ * Returns a pointer to the tag's header.
+ */
+ Header *header() const;
+
+ /*!
+ * Returns a pointer to the tag's extended header or null if there is no
+ * extended header.
+ */
+ ExtendedHeader *extendedHeader() const;
+
+ /*!
+ * Returns a pointer to the tag's footer or null if there is no footer.
+ *
+ * \deprecated I don't see any reason to keep this around since there's
+ * nothing useful to be retrieved from the footer, but well, again, I'm
+ * prone to change my mind, so this gets to stay around until near a
+ * release.
+ */
+ Footer *footer() const;
+
+ /*!
+ * Returns a pointer to the frame list map. This is an FrameListMap of
+ * all of the frames in the tag.
+ *
+ * \warning You should not modify this data structure directly, instead
+ * use addFrame() and removeFrame().
+ */
+ const FrameListMap &frameListMap() const;
+
+ /*!
+ * Returns a pointer to the frame list. This is an FrameList of all of
+ * the frames in the tag in the order that they were parsed.
+ *
+ * \warning You should not modify this data structure directly, instead
+ * use addFrame() and removeFrame().
+ */
+ const FrameList &frameList() const;
+
+ /*!
+ * Add a frame to the tag. At this point the tag takes ownership of
+ * the frame and will handle freeing its memory.
+ */
+ void addFrame(Frame *frame);
+
+ /*!
+ * Remove a frame from the tag. If \a del is true the frame's memory
+ * will be freed; if it is false, it must be deleted by the user.
+ */
+ void removeFrame(Frame *frame, bool del = true);
+
+ /*!
+ * Remove all frames of type \a id from the tag and free their memory.
+ */
+ void removeFrames(const ByteVector &id);
+
+ /*!
+ * Render the tag back to binary data, suitable to be written to disk.
+ */
+ ByteVector render() const;
+
+ protected:
+ /*!
+ * Reads data from the file specified in the constructor. It does basic
+ * parsing of the data in the largest chunks. It partitions the tag into
+ * the Header, the body of the tag (which contains the ExtendedHeader and
+ * frames) and Footer.
+ */
+ void read();
+
+ /*!
+ * This is called by read to parse the body of the tag. It determines if an
+ * extended header exists and adds frames to the FrameListMap.
+ */
+ void parse(const ByteVector &data);
+
+ /*!
+ * Sets the value of the text frame with the Frame ID \a id to \a value.
+ * If the frame does not exist, it is created.
+ */
+ void setTextFrame(const ByteVector &id, const String &value);
+
+ private:
+ Tag(const Tag &);
+ Tag &operator=(const Tag &);
+
+ class TagPrivate;
+ TagPrivate *d;
+ };
+
+ }
+}
+
+#endif
--- /dev/null
+/***************************************************************************
+ copyright : (C) 2002, 2003 by Scott Wheeler
+ email : wheeler@kde.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 <id3v2tag.h>
+#include <id3v2header.h>
+#include <id3v1tag.h>
+#include <tdebug.h>
+
+#include <bitset>
+
+#include "mpegfile.h"
+#include "mpegheader.h"
+
+using namespace TagLib;
+
+namespace TagLib {
+ /*!
+ * A union of the ID3v2 and ID3v1 tags.
+ */
+ class MPEGTag : public TagLib::Tag
+ {
+ public:
+ MPEGTag(MPEG::File *f) : TagLib::Tag(), file(f) {}
+
+ virtual String title() const {
+ if(file->ID3v2Tag() && !file->ID3v2Tag()->title().isEmpty())
+ return file->ID3v2Tag()->title();
+
+ if(file->ID3v1Tag())
+ return file->ID3v1Tag()->title();
+
+ return String::null;
+ }
+
+ virtual String artist() const {
+ if(file->ID3v2Tag() && !file->ID3v2Tag()->artist().isEmpty())
+ return file->ID3v2Tag()->artist();
+
+ if(file->ID3v1Tag())
+ return file->ID3v1Tag()->artist();
+
+ return String::null;
+ }
+
+ virtual String album() const {
+ if(file->ID3v2Tag() && !file->ID3v2Tag()->album().isEmpty())
+ return file->ID3v2Tag()->album();
+
+ if(file->ID3v1Tag())
+ return file->ID3v1Tag()->album();
+
+ return String::null;
+ }
+
+ virtual String comment() const {
+ if(file->ID3v2Tag() && !file->ID3v2Tag()->comment().isEmpty())
+ return file->ID3v2Tag()->comment();
+
+ if(file->ID3v1Tag())
+ return file->ID3v1Tag()->comment();
+
+ return String::null;
+ }
+
+ virtual String genre() const {
+ if(file->ID3v2Tag() && !file->ID3v2Tag()->genre().isEmpty())
+ return file->ID3v2Tag()->genre();
+
+ if(file->ID3v1Tag())
+ return file->ID3v1Tag()->genre();
+
+ return String::null;
+ }
+
+ virtual uint year() const {
+ if(file->ID3v2Tag() && file->ID3v2Tag()->year() > 0)
+ return file->ID3v2Tag()->year();
+
+ if(file->ID3v1Tag())
+ return file->ID3v1Tag()->year();
+
+ return 0;
+ }
+
+ virtual uint track() const {
+ if(file->ID3v2Tag() && file->ID3v2Tag()->track() > 0)
+ return file->ID3v2Tag()->track();
+
+ if(file->ID3v1Tag())
+ return file->ID3v1Tag()->track();
+
+ return 0;
+ }
+
+ virtual void setTitle(const String &s) {
+ file->ID3v2Tag(true)->setTitle(s);
+ file->ID3v1Tag(true)->setTitle(s);
+ }
+
+ virtual void setArtist(const String &s) {
+ file->ID3v2Tag(true)->setArtist(s);
+ file->ID3v1Tag(true)->setArtist(s);
+ }
+
+ virtual void setAlbum(const String &s) {
+ file->ID3v2Tag(true)->setAlbum(s);
+ file->ID3v1Tag(true)->setAlbum(s);
+ }
+
+ virtual void setComment(const String &s) {
+ file->ID3v2Tag(true)->setComment(s);
+ file->ID3v1Tag(true)->setComment(s);
+ }
+
+ virtual void setGenre(const String &s) {
+ file->ID3v2Tag(true)->setGenre(s);
+ file->ID3v1Tag(true)->setGenre(s);
+ }
+
+ virtual void setYear(uint i) {
+ file->ID3v2Tag(true)->setYear(i);
+ file->ID3v1Tag(true)->setYear(i);
+ }
+
+ virtual void setTrack(uint i) {
+ file->ID3v2Tag(true)->setTrack(i);
+ file->ID3v1Tag(true)->setTrack(i);
+ }
+
+ private:
+ MPEG::File *file;
+ };
+}
+
+class MPEG::File::FilePrivate
+{
+public:
+ FilePrivate() :
+ ID3v2FrameFactory(ID3v2::FrameFactory::instance()),
+ ID3v2Tag(0),
+ ID3v2Location(-1),
+ ID3v2OriginalSize(0),
+ ID3v1Tag(0),
+ ID3v1Location(-1),
+ tag(0),
+ hasID3v2(false),
+ hasID3v1(false),
+ properties(0) {}
+
+ ~FilePrivate() {
+ delete ID3v2Tag;
+ delete ID3v1Tag;
+ delete tag;
+ delete properties;
+ }
+
+ const ID3v2::FrameFactory *ID3v2FrameFactory;
+ ID3v2::Tag *ID3v2Tag;
+ long ID3v2Location;
+ uint ID3v2OriginalSize;
+
+ ID3v1::Tag *ID3v1Tag;
+ long ID3v1Location;
+
+ MPEGTag *tag;
+
+ // These indicate whether the file *on disk* has an ID3v[1/2] tag, not if
+ // this data structure does. This is used in computing offsets.
+
+ bool hasID3v2;
+ bool hasID3v1;
+
+ Properties *properties;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// public members
+////////////////////////////////////////////////////////////////////////////////
+
+MPEG::File::File(const char *file, bool readProperties,
+ Properties::ReadStyle propertiesStyle) : TagLib::File(file)
+{
+ d = new FilePrivate;
+ if(isOpen()) {
+ d->tag = new MPEGTag(this);
+ read(readProperties, propertiesStyle);
+ }
+}
+
+MPEG::File::~File()
+{
+ delete d;
+}
+
+TagLib::Tag *MPEG::File::tag() const
+{
+ return d->tag;
+}
+
+MPEG::Properties *MPEG::File::audioProperties() const
+{
+ return d->properties;
+}
+
+void MPEG::File::save()
+{
+ save(ID3v1 | ID3v2);
+}
+
+void MPEG::File::save(int tags)
+{
+ if(tags == NoTags) {
+ strip(AllTags);
+ return;
+ }
+
+ if(!d->ID3v2Tag && !d->ID3v1Tag) {
+
+ if(d->hasID3v1 || d->hasID3v2)
+ strip(AllTags);
+
+ return;
+ }
+
+ if(readOnly()) {
+ debug("MPEG::File::save() -- File is read only.");
+ return;
+ }
+
+ // Create the tags if we've been asked to. Copy the values from the tag that
+ // does exist into the new tag.
+
+ if(tags & ID3v2 && d->ID3v1Tag)
+ Tag::duplicate(d->ID3v1Tag, ID3v2Tag(true), false);
+
+ if(tags & ID3v1 && d->ID3v2Tag)
+ Tag::duplicate(d->ID3v2Tag, ID3v1Tag(true), false);
+
+
+ if(ID3v2 & tags) {
+
+ if(d->ID3v2Tag && !d->ID3v2Tag->isEmpty()) {
+
+ if(!d->hasID3v2)
+ d->ID3v2Location = 0;
+
+ insert(d->ID3v2Tag->render(), d->ID3v2Location, d->ID3v2OriginalSize);
+ }
+ else
+ strip(ID3v2);
+ }
+ else if(d->hasID3v2)
+ strip(ID3v2);
+
+ if(ID3v1 & tags) {
+ if(d->ID3v1Tag && !d->ID3v1Tag->isEmpty()) {
+ int offset = d->hasID3v1 ? -128 : 0;
+ seek(offset, End);
+ writeBlock(d->ID3v1Tag->render());
+ }
+ else
+ strip(ID3v1);
+ }
+ else if(d->hasID3v1)
+ strip(ID3v1);
+}
+
+ID3v2::Tag *MPEG::File::ID3v2Tag(bool create)
+{
+ if(!create || d->ID3v2Tag)
+ return d->ID3v2Tag;
+
+ // no ID3v2 tag exists and we've been asked to create one
+
+ d->ID3v2Tag = new ID3v2::Tag();
+ return d->ID3v2Tag;
+}
+
+ID3v1::Tag *MPEG::File::ID3v1Tag(bool create)
+{
+ if(!create || d->ID3v1Tag)
+ return d->ID3v1Tag;
+
+ // no ID3v1 tag exists and we've been asked to create one
+
+ d->ID3v1Tag = new ID3v1::Tag;
+ return d->ID3v1Tag;
+}
+
+void MPEG::File::strip(int tags)
+{
+ if(readOnly()) {
+ debug("MPEG::File::strip() - Cannot strip tags from a read only file.");
+ return;
+ }
+
+ if(tags & ID3v2 && d->hasID3v2)
+ removeBlock(d->ID3v2Location, d->ID3v2OriginalSize);
+
+ if(tags & ID3v1 && d->hasID3v1) {
+ truncate(d->ID3v1Location);
+ d->ID3v1Location = -1;
+ d->hasID3v1 = false;
+ }
+}
+
+void MPEG::File::setID3v2FrameFactory(const ID3v2::FrameFactory *factory)
+{
+ d->ID3v2FrameFactory = factory;
+}
+
+long MPEG::File::nextFrameOffset(long position)
+{
+ // TODO: This will miss syncs spanning buffer read boundaries.
+
+ ByteVector buffer = readBlock(bufferSize());
+
+ while(buffer.size() > 0) {
+ seek(position);
+ ByteVector buffer = readBlock(bufferSize());
+
+ for(uint i = 0; i < buffer.size(); i++) {
+ if(uchar(buffer[i]) == 0xff && secondSynchByte(buffer[i + 1]))
+ return position + i;
+ }
+ position += bufferSize();
+ }
+
+ return -1;
+}
+
+long MPEG::File::previousFrameOffset(long position)
+{
+ // TODO: This will miss syncs spanning buffer read boundaries.
+
+ while(int(position - bufferSize()) > int(bufferSize())) {
+ position -= bufferSize();
+ seek(position);
+ ByteVector buffer = readBlock(bufferSize());
+
+ // If the amount of data is smaller than an MPEG header (4 bytes) there's no
+ // chance of this being valid.
+
+ if(buffer.size() < 4)
+ return -1;
+
+ for(int i = buffer.size() - 2; i >= 0; i--) {
+ if(uchar(buffer[i]) == 0xff && secondSynchByte(buffer[i + 1]))
+ return position + i;
+ }
+ }
+
+ return -1;
+}
+
+long MPEG::File::firstFrameOffset()
+{
+ long position = 0;
+
+ if(d->ID3v2Tag)
+ position = d->ID3v2Location + d->ID3v2Tag->header()->completeTagSize();
+
+ return nextFrameOffset(position);
+}
+
+long MPEG::File::lastFrameOffset()
+{
+ return previousFrameOffset(d->ID3v1Tag ? d->ID3v1Location - 1 : length());
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// private members
+////////////////////////////////////////////////////////////////////////////////
+
+void MPEG::File::read(bool readProperties, Properties::ReadStyle propertiesStyle)
+{
+ // Look for an ID3v2 tag
+
+ d->ID3v2Location = findID3v2();
+
+ if(d->ID3v2Location >= 0) {
+
+ d->ID3v2Tag = new ID3v2::Tag(this, d->ID3v2Location, d->ID3v2FrameFactory);
+
+ d->ID3v2OriginalSize = d->ID3v2Tag->header()->completeTagSize();
+
+ if(d->ID3v2Tag->header()->tagSize() <= 0) {
+ delete d->ID3v2Tag;
+ d->ID3v2Tag = 0;
+ }
+ else
+ d->hasID3v2 = true;
+ }
+
+ // Look for an ID3v1 tag
+
+ d->ID3v1Location = findID3v1();
+
+ if(d->ID3v1Location >= 0) {
+ d->ID3v1Tag = new ID3v1::Tag(this, d->ID3v1Location);
+ d->hasID3v1 = true;
+ }
+
+ if(readProperties)
+ d->properties = new Properties(this, propertiesStyle);
+}
+
+long MPEG::File::findID3v2()
+{
+ // This method is based on the contents of TagLib::File::find(), but because
+ // of some subtlteies -- specifically the need to look for the bit pattern of
+ // an MPEG sync, it has been modified for use here.
+
+ if(isValid() && ID3v2::Header::fileIdentifier().size() <= bufferSize()) {
+
+ // The position in the file that the current buffer starts at.
+
+ long bufferOffset = 0;
+ ByteVector buffer;
+
+ // These variables are used to keep track of a partial match that happens at
+ // the end of a buffer.
+
+ int previousPartialMatch = -1;
+ bool previousPartialSynchMatch = false;
+
+ // Save the location of the current read pointer. We will restore the
+ // position using seek() before all returns.
+
+ long originalPosition = tell();
+
+ // Start the search at the beginning of the file.
+
+ seek(0);
+
+ // This loop is the crux of the find method. There are three cases that we
+ // want to account for:
+ // (1) The previously searched buffer contained a partial match of the search
+ // pattern and we want to see if the next one starts with the remainder of
+ // that pattern.
+ //
+ // (2) The search pattern is wholly contained within the current buffer.
+ //
+ // (3) The current buffer ends with a partial match of the pattern. We will
+ // note this for use in the next itteration, where we will check for the rest
+ // of the pattern.
+
+ for(buffer = readBlock(bufferSize()); buffer.size() > 0; buffer = readBlock(bufferSize())) {
+
+ // (1) previous partial match
+
+ if(previousPartialSynchMatch && secondSynchByte(buffer[0]))
+ return -1;
+
+ if(previousPartialMatch >= 0 && int(bufferSize()) > previousPartialMatch) {
+ const int patternOffset = (bufferSize() - previousPartialMatch);
+ if(buffer.containsAt(ID3v2::Header::fileIdentifier(), 0, patternOffset)) {
+ seek(originalPosition);
+ return bufferOffset - bufferSize() + previousPartialMatch;
+ }
+ }
+
+ // (2) pattern contained in current buffer
+
+ long location = buffer.find(ID3v2::Header::fileIdentifier());
+ if(location >= 0) {
+ seek(originalPosition);
+ return bufferOffset + location;
+ }
+
+ int firstSynchByte = buffer.find(char(uchar(255)));
+
+ // Here we have to loop because there could be several of the first
+ // (11111111) byte, and we want to check all such instances until we find
+ // a full match (11111111 111) or hit the end of the buffer.
+
+ while(firstSynchByte >= 0) {
+
+ // if this *is not* at the end of the buffer
+
+ if(firstSynchByte < int(buffer.size()) - 1) {
+ if(secondSynchByte(buffer[firstSynchByte + 1])) {
+ // We've found the frame synch pattern.
+ seek(originalPosition);
+ return -1;
+ }
+ else {
+
+ // We found 11111111 at the end of the current buffer indicating a
+ // partial match of the synch pattern. The find() below should
+ // return -1 and break out of the loop.
+
+ previousPartialSynchMatch = true;
+ }
+ }
+
+ // Check in the rest of the buffer.
+
+ firstSynchByte = buffer.find(char(uchar(255)), firstSynchByte + 1);
+ }
+
+ // (3) partial match
+
+ previousPartialMatch = buffer.endsWithPartialMatch(ID3v2::Header::fileIdentifier());
+
+ bufferOffset += bufferSize();
+ }
+
+ // Since we hit the end of the file, reset the status before continuing.
+
+ clear();
+
+ seek(originalPosition);
+ }
+
+ return -1;
+}
+
+long MPEG::File::findID3v1()
+{
+ if(isValid()) {
+ seek(-128, End);
+ long p = tell();
+
+ if(readBlock(3) == ID3v1::Tag::fileIdentifier())
+ return p;
+ }
+ return -1;
+}
+
+bool MPEG::File::secondSynchByte(char byte)
+{
+ if(uchar(byte) == 0xff)
+ return false;
+
+ std::bitset<8> b(byte);
+
+ // check to see if the byte matches 111xxxxx
+ return b.test(7) && b.test(6) && b.test(5);
+}
--- /dev/null
+/***************************************************************************
+ copyright : (C) 2002, 2003 by Scott Wheeler
+ email : wheeler@kde.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_MPEGFILE_H
+#define TAGLIB_MPEGFILE_H
+
+#include <tfile.h>
+
+#include "mpegproperties.h"
+
+namespace TagLib {
+
+ namespace ID3v2 { class Tag; class FrameFactory; }
+ namespace ID3v1 { class Tag; }
+
+ //! An implementation of TagLib::File with MPEG (MP3) specific methods
+
+ namespace MPEG {
+
+ //! An MPEG file class with some useful methods specific to MPEG
+
+ /*!
+ * This implements the generic TagLib::File API and additionally provides
+ * access to properties that are distinct to MPEG files, notably access
+ * to the different ID3 tags.
+ */
+
+ class 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 ID3v2 tags.
+ ID3v2 = 0x0002,
+ //! Matches all tag types.
+ AllTags = 0xffff
+ };
+
+ /*!
+ * Contructs an MPEG 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 a pointer to a tag that is the union of the ID3v2 and ID3v1
+ * tags. The ID3v2 tag is given priority in reading the information -- if
+ * requested information exists in both the ID3v2 tag and the ID3v1 tag,
+ * the information from the ID3v2 tag will be returned.
+ *
+ * If you would like more granular control over the content of the tags,
+ * with the concession of generality, use the tag-type specific calls.
+ *
+ * \note As this tag is not implemented as an ID3v2 tag or an ID3v1 tag,
+ * but a union of the two this pointer may not be cast to the specific
+ * tag types.
+ *
+ * \see ID3v1Tag()
+ * \see ID3v2Tag()
+ */
+ virtual Tag *tag() const;
+
+ /*!
+ * Returns the MPEG::Properties for this file. If no audio properties
+ * were read then this will return a null pointer.
+ */
+ virtual Properties *audioProperties() const;
+
+ /*!
+ * Save the file. If at least one tag -- ID3v1 or ID3v2 -- exists this
+ * will duplicate its content into the other tag.
+ *
+ * If neither exists or if both tags are empty, this will strip the tags
+ * from the file.
+ *
+ * This is the same as calling save(AllTags);
+ *
+ * If you would like more granular control over the content of the tags,
+ * with the concession of generality, use paramaterized save call below.
+ *
+ * \see save(int tags)
+ */
+ virtual void save();
+
+ /*!
+ * Save the file. This will attempt to save all of the tag types that are
+ * specified by OR-ing together TagTypes values. The save() method above
+ * uses AllTags.
+ *
+ * This strips all tags not included in the mask, but does not modify them
+ * in memory, so later calls to save() which make use of these tags will
+ * remain valid. This also strips empty tags.
+ */
+ void save(int tags);
+
+ /*!
+ * Returns a pointer to the ID3v2 tag of the file.
+ *
+ * This method will return a null pointer if either the file can not be
+ * read from.
+ *
+ * If \a create is false (the default) it will also return a null pointer
+ * if there is no valid ID3v2 tag. If \a create is true it will create
+ * an ID3v2 tag if one does not exist.
+ *
+ * \note The Tag <b>is still</b> owned by the MPEG::File and should not be
+ * deleted by the user. It will be deleted when the file (object) is
+ * destroyed.
+ */
+ ID3v2::Tag *ID3v2Tag(bool create = false);
+
+ /*!
+ * Returns a pointer to the ID3v1 tag of the file.
+ *
+ * This method will return a null pointer if either the file can not be
+ * read from.
+ *
+ * If \a create is false (the default) it will also 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.
+ *
+ * \note The Tag <b>is still</b> owned by the MPEG::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);
+
+ /*!
+ * This will strip the tags that match the OR-ed together TagTypes from the
+ * file. By default it strips all tags.
+ */
+ void strip(int tags = AllTags);
+
+ /*!
+ * Set the ID3v2::FrameFactory to something other than the default.
+ *
+ * \see ID3v2FrameFactory
+ */
+ void setID3v2FrameFactory(const ID3v2::FrameFactory *factory);
+
+ /*!
+ * Returns the position in the file of the first MPEG frame.
+ */
+ long firstFrameOffset();
+
+ /*!
+ * Returns the position in the file of the next MPEG frame,
+ * using the current position as start
+ */
+ long nextFrameOffset(long position);
+
+ /*!
+ * Returns the position in the file of the previous MPEG frame,
+ * using the current position as start
+ */
+ long previousFrameOffset(long position);
+
+ /*!
+ * Returns the position in the file of the last MPEG frame.
+ */
+ long lastFrameOffset();
+
+ private:
+ File(const File &);
+ File &operator=(const File &);
+
+ void read(bool readProperties, Properties::ReadStyle propertiesStyle);
+ long findID3v2();
+ long findID3v1();
+
+ /*!
+ * MPEG frames can be recognized by the bit pattern 11111111 111, so the
+ * first byte is easy to check for, however checking to see if the second byte
+ * starts with \e 111 is a bit more tricky, hence this member function.
+ */
+ static bool secondSynchByte(char byte);
+
+ class FilePrivate;
+ FilePrivate *d;
+ };
+ }
+}
+
+#endif
--- /dev/null
+/***************************************************************************
+ copyright : (C) 2003 by Scott Wheeler
+ email : wheeler@kde.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 <bitset>
+
+#include <tbytevector.h>
+#include <tstring.h>
+#include <tdebug.h>
+
+#include "mpegheader.h"
+
+using namespace TagLib;
+
+class MPEG::Header::HeaderPrivate : public RefCounter
+{
+public:
+ HeaderPrivate() :
+ isValid(false),
+ version(Version1),
+ layer(0),
+ protectionEnabled(false),
+ sampleRate(0),
+ isPadded(false),
+ channelMode(Stereo),
+ isCopyrighted(false),
+ isOriginal(false),
+ frameLength(0) {}
+
+ bool isValid;
+ Version version;
+ int layer;
+ bool protectionEnabled;
+ int bitrate;
+ int sampleRate;
+ bool isPadded;
+ ChannelMode channelMode;
+ bool isCopyrighted;
+ bool isOriginal;
+ int frameLength;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// public members
+////////////////////////////////////////////////////////////////////////////////
+
+MPEG::Header::Header(const ByteVector &data)
+{
+ d = new HeaderPrivate;
+ parse(data);
+}
+
+MPEG::Header::Header(const Header &h) : d(h.d)
+{
+ d->ref();
+}
+
+MPEG::Header::~Header()
+{
+ if (d->deref())
+ delete d;
+}
+
+bool MPEG::Header::isValid() const
+{
+ return d->isValid;
+}
+
+MPEG::Header::Version MPEG::Header::version() const
+{
+ return d->version;
+}
+
+int MPEG::Header::layer() const
+{
+ return d->layer;
+}
+
+bool MPEG::Header::protectionEnabled() const
+{
+ return d->protectionEnabled;
+}
+
+int MPEG::Header::bitrate() const
+{
+ return d->bitrate;
+}
+
+int MPEG::Header::sampleRate() const
+{
+ return d->sampleRate;
+}
+
+bool MPEG::Header::isPadded() const
+{
+ return d->isPadded;
+}
+
+MPEG::Header::ChannelMode MPEG::Header::channelMode() const
+{
+ return d->channelMode;
+}
+
+bool MPEG::Header::isCopyrighted() const
+{
+ return d->isCopyrighted;
+}
+
+bool MPEG::Header::isOriginal() const
+{
+ return d->isOriginal;
+}
+
+int MPEG::Header::frameLength() const
+{
+ return d->frameLength;
+}
+
+MPEG::Header &MPEG::Header::operator=(const Header &h)
+{
+ if(&h == this)
+ return *this;
+
+ if(d->deref())
+ delete d;
+
+ d = h.d;
+ d->ref();
+ return *this;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// private members
+////////////////////////////////////////////////////////////////////////////////
+
+void MPEG::Header::parse(const ByteVector &data)
+{
+ if(data.size() < 4 || uchar(data[0]) != 0xff) {
+ debug("MPEG::Header::parse() -- First byte did not mactch MPEG synch.");
+ return;
+ }
+
+ std::bitset<32> flags(data.toUInt());
+
+ // Check for the second byte's part of the MPEG synch
+
+ if(!flags[23] || !flags[22] || !flags[21]) {
+ debug("MPEG::Header::parse() -- Second byte did not mactch MPEG synch.");
+ return;
+ }
+
+ // Set the MPEG version
+
+ if(!flags[20] && !flags[19])
+ d->version = Version2_5;
+ else if(flags[20] && !flags[19])
+ d->version = Version2;
+ else if(flags[20] && flags[19])
+ d->version = Version1;
+
+ // Set the MPEG layer
+
+ if(!flags[18] && flags[17])
+ d->layer = 3;
+ else if(flags[18] && !flags[17])
+ d->layer = 2;
+ else if(flags[18] && flags[17])
+ d->layer = 1;
+
+ d->protectionEnabled = !flags[16];
+
+ // Set the bitrate
+
+ static const int bitrates[2][3][16] = {
+ { // Version 1
+ { 0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448, 0 }, // layer 1
+ { 0, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384, 0 }, // layer 2
+ { 0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 0 } // layer 3
+ },
+ { // Version 2 or 2.5
+ { 0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256, 0 }, // layer 1
+ { 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 0 }, // layer 2
+ { 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 0 } // layer 3
+ }
+ };
+
+ const int versionIndex = d->version == Version1 ? 0 : 1;
+ const int layerIndex = d->layer > 0 ? d->layer - 1 : 0;
+
+ // The bitrate index is encoded as the first 4 bits of the 3rd byte,
+ // i.e. 1111xxxx
+
+ int i = uchar(data[2]) >> 4;
+
+ d->bitrate = bitrates[versionIndex][layerIndex][i];
+
+ // Set the sample rate
+
+ static const int sampleRates[3][4] = {
+ { 44100, 48000, 32000, 0 }, // Version 1
+ { 22050, 24000, 16000, 0 }, // Version 2
+ { 11025, 12000, 8000, 0 } // Version 2.5
+ };
+
+ // The sample rate index is encoded as two bits in the 3nd byte, i.e. xxxx11xx
+
+ i = uchar(data[2]) >> 2 & 0x03;
+
+ d->sampleRate = sampleRates[d->version][i];
+
+ if(d->sampleRate == 0) {
+ debug("MPEG::Header::parse() -- Invalid sample rate.");
+ return;
+ }
+
+ // The channel mode is encoded as a 2 bit value at the end of the 3nd byte,
+ // i.e. xxxxxx11
+
+ d->channelMode = ChannelMode(uchar(data[2]) & 0x3);
+
+ // TODO: Add mode extension for completeness
+
+ d->isCopyrighted = flags[0];
+ d->isOriginal = flags[1];
+
+ // Calculate the frame length
+
+ if(d->layer == 1)
+ d->frameLength = 24000 * 2 * d->bitrate / d->sampleRate + int(d->isPadded);
+ else
+ d->frameLength = 72000 * d->bitrate / d->sampleRate + int(d->isPadded);
+
+ // Now that we're done parsing, set this to be a valid frame.
+
+ d->isValid = true;
+}
--- /dev/null
+/***************************************************************************
+ copyright : (C) 2003 by Scott Wheeler
+ email : wheeler@kde.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_MPEGHEADER_H
+#define TAGLIB_MPEGHEADER_H
+
+namespace TagLib {
+
+ class ByteVector;
+
+ namespace MPEG {
+
+ //! An implementation of MP3 frame headers
+
+ /*!
+ * This is an implementation of MPEG Layer III headers. The API follows more
+ * or less the binary format of these headers. I've used
+ * <a href="http://www.mp3-tech.org/programmer/frame_header.html">this</a>
+ * document as a reference.
+ */
+
+ class Header
+ {
+ public:
+ /*!
+ * Parses an MPEG header based on \a data.
+ */
+ Header(const ByteVector &data);
+
+ /*!
+ * Does a shallow copy of \a h.
+ */
+ Header(const Header &h);
+
+ /*!
+ * Destroys this Header instance.
+ */
+ virtual ~Header();
+
+ /*!
+ * Returns true if the frame is at least an appropriate size and has
+ * legal values.
+ */
+ bool isValid() const;
+
+ /*!
+ * The MPEG Version.
+ */
+ enum Version {
+ //! MPEG Version 1
+ Version1 = 0,
+ //! MPEG Version 2
+ Version2 = 1,
+ //! MPEG Version 2.5
+ Version2_5 = 2
+ };
+
+ /*!
+ * Returns the MPEG Version of the header.
+ */
+ Version version() const;
+
+ /*!
+ * Returns the layer version. This will be between the values 1-3.
+ */
+ int layer() const;
+
+ /*!
+ * Returns true if the MPEG protection bit is enabled.
+ */
+ bool protectionEnabled() const;
+
+ /*!
+ * Returns the bitrate encoded in the header.
+ */
+ int bitrate() const;
+
+ /*!
+ * Returns the sample rate in Hz.
+ */
+ int sampleRate() const;
+
+ /*!
+ * Returns true if the frame is padded.
+ */
+ bool isPadded() const;
+
+ /*!
+ * There are a few combinations or one or two channel audio that are
+ * possible:
+ */
+ enum ChannelMode {
+ //! Stereo
+ Stereo = 0,
+ //! Stereo
+ JointStereo = 1,
+ //! Dual Mono
+ DualChannel = 2,
+ //! Mono
+ SingleChannel = 3
+ };
+
+ /*!
+ * Returns the channel mode for this frame.
+ */
+ ChannelMode channelMode() const;
+
+ /*!
+ * Returns true if the copyrighted bit is set.
+ */
+ bool isCopyrighted() const;
+
+ /*!
+ * Returns true if the "original" bit is set.
+ */
+ bool isOriginal() const;
+
+ /*!
+ * Returns the frame length.
+ */
+ int frameLength() const;
+
+ /*!
+ * Makes a shallow copy of the header.
+ */
+ Header &operator=(const Header &h);
+
+ private:
+ void parse(const ByteVector &data);
+
+ class HeaderPrivate;
+ HeaderPrivate *d;
+ };
+ }
+}
+
+#endif
--- /dev/null
+/***************************************************************************
+ copyright : (C) 2003 by Scott Wheeler
+ email : wheeler@kde.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 <tdebug.h>
+#include <tstring.h>
+
+#include "mpegproperties.h"
+#include "mpegfile.h"
+#include "xingheader.h"
+
+using namespace TagLib;
+
+class MPEG::Properties::PropertiesPrivate
+{
+public:
+ PropertiesPrivate(File *f, ReadStyle s) :
+ file(f),
+ style(s),
+ length(0),
+ bitrate(0),
+ sampleRate(0),
+ channels(0) {}
+
+ File *file;
+ ReadStyle style;
+ int length;
+ int bitrate;
+ int sampleRate;
+ int channels;
+ Header::Version version;
+ int layer;
+ Header::ChannelMode channelMode;
+ bool isCopyrighted;
+ bool isOriginal;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// public members
+////////////////////////////////////////////////////////////////////////////////
+
+MPEG::Properties::Properties(File *file, ReadStyle style) : AudioProperties(style)
+{
+ d = new PropertiesPrivate(file, style);
+
+ if(file && file->isOpen())
+ read();
+}
+
+MPEG::Properties::~Properties()
+{
+ delete d;
+}
+
+int MPEG::Properties::length() const
+{
+ return d->length;
+}
+
+int MPEG::Properties::bitrate() const
+{
+ return d->bitrate;
+}
+
+int MPEG::Properties::sampleRate() const
+{
+ return d->sampleRate;
+}
+
+int MPEG::Properties::channels() const
+{
+ return d->channels;
+}
+
+MPEG::Header::Version MPEG::Properties::version() const
+{
+ return d->version;
+}
+
+int MPEG::Properties::layer() const
+{
+ return d->layer;
+}
+
+MPEG::Header::ChannelMode MPEG::Properties::channelMode() const
+{
+ return d->channelMode;
+}
+
+bool MPEG::Properties::isCopyrighted() const
+{
+ return d->isCopyrighted;
+}
+
+bool MPEG::Properties::isOriginal() const
+{
+ return d->isOriginal;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// private members
+////////////////////////////////////////////////////////////////////////////////
+
+void MPEG::Properties::read()
+{
+ // Since we've likely just looked for the ID3v1 tag, start at the end of the
+ // file where we're least likely to have to have to move the disk head.
+
+ long last = d->file->lastFrameOffset();
+
+ if(last < 0) {
+ debug("MPEG::Properties::read() -- Could not find a valid last MPEG frame in the stream.");
+ return;
+ }
+
+ d->file->seek(last);
+ Header lastHeader(d->file->readBlock(4));
+
+ long first = d->file->firstFrameOffset();
+
+ if(first < 0) {
+ debug("MPEG::Properties::read() -- Could not find a valid first MPEG frame in the stream.");
+ return;
+ }
+
+ if(!lastHeader.isValid()) {
+
+ long pos = last;
+
+ while(pos > first) {
+
+ pos = d->file->previousFrameOffset(pos);
+
+ if(pos < 0)
+ break;
+
+ d->file->seek(pos);
+ Header header(d->file->readBlock(4));
+
+ if(header.isValid()) {
+ lastHeader = header;
+ last = pos;
+ break;
+ }
+ }
+ }
+
+ // Now jump back to the front of the file and read what we need from there.
+
+ d->file->seek(first);
+ Header firstHeader(d->file->readBlock(4));
+
+ if(!firstHeader.isValid() || !lastHeader.isValid()) {
+ debug("MPEG::Properties::read() -- Page headers were invalid.");
+ return;
+ }
+
+ // Check for a Xing header that will help us in gathering information about a
+ // VBR stream.
+
+ int xingHeaderOffset = MPEG::XingHeader::xingHeaderOffset(firstHeader.version(),
+ firstHeader.channelMode());
+
+ d->file->seek(first + xingHeaderOffset);
+ XingHeader xingHeader(d->file->readBlock(16));
+
+ // Read the length and the bitrate from the Xing header.
+
+ if(xingHeader.isValid() &&
+ firstHeader.sampleRate() > 0 &&
+ xingHeader.totalFrames() > 0)
+ {
+ static const int blockSize[] = { 0, 384, 1152, 1152 };
+
+ double timePerFrame = blockSize[firstHeader.layer()];
+ timePerFrame = timePerFrame / firstHeader.sampleRate();
+ d->length = int(timePerFrame * xingHeader.totalFrames());
+ d->bitrate = xingHeader.totalSize() * 8 / d->length / 1000;
+ }
+
+ // Since there was no valid Xing header found, we hope that we're in a constant
+ // bitrate file.
+
+ // TODO: Make this more robust with audio property detection for VBR without a
+ // Xing header.
+
+ else if(firstHeader.frameLength() > 0 && firstHeader.bitrate() > 0) {
+ int frames = (last - first) / firstHeader.frameLength() + 1;
+
+ d->length = int(float(firstHeader.frameLength() * frames) /
+ float(firstHeader.bitrate() * 125) + 0.5);
+ d->bitrate = firstHeader.bitrate();
+ }
+
+
+ d->sampleRate = firstHeader.sampleRate();
+ d->channels = firstHeader.channelMode() == Header::SingleChannel ? 1 : 2;
+ d->version = firstHeader.version();
+ d->layer = firstHeader.layer();
+ d->channelMode = firstHeader.channelMode();
+ d->isCopyrighted = firstHeader.isCopyrighted();
+ d->isOriginal = firstHeader.isOriginal();
+}
--- /dev/null
+/***************************************************************************
+ copyright : (C) 2003 by Scott Wheeler
+ email : wheeler@kde.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_MPEGPROPERTIES_H
+#define TAGLIB_MPEGPROPERTIES_H
+
+#include <audioproperties.h>
+
+#include "mpegheader.h"
+
+namespace TagLib {
+
+ namespace MPEG {
+
+ class File;
+
+ //! An implementation of audio property reading for MP3
+
+ /*!
+ * This reads the data from an MPEG Layer III stream found in the
+ * AudioProperties API.
+ */
+
+ class Properties : public AudioProperties
+ {
+ public:
+ /*!
+ * Create an instance of MPEG::Properties with the data read from the
+ * MPEG::File \a file.
+ */
+ Properties(File *file, ReadStyle style = Average);
+
+ /*!
+ * Destroys this MPEG Properties instance.
+ */
+ virtual ~Properties();
+
+ // Reimplementations.
+
+ virtual int length() const;
+ virtual int bitrate() const;
+ virtual int sampleRate() const;
+ virtual int channels() const;
+
+ /*!
+ * Returns the MPEG Version of the file.
+ */
+ Header::Version version() const;
+
+ /*!
+ * Returns the layer version. This will be between the values 1-3.
+ */
+ int layer() const;
+
+ /*!
+ * Returns true if the MPEG protection bit is enabled.
+ */
+ bool protectionEnabled() const;
+
+ /*!
+ * Returns the channel mode for this frame.
+ */
+ Header::ChannelMode channelMode() const;
+
+ /*!
+ * Returns true if the copyrighted bit is set.
+ */
+ bool isCopyrighted() const;
+
+ /*!
+ * Returns true if the "original" bit is set.
+ */
+ bool isOriginal() const;
+
+ private:
+ Properties(const Properties &);
+ Properties &operator=(const Properties &);
+
+ void read();
+
+ class PropertiesPrivate;
+ PropertiesPrivate *d;
+ };
+ }
+}
+
+#endif
--- /dev/null
+/***************************************************************************
+ copyright : (C) 2003 by Ismael Orenstein
+ email : orenstein@kde.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 "xingheader.h"
+
+using namespace TagLib;
+
+class MPEG::XingHeader::XingHeaderPrivate
+{
+public:
+ XingHeaderPrivate() :
+ frames(0),
+ size(0),
+ valid(false)
+ {}
+
+ uint frames;
+ uint size;
+ bool valid;
+};
+
+MPEG::XingHeader::XingHeader(const ByteVector &data)
+{
+ d = new XingHeaderPrivate;
+ parse(data);
+}
+
+MPEG::XingHeader::~XingHeader()
+{
+ delete d;
+}
+
+bool MPEG::XingHeader::isValid() const
+{
+ return d->valid;
+}
+
+TagLib::uint MPEG::XingHeader::totalFrames() const
+{
+ return d->frames;
+}
+
+TagLib::uint MPEG::XingHeader::totalSize() const
+{
+ return d->size;
+}
+
+int MPEG::XingHeader::xingHeaderOffset(TagLib::MPEG::Header::Version v,
+ TagLib::MPEG::Header::ChannelMode c)
+{
+ if(v == MPEG::Header::Version1) {
+ if(c == MPEG::Header::SingleChannel)
+ return 0x15;
+ else
+ return 0x24;
+ }
+ else {
+ if(c == MPEG::Header::SingleChannel)
+ return 0x0D;
+ else
+ return 0x15;
+ }
+}
+
+void MPEG::XingHeader::parse(const ByteVector &data)
+{
+ // Check to see if a valid Xing header is available.
+
+ if(data.mid(0, 4) != "Xing")
+ return;
+
+ // If the XingHeader doesn't contain the number of frames and the total stream
+ // info it's invalid.
+
+ if(!(data[7] & 0x02)) {
+ debug("MPEG::XingHeader::parse() -- Xing header doesn't contain the total number of frames.");
+ return;
+ }
+
+ if(!(data[7] & 0x04)) {
+ debug("MPEG::XingHeader::parse() -- Xing header doesn't contain the total stream size.");
+ return;
+ }
+
+ d->frames = data.mid(8, 4).toUInt();
+ d->size = data.mid(12, 4).toUInt();
+
+ d->valid = true;
+}
--- /dev/null
+/***************************************************************************
+ copyright : (C) 2003 by Ismael Orenstein
+ email : orenstein@kde.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_XINGHEADER_H
+#define TAGLIB_XINGHEADER_H
+
+#include "mpegheader.h"
+
+namespace TagLib {
+
+ class ByteVector;
+
+ namespace MPEG {
+
+ //! An implementation of the Xing VBR headers
+
+ /*!
+ * This is a minimalistic implementation of the Xing VBR headers. Xing
+ * headers are often added to VBR (variable bit rate) MP3 streams to make it
+ * easy to compute the length and quality of a VBR stream. Our implementation
+ * is only concerned with the total size of the stream (so that we can
+ * calculate the total playing time and the average bitrate). It uses
+ * <a href="http://home.pcisys.net/~melanson/codecs/mp3extensions.txt">this text</a>
+ * and the XMMS sources as references.
+ */
+
+ class XingHeader
+ {
+ public:
+ /*!
+ * Parses a Xing header based on \a data. The data must be at least 16
+ * bytes long (anything longer than this is discarded).
+ */
+ XingHeader(const ByteVector &data);
+
+ /*!
+ * Destroy this XingHeader instance.
+ */
+ virtual ~XingHeader();
+
+ /*!
+ * Returns true if the data was parsed properly and if there is a vaild
+ * Xing header present.
+ */
+ bool isValid() const;
+
+ /*!
+ * Returns the total number of frames.
+ */
+ uint totalFrames() const;
+
+ /*!
+ * Returns the total size of stream in bytes.
+ */
+ uint totalSize() const;
+
+ /*!
+ * Returns the offset for the start of this Xing header, given the
+ * version and channels of the frame
+ */
+ static int xingHeaderOffset(TagLib::MPEG::Header::Version v,
+ TagLib::MPEG::Header::ChannelMode c);
+
+ private:
+ void parse(const ByteVector &data);
+
+ class XingHeaderPrivate;
+ XingHeaderPrivate *d;
+ };
+ }
+}
+
+#endif
--- /dev/null
+SUBDIRS = vorbis
+
+INCLUDES = -I$(top_srcdir)/taglib -I$(top_srcdir)/taglib/toolkit $(all_includes)
+
+noinst_LTLIBRARIES = libogg.la
+
+libogg_la_SOURCES = \
+ oggfile.cpp \
+ oggpage.cpp \
+ oggpageheader.cpp \
+ xiphcomment.cpp
+
+taglib_include_HEADERS = \
+ oggfile.h \
+ oggpage.h \
+ oggpageheader.h \
+ xiphcomment.h
+
+taglib_includedir = $(includedir)/taglib
+
+libogg_la_LIBADD = ./vorbis/libvorbis.la
+
+EXTRA_DIST = $(libogg_la_SOURCES) $(taglib_include_HEADERS)
--- /dev/null
+/***************************************************************************
+ copyright : (C) 2003 by Scott Wheeler
+ email : wheeler@kde.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 <tbytevectorlist.h>
+#include <tmap.h>
+#include <tstring.h>
+#include <tdebug.h>
+
+#include "oggfile.h"
+#include "oggpage.h"
+#include "oggpageheader.h"
+
+using namespace TagLib;
+
+class Ogg::File::FilePrivate
+{
+public:
+ FilePrivate() :
+ streamSerialNumber(0),
+ firstPageHeader(0),
+ lastPageHeader(0),
+ currentPage(0),
+ currentPacketPage(0)
+ {
+ pages.setAutoDelete(true);
+ }
+
+ ~FilePrivate()
+ {
+ delete firstPageHeader;
+ delete lastPageHeader;
+ }
+
+ uint streamSerialNumber;
+ List<Page *> pages;
+ PageHeader *firstPageHeader;
+ PageHeader *lastPageHeader;
+ std::vector< List<int> > packetToPageMap;
+ Map<int, ByteVector> dirtyPackets;
+ List<int> dirtyPages;
+
+ //! The current page for the reader -- used by nextPage()
+ Page *currentPage;
+ //! The current page for the packet parser -- used by packet()
+ Page *currentPacketPage;
+ //! The packets for the currentPacketPage -- used by packet()
+ ByteVectorList currentPackets;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// public members
+////////////////////////////////////////////////////////////////////////////////
+
+Ogg::File::~File()
+{
+ delete d;
+}
+
+ByteVector Ogg::File::packet(uint i)
+{
+ // Check to see if we're called setPacket() for this packet since the last
+ // save:
+
+ if(d->dirtyPackets.contains(i))
+ return d->dirtyPackets[i];
+
+ // If we haven't indexed the page where the packet we're interested in starts,
+ // begin reading pages until we have.
+
+ while(d->packetToPageMap.size() <= i) {
+ if(!nextPage()) {
+ debug("Ogg::File::packet() -- Could not find the requested packet.");
+ return ByteVector::null;
+ }
+ }
+
+ // Start reading at the first page that contains part (or all) of this packet.
+ // If the last read stopped at the packet that we're interested in, don't
+ // reread its packet list. (This should make sequential packet reads fast.)
+
+ uint pageIndex = d->packetToPageMap[i].front();
+ if(d->currentPacketPage != d->pages[pageIndex]) {
+ d->currentPacketPage = d->pages[pageIndex];
+ d->currentPackets = d->currentPacketPage->packets();
+ }
+
+ // If the packet is completely contained in the first page that it's in, then
+ // just return it now.
+
+ if(d->currentPacketPage->containsPacket(i) & Page::CompletePacket)
+ return d->currentPackets[i - d->currentPacketPage->firstPacketIndex()];
+
+ // If the packet is *not* completely contained in the first page that it's a
+ // part of then that packet trails off the end of the page. Continue appending
+ // the pages' packet data until we hit a page that either does not end with the
+ // packet that we're fetching or where the last packet is complete.
+
+ ByteVector packet = d->currentPackets.back();
+ while(d->currentPacketPage->containsPacket(i) & Page::EndsWithPacket &&
+ !d->currentPacketPage->header()->lastPacketCompleted())
+ {
+ pageIndex++;
+ if(pageIndex == d->pages.size()) {
+ if(!nextPage()) {
+ debug("Ogg::File::packet() -- Could not find the requested packet.");
+ return ByteVector::null;
+ }
+ }
+ d->currentPacketPage = d->pages[pageIndex];
+ d->currentPackets = d->currentPacketPage->packets();
+ packet.append(d->currentPackets.front());
+ }
+
+ return packet;
+}
+
+void Ogg::File::setPacket(uint i, const ByteVector &p)
+{
+ while(d->packetToPageMap.size() <= i) {
+ if(!nextPage()) {
+ debug("Ogg::File::setPacket() -- Could not set the requested packet.");
+ return;
+ }
+ }
+
+ List<int>::ConstIterator it = d->packetToPageMap[i].begin();
+ for(; it != d->packetToPageMap[i].end(); ++it)
+ d->dirtyPages.sortedInsert(*it, true);
+
+ d->dirtyPackets.insert(i, p);
+}
+
+const Ogg::PageHeader *Ogg::File::firstPageHeader()
+{
+ if(d->firstPageHeader)
+ return d->firstPageHeader->isValid() ? d->firstPageHeader : 0;
+
+ long firstPageHeaderOffset = find("OggS");
+
+ if(firstPageHeaderOffset < 0)
+ return 0;
+
+ d->firstPageHeader = new PageHeader(this, firstPageHeaderOffset);
+ return d->firstPageHeader->isValid() ? d->firstPageHeader : 0;
+}
+
+const Ogg::PageHeader *Ogg::File::lastPageHeader()
+{
+ if(d->lastPageHeader)
+ return d->lastPageHeader->isValid() ? d->lastPageHeader : 0;
+
+ long lastPageHeaderOffset = rfind("OggS");
+
+ if(lastPageHeaderOffset < 0)
+ return 0;
+
+ d->lastPageHeader = new PageHeader(this, lastPageHeaderOffset);
+ return d->lastPageHeader->isValid() ? d->lastPageHeader : 0;
+}
+
+void Ogg::File::save()
+{
+ List<int> pageGroup;
+
+ for(List<int>::ConstIterator it = d->dirtyPages.begin(); it != d->dirtyPages.end(); ++it) {
+ if(!pageGroup.isEmpty() && pageGroup.back() + 1 != *it) {
+ writePageGroup(pageGroup);
+ pageGroup.clear();
+ }
+ else
+ pageGroup.append(*it);
+ }
+ writePageGroup(pageGroup);
+ d->dirtyPages.clear();
+ d->dirtyPackets.clear();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// protected members
+////////////////////////////////////////////////////////////////////////////////
+
+Ogg::File::File(const char *file) : TagLib::File(file)
+{
+ d = new FilePrivate;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// private members
+////////////////////////////////////////////////////////////////////////////////
+
+bool Ogg::File::nextPage()
+{
+ long nextPageOffset;
+ int currentPacket;
+
+ if(d->pages.isEmpty()) {
+ currentPacket = 0;
+ nextPageOffset = find("OggS");
+ if(nextPageOffset < 0)
+ return false;
+ }
+ else {
+ if(d->currentPage->header()->lastPageOfStream())
+ return false;
+
+ if(d->currentPage->header()->lastPacketCompleted())
+ currentPacket = d->currentPage->firstPacketIndex() + d->currentPage->packetCount();
+ else
+ currentPacket = d->currentPage->firstPacketIndex() + d->currentPage->packetCount() - 1;
+
+ nextPageOffset = d->currentPage->fileOffset() + d->currentPage->size();
+ }
+
+ // Read the next page and add it to the page list.
+
+ d->currentPage = new Page(this, nextPageOffset);
+
+ if(!d->currentPage->header()->isValid()) {
+ delete d->currentPage;
+ d->currentPage = 0;
+ return false;
+ }
+
+ d->currentPage->setFirstPacketIndex(currentPacket);
+
+ if(d->pages.isEmpty())
+ d->streamSerialNumber = d->currentPage->header()->streamSerialNumber();
+
+ d->pages.append(d->currentPage);
+
+ // Loop through the packets in the page that we just read appending the
+ // current page number to the packet to page map for each packet.
+
+ for(uint i = 0; i < d->currentPage->packetCount(); i++) {
+ uint currentPacket = d->currentPage->firstPacketIndex() + i;
+ if(d->packetToPageMap.size() <= currentPacket)
+ d->packetToPageMap.push_back(List<int>());
+ d->packetToPageMap[currentPacket].append(d->pages.size() - 1);
+ }
+
+ return true;
+}
+
+void Ogg::File::writePageGroup(const List<int> &pageGroup)
+{
+ if(pageGroup.isEmpty())
+ return;
+
+ ByteVectorList packets;
+
+ // If the first page of the group isn't dirty, append its partial content here.
+
+ if(!d->dirtyPages.contains(d->pages[pageGroup.front()]->firstPacketIndex()))
+ packets.append(d->pages[pageGroup.front()]->packets().front());
+
+ int previousPacket = -1;
+ int originalSize = 0;
+
+ for(List<int>::ConstIterator it = pageGroup.begin(); it != pageGroup.end(); ++it) {
+ uint firstPacket = d->pages[*it]->firstPacketIndex();
+ uint lastPacket = firstPacket + d->pages[*it]->packetCount() - 1;
+
+ List<int>::ConstIterator last = --pageGroup.end();
+
+ for(uint i = firstPacket; i <= lastPacket; i++) {
+
+ if(it == last && i == lastPacket && !d->dirtyPages.contains(i))
+ packets.append(d->pages[*it]->packets().back());
+ else if(int(i) != previousPacket) {
+ previousPacket = i;
+ packets.append(packet(i));
+ }
+ }
+ originalSize += d->pages[*it]->size();
+ }
+
+ const bool continued = d->pages[pageGroup.front()]->header()->firstPacketContinued();
+ const bool completed = d->pages[pageGroup.back()]->header()->lastPacketCompleted();
+
+ // TODO: This pagination method isn't accurate for what's being done here.
+ // This should account for real possibilities like non-aligned packets and such.
+
+ List<Page *> pages = Page::paginate(packets, Page::SinglePagePerGroup,
+ d->streamSerialNumber, pageGroup.front(),
+ continued, completed);
+
+ ByteVector data;
+ for(List<Page *>::ConstIterator it = pages.begin(); it != pages.end(); ++it)
+ data.append((*it)->render());
+
+ // The insertion algorithms could also be improve to queue and prioritize data
+ // on the way out. Currently it requires rewriting the file for every page
+ // group rather than just once; however, for tagging applications there will
+ // generally only be one page group, so it's not worth the time for the
+ // optimization at the moment.
+
+ insert(data, d->pages[pageGroup.front()]->fileOffset(), originalSize);
+
+ // Update the page index to include the pages we just created and to delete the
+ // old pages.
+
+ for(List<Page *>::ConstIterator it = pages.begin(); it != pages.end(); ++it) {
+ const int index = (*it)->header()->pageSequenceNumber();
+ delete d->pages[index];
+ d->pages[index] = *it;
+ }
+}
--- /dev/null
+/***************************************************************************
+ copyright : (C) 2003 by Scott Wheeler
+ email : wheeler@kde.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 <tfile.h>
+#include <tbytevectorlist.h>
+
+#ifndef TAGLIB_OGGFILE_H
+#define TAGLIB_OGGFILE_H
+
+namespace TagLib {
+
+ //! A namespace for the classes used by Ogg-based metadata files
+
+ namespace Ogg {
+
+ class PageHeader;
+
+ //! An implementation of TagLib::File with some helpers for Ogg based formats
+
+ /*!
+ * This is an implementation of Ogg file page and packet rendering and is of
+ * use to Ogg based formats. While the API is small this handles the
+ * non-trivial details of breaking up an Ogg stream into packets and makes
+ * these available (via subclassing) to the codec meta data implementations.
+ */
+
+ class File : public TagLib::File
+ {
+ public:
+ virtual ~File();
+
+ /*!
+ * Returns the packet contents for the i-th packet (starting from zero)
+ * in the Ogg bitstream.
+ *
+ * \warning The requires reading at least the packet header for every page
+ * up to the requested page.
+ */
+ ByteVector packet(uint i);
+
+ /*!
+ * Sets the packet with index \a i to the value \a p.
+ */
+ void setPacket(uint i, const ByteVector &p);
+
+ /*!
+ * Returns a pointer to the PageHeader for the first page in the stream or
+ * null if the page could not be found.
+ */
+ const PageHeader *firstPageHeader();
+
+ /*!
+ * Returns a pointer to the PageHeader for the last page in the stream or
+ * null if the page could not be found.
+ */
+ const PageHeader *lastPageHeader();
+
+ virtual void save();
+
+ protected:
+ /*!
+ * Contructs an Ogg 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.
+ *
+ * \note This constructor is protected since Ogg::File shouldn't be
+ * instantiated directly but rather should be used through the codec
+ * specific subclasses.
+ */
+ File(const char *file);
+
+ private:
+ File(const File &);
+ File &operator=(const File &);
+
+ /*!
+ * Reads the next page and updates the internal "current page" pointer.
+ */
+ bool nextPage();
+ void writePageGroup(const List<int> &group);
+
+ class FilePrivate;
+ FilePrivate *d;
+ };
+
+ }
+}
+
+#endif
--- /dev/null
+/***************************************************************************
+ copyright : (C) 2003 by Scott Wheeler
+ email : wheeler@kde.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 "oggpage.h"
+#include "oggpageheader.h"
+#include "oggfile.h"
+
+using namespace TagLib;
+
+class Ogg::Page::PagePrivate
+{
+public:
+ PagePrivate(File *f = 0, long pageOffset = -1) :
+ file(f),
+ fileOffset(pageOffset),
+ packetOffset(0),
+ header(f, pageOffset),
+ firstPacketIndex(-1)
+ {
+ if(file) {
+ packetOffset = fileOffset + header.size();
+ packetSizes = header.packetSizes();
+ dataSize = header.dataSize();
+ }
+ }
+
+ File *file;
+ long fileOffset;
+ long packetOffset;
+ int dataSize;
+ List<int> packetSizes;
+ PageHeader header;
+ int firstPacketIndex;
+ ByteVectorList packets;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// public members
+////////////////////////////////////////////////////////////////////////////////
+
+Ogg::Page::Page(Ogg::File *file, long pageOffset)
+{
+ d = new PagePrivate(file, pageOffset);
+}
+
+Ogg::Page::~Page()
+{
+ delete d;
+}
+
+long Ogg::Page::fileOffset() const
+{
+ return d->fileOffset;
+}
+
+const Ogg::PageHeader *Ogg::Page::header() const
+{
+ return &d->header;
+}
+
+int Ogg::Page::firstPacketIndex() const
+{
+ return d->firstPacketIndex;
+}
+
+void Ogg::Page::setFirstPacketIndex(int index)
+{
+ d->firstPacketIndex = index;
+}
+
+Ogg::Page::ContainsPacketFlags Ogg::Page::containsPacket(int index) const
+{
+ int lastPacketIndex = d->firstPacketIndex + packetCount() - 1;
+ if(index < d->firstPacketIndex || index > lastPacketIndex)
+ return DoesNotContainPacket;
+
+ ContainsPacketFlags flags = DoesNotContainPacket;
+
+ if(index == d->firstPacketIndex)
+ flags = ContainsPacketFlags(flags | BeginsWithPacket);
+
+ if(index == lastPacketIndex)
+ flags = ContainsPacketFlags(flags | EndsWithPacket);
+
+ // If there's only one page and it's complete:
+
+ if(packetCount() == 1 &&
+ !d->header.firstPacketContinued() &&
+ d->header.lastPacketCompleted())
+ {
+ flags = ContainsPacketFlags(flags | CompletePacket);
+ }
+
+ // Or if the page is (a) the first page and it's complete or (b) the last page
+ // and it's complete or (c) a page in the middle.
+
+ else if((flags & BeginsWithPacket && !d->header.firstPacketContinued()) ||
+ (flags & EndsWithPacket && d->header.lastPacketCompleted()) ||
+ (!flags & BeginsWithPacket && !flags & EndsWithPacket))
+ {
+ flags = ContainsPacketFlags(flags | CompletePacket);
+ }
+
+ return flags;
+}
+
+TagLib::uint Ogg::Page::packetCount() const
+{
+ return d->header.packetSizes().size();
+}
+
+ByteVectorList Ogg::Page::packets() const
+{
+ if(!d->packets.isEmpty())
+ return d->packets;
+
+ ByteVectorList l;
+
+ if(d->file && d->header.isValid()) {
+
+ d->file->seek(d->packetOffset);
+
+ List<int> packetSizes = d->header.packetSizes();
+
+ List<int>::ConstIterator it = packetSizes.begin();
+ for(; it != packetSizes.end(); ++it)
+ l.append(d->file->readBlock(*it));
+ }
+ else
+ debug("Ogg::Page::packets() -- attempting to read packets from an invalid page.");
+
+ return l;
+}
+
+int Ogg::Page::size() const
+{
+ return d->header.size() + d->header.dataSize();
+}
+
+ByteVector Ogg::Page::render() const
+{
+ ByteVector data;
+
+ data.append(d->header.render());
+
+ if(d->packets.isEmpty()) {
+ if(d->file) {
+ d->file->seek(d->packetOffset);
+ data.append(d->file->readBlock(d->dataSize));
+ }
+ else
+ debug("Ogg::Page::render() -- this page is empty!");
+ }
+ else {
+ ByteVectorList::ConstIterator it = d->packets.begin();
+ for(; it != d->packets.end(); ++it)
+ data.append(*it);
+ }
+
+ // Compute and set the checksum for the Ogg page. The checksum is taken over
+ // the entire page with the 4 bytes reserved for the checksum zeroed and then
+ // inserted in bytes 22-25 of the page header.
+
+ ByteVector checksum = ByteVector::fromUInt(data.checksum(), false);
+ for(int i = 0; i < 4; i++)
+ data[i + 22] = checksum[i];
+
+ return data;
+}
+
+List<Ogg::Page *> Ogg::Page::paginate(const ByteVectorList &packets,
+ PaginationStrategy strategy,
+ uint streamSerialNumber,
+ int firstPage,
+ bool firstPacketContinued,
+ bool lastPacketCompleted,
+ bool containsLastPacket)
+{
+ List<Page *> l;
+
+ int totalSize = 0;
+
+ for(ByteVectorList::ConstIterator it = packets.begin(); it != packets.end(); ++it)
+ totalSize += (*it).size();
+
+ if(strategy == Repaginate || totalSize + packets.size() > 255 * 256) {
+ debug("Ogg::Page::paginate() -- Sorry! Repagination is not yet implemented.");
+ return l;
+ }
+
+ // TODO: Handle creation of multiple pages here with appropriate pagination.
+
+ Page *p = new Page(packets, streamSerialNumber, firstPage, firstPacketContinued,
+ lastPacketCompleted, containsLastPacket);
+ l.append(p);
+
+ return l;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// protected members
+////////////////////////////////////////////////////////////////////////////////
+
+Ogg::Page::Page(const ByteVectorList &packets,
+ uint streamSerialNumber,
+ int pageNumber,
+ bool firstPacketContinued,
+ bool lastPacketCompleted,
+ bool containsLastPacket)
+{
+ d = new PagePrivate;
+
+ ByteVector data;
+ List<int> packetSizes;
+
+ d->header.setFirstPageOfStream(pageNumber == 0 && !firstPacketContinued);
+ d->header.setLastPageOfStream(containsLastPacket);
+ d->header.setFirstPacketContinued(firstPacketContinued);
+ d->header.setLastPacketCompleted(lastPacketCompleted);
+ d->header.setStreamSerialNumber(streamSerialNumber);
+ d->header.setPageSequenceNumber(pageNumber);
+
+ // Build a page from the list of packets.
+
+ for(ByteVectorList::ConstIterator it = packets.begin(); it != packets.end(); ++it) {
+ packetSizes.append((*it).size());
+ data.append(*it);
+ }
+ d->packets = packets;
+ d->header.setPacketSizes(packetSizes);
+}
--- /dev/null
+/***************************************************************************
+ copyright : (C) 2003 by Scott Wheeler
+ email : wheeler@kde.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_OGGPAGE_H
+#define TAGLIB_OGGPAGE_H
+
+#include <tbytevectorlist.h>
+
+namespace TagLib {
+
+ namespace Ogg {
+
+ class File;
+ class PageHeader;
+
+ //! An implementation of Ogg pages
+
+ /*!
+ * This is an implementation of the pages that make up an Ogg stream.
+ * This handles parsing pages and breaking them down into packets and handles
+ * the details of packets spanning multiple pages and pages that contiain
+ * multiple packets.
+ *
+ * In most Xiph.org formats the comments are found in the first few packets,
+ * this however is a reasonably complete implementation of Ogg pages that
+ * could potentially be useful for non-meta data purposes.
+ */
+
+ class Page
+ {
+ public:
+ /*!
+ * Read an Ogg page from the \a file at the position \a pageOffset.
+ */
+ Page(File *file, long pageOffset);
+
+ virtual ~Page();
+
+ /*!
+ * Returns the page's position within the file (in bytes).
+ */
+ long fileOffset() const;
+
+ /*!
+ * Returns a pointer to the header for this page. This pointer will become
+ * invalid when the page is deleted.
+ */
+ const PageHeader *header() const;
+
+ /*!
+ * Returns the index of the first packet wholly or partially contained in
+ * this page.
+ *
+ * \see setFirstPacketIndex()
+ */
+ int firstPacketIndex() const;
+
+ /*!
+ * Sets the index of the first packet in the page.
+ *
+ * \see firstPacketIndex()
+ */
+ void setFirstPacketIndex(int index);
+
+ /*!
+ * When checking to see if a page contains a given packet this set of flags
+ * represents the possible values for that packets status in the page.
+ *
+ * \see containsPacket()
+ */
+ enum ContainsPacketFlags {
+ //! No part of the packet is contained in the page
+ DoesNotContainPacket = 0x0000,
+ //! The packet is wholly contained in the page
+ CompletePacket = 0x0001,
+ //! The page starts with the given packet
+ BeginsWithPacket = 0x0002,
+ //! The page ends with the given packet
+ EndsWithPacket = 0x0004
+ };
+
+ /*!
+ * Checks to see if the specified \a packet is contained in the current
+ * page.
+ *
+ * \see ContainsPacketFlags
+ */
+ ContainsPacketFlags containsPacket(int index) const;
+
+ /*!
+ * Returns the number of packets (whole or partial) in this page.
+ */
+ uint packetCount() const;
+
+ /*!
+ * Returns a list of the packets in this page.
+ *
+ * \note Either or both the first and last packets may be only partial.
+ * \see PageHeader::firstPacketContinued()
+ */
+ ByteVectorList packets() const;
+
+ /*!
+ * Returns the size of the page in bytes.
+ */
+ int size() const;
+
+ ByteVector render() const;
+
+ /*!
+ * Defines a strategy for pagination, or grouping pages into Ogg packets,
+ * for use with pagination methods.
+ *
+ * \note Yes, I'm aware that this is not a canonical "Strategy Pattern",
+ * the term was simply convenient.
+ */
+ enum PaginationStrategy {
+ /*!
+ * Attempt to put the specified set of packets into a single Ogg packet.
+ * If the sum of the packet data is greater than will fit into a single
+ * Ogg page -- 65280 bytes -- this will fall back to repagination using
+ * the recommended page sizes.
+ */
+ SinglePagePerGroup,
+ /*!
+ * Split the packet or group of packets into pages that conform to the
+ * sizes recommended in the Ogg standard.
+ */
+ Repaginate
+ };
+
+ /*!
+ * Pack \a packets into Ogg pages using the \a strategy for pagination.
+ * The page number indicater inside of the rendered packets will start
+ * with \a firstPage and be incremented for each page rendered.
+ * \a containsLastPacket should be set to true if \a packets contains the
+ * last page in the stream and will set the appropriate flag in the last
+ * rendered Ogg page's header. \a streamSerialNumber should be set to
+ * the serial number for this stream.
+ *
+ * \note The "absolute granule position" is currently always zeroed using
+ * this method as this suffices for the comment headers.
+ *
+ * \warning The pages returned by this method must be deleted by the user.
+ * You can use List<T>::setAutoDelete(true) to set these pages to be
+ * automatically deleted when this list passes out of scope.
+ *
+ * \see PaginationStrategy
+ * \see List::setAutoDelete()
+ */
+ static List<Page *> paginate(const ByteVectorList &packets,
+ PaginationStrategy strategy,
+ uint streamSerialNumber,
+ int firstPage,
+ bool firstPacketContinued = false,
+ bool lastPacketCompleted = true,
+ bool containsLastPacket = false);
+
+ protected:
+ /*!
+ * Creates an Ogg packet based on the data in \a packets. The page number
+ * for each page will be set to \a pageNumber.
+ */
+ Page(const ByteVectorList &packets,
+ uint streamSerialNumber,
+ int pageNumber,
+ bool firstPacketContinued = false,
+ bool lastPacketCompleted = true,
+ bool containsLastPacket = false);
+
+ private:
+ Page(const Page &);
+ Page &operator=(const Page &);
+
+ class PagePrivate;
+ PagePrivate *d;
+ };
+ }
+}
+#endif
--- /dev/null
+/***************************************************************************
+ copyright : (C) 2003 by Scott Wheeler
+ email : wheeler@kde.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 <bitset>
+
+#include <tstring.h>
+#include <tdebug.h>
+#include <taglib.h>
+
+#include "oggpageheader.h"
+#include "oggfile.h"
+
+using namespace TagLib;
+
+class Ogg::PageHeader::PageHeaderPrivate
+{
+public:
+ PageHeaderPrivate(File *f, long pageOffset) :
+ file(f),
+ fileOffset(pageOffset),
+ isValid(false),
+ firstPacketContinued(false),
+ lastPacketCompleted(false),
+ firstPageOfStream(false),
+ lastPageOfStream(false),
+ absoluteGranularPosition(0),
+ streamSerialNumber(0),
+ pageSequenceNumber(-1),
+ size(0),
+ dataSize(0)
+ {}
+
+ File *file;
+ long fileOffset;
+ bool isValid;
+ List<int> packetSizes;
+ bool firstPacketContinued;
+ bool lastPacketCompleted;
+ bool firstPageOfStream;
+ bool lastPageOfStream;
+ long long absoluteGranularPosition;
+ uint streamSerialNumber;
+ int pageSequenceNumber;
+ int size;
+ int dataSize;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// public members
+////////////////////////////////////////////////////////////////////////////////
+
+Ogg::PageHeader::PageHeader(Ogg::File *file, long pageOffset)
+{
+ d = new PageHeaderPrivate(file, pageOffset);
+ if(file && pageOffset >= 0)
+ read();
+}
+
+Ogg::PageHeader::~PageHeader()
+{
+ delete d;
+}
+
+bool Ogg::PageHeader::isValid() const
+{
+ return d->isValid;
+}
+
+List<int> Ogg::PageHeader::packetSizes() const
+{
+ return d->packetSizes;
+}
+
+void Ogg::PageHeader::setPacketSizes(const List<int> &sizes)
+{
+ d->packetSizes = sizes;
+}
+
+bool Ogg::PageHeader::firstPacketContinued() const
+{
+ return d->firstPacketContinued;
+}
+
+void Ogg::PageHeader::setFirstPacketContinued(bool continued)
+{
+ d->firstPacketContinued = continued;
+}
+
+bool Ogg::PageHeader::lastPacketCompleted() const
+{
+ return d->lastPacketCompleted;
+}
+
+void Ogg::PageHeader::setLastPacketCompleted(bool completed)
+{
+ d->lastPacketCompleted = completed;
+}
+
+bool Ogg::PageHeader::firstPageOfStream() const
+{
+ return d->firstPageOfStream;
+}
+
+void Ogg::PageHeader::setFirstPageOfStream(bool first)
+{
+ d->firstPageOfStream = first;
+}
+
+bool Ogg::PageHeader::lastPageOfStream() const
+{
+ return d->lastPageOfStream;
+}
+
+void Ogg::PageHeader::setLastPageOfStream(bool last)
+{
+ d->lastPageOfStream = last;
+}
+
+long long Ogg::PageHeader::absoluteGranularPosition() const
+{
+ return d->absoluteGranularPosition;
+}
+
+void Ogg::PageHeader::setAbsoluteGranularPosition(long long agp)
+{
+ d->absoluteGranularPosition = agp;
+}
+
+int Ogg::PageHeader::pageSequenceNumber() const
+{
+ return d->pageSequenceNumber;
+}
+
+void Ogg::PageHeader::setPageSequenceNumber(int sequenceNumber)
+{
+ d->pageSequenceNumber = sequenceNumber;
+}
+
+TagLib::uint Ogg::PageHeader::streamSerialNumber() const
+{
+ return d->streamSerialNumber;
+}
+
+void Ogg::PageHeader::setStreamSerialNumber(uint n)
+{
+ d->streamSerialNumber = n;
+}
+
+int Ogg::PageHeader::size() const
+{
+ return d->size;
+}
+
+int Ogg::PageHeader::dataSize() const
+{
+ return d->dataSize;
+}
+
+ByteVector Ogg::PageHeader::render() const
+{
+ ByteVector data;
+
+ // capture patern
+
+ data.append("OggS");
+
+ // stream structure version
+
+ data.append(char(0));
+
+ // header type flag
+
+ std::bitset<8> flags;
+ flags[0] = d->firstPacketContinued;
+ flags[1] = d->pageSequenceNumber == 0;
+ flags[2] = d->lastPageOfStream;
+
+ data.append(char(flags.to_ulong()));
+
+ // absolute granular position
+
+ data.append(ByteVector::fromLongLong(d->absoluteGranularPosition, false));
+
+ // stream serial number
+
+ data.append(ByteVector::fromUInt(d->streamSerialNumber, false));
+
+ // page sequence number
+
+ data.append(ByteVector::fromUInt(d->pageSequenceNumber, false));
+
+ // checksum -- this is left empty and should be filled in by the Ogg::Page
+ // class
+
+ data.append(ByteVector(4, 0));
+
+ // page segment count and page segment table
+
+ ByteVector pageSegments = lacingValues();
+
+ data.append(char(uchar(pageSegments.size())));
+ data.append(pageSegments);
+
+ return data;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// private members
+////////////////////////////////////////////////////////////////////////////////
+
+void Ogg::PageHeader::read()
+{
+ d->file->seek(d->fileOffset);
+
+ // An Ogg page header is at least 27 bytes, so we'll go ahead and read that
+ // much and then get the rest when we're ready for it.
+
+ ByteVector data = d->file->readBlock(27);
+
+ // Sanity check -- make sure that we were in fact able to read as much data as
+ // we asked for and that the page begins with "OggS".
+
+ if(data.size() != 27 || data.mid(0, 4) != "OggS") {
+ debug("Ogg::PageHeader::read() -- error reading page header");
+ return;
+ }
+
+ std::bitset<8> flags(data[5]);
+
+ d->firstPacketContinued = flags.test(0);
+ d->firstPageOfStream = flags.test(1);
+ d->lastPageOfStream = flags.test(2);
+
+ d->absoluteGranularPosition = data.mid(6, 8).toLongLong(false);
+ d->streamSerialNumber = data.mid(14, 4).toUInt(false);
+ d->pageSequenceNumber = data.mid(18, 4).toUInt(false);
+
+ // Byte number 27 is the number of page segments, which is the only variable
+ // length portion of the page header. After reading the number of page
+ // segments we'll then read in the coresponding data for this count.
+
+ int pageSegmentCount = uchar(data[26]);
+
+ ByteVector pageSegments = d->file->readBlock(pageSegmentCount);
+
+ // Another sanity check.
+
+ if(pageSegmentCount < 1 || int(pageSegments.size()) != pageSegmentCount)
+ return;
+
+ // The base size of an Ogg page 27 bytes plus the number of lacing values.
+
+ d->size = 27 + pageSegmentCount;
+
+ int packetSize = 0;
+
+ for(int i = 0; i < pageSegmentCount; i++) {
+ d->dataSize += uchar(pageSegments[i]);
+ packetSize += uchar(pageSegments[i]);
+
+ if(uchar(pageSegments[i]) < 255) {
+ d->packetSizes.append(packetSize);
+ packetSize = 0;
+ }
+ }
+
+ if(packetSize > 0) {
+ d->packetSizes.append(packetSize);
+ d->lastPacketCompleted = false;
+ }
+ else
+ d->lastPacketCompleted = true;
+
+ d->isValid = true;
+}
+
+ByteVector Ogg::PageHeader::lacingValues() const
+{
+ ByteVector data;
+
+ List<int> sizes = d->packetSizes;
+ for(List<int>::ConstIterator it = sizes.begin(); it != sizes.end(); ++it) {
+
+ // The size of a packet in an Ogg page is indicated by a series of "lacing
+ // values" where the sum of the values is the packet size in bytes. Each of
+ // these values is a byte. A value of less than 255 (0xff) indicates the end
+ // of the packet.
+
+ div_t n = div(*it, 255);
+
+ for(int i = 0; i < n.quot; i++)
+ data.append(char(uchar(255)));
+
+ if(it != --sizes.end() || d->lastPacketCompleted)
+ data.append(char(uchar(n.rem)));
+ }
+
+ return data;
+}
--- /dev/null
+/***************************************************************************
+ copyright : (C) 2003 by Scott Wheeler
+ email : wheeler@kde.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_OGGPAGEHEADER_H
+#define TAGLIB_OGGPAGEHEADER_H
+
+#include <tlist.h>
+#include <tbytevector.h>
+
+namespace TagLib {
+
+ namespace Ogg {
+
+ class File;
+
+ //! An implementation of the page headers associated with each Ogg::Page
+
+ /*!
+ * This class implements Ogg page headers which contain the information
+ * about Ogg pages needed to break them into packets which can be passed on
+ * to the codecs.
+ */
+
+ class PageHeader
+ {
+ public:
+ /*!
+ * Reads a PageHeader from \a file starting at \a pageOffset. The defaults
+ * create a page with no (and as such, invalid) data that must be set
+ * later.
+ */
+ PageHeader(File *file = 0, long pageOffset = -1);
+
+ /*!
+ * Deletes this instance of the PageHeader.
+ */
+ virtual ~PageHeader();
+
+ /*!
+ * Returns true if the header parsed properly and is valid.
+ */
+ bool isValid() const;
+
+ /*!
+ * Ogg pages contain a list of packets (which are used by the contained
+ * codecs). The sizes of these pages is encoded in the page header. This
+ * returns a list of the packet sizes in bytes.
+ *
+ * \see setPacketSizes()
+ */
+ List<int> packetSizes() const;
+
+ /*!
+ * Sets the sizes of the packets in this page to \a sizes. Internally this
+ * updates the lacing values in the header.
+ *
+ * \see packetSizes()
+ */
+ void setPacketSizes(const List<int> &sizes);
+
+ /*!
+ * Some packets can be <i>continued</i> across multiple pages. If the
+ * first packet in the current page is a continuation this will return
+ * true. If this is page starts with a new packet this will return false.
+ *
+ * \see lastPacketCompleted()
+ * \see setFirstPacketContinued()
+ */
+ bool firstPacketContinued() const;
+
+ /*!
+ * Sets the internal flag indicating if the first packet in this page is
+ * continued to \a continued.
+ *
+ * \see firstPacketContinued()
+ */
+ void setFirstPacketContinued(bool continued);
+
+ /*!
+ * Returns true if the last packet of this page is completely contained in
+ * this page.
+ *
+ * \see firstPacketContinued()
+ * \see setLastPacketCompleted()
+ */
+ bool lastPacketCompleted() const;
+
+ /*!
+ * Sets the internal flag indicating if the last packet in this page is
+ * complete to \a completed.
+ *
+ * \see lastPacketCompleted()
+ */
+ void setLastPacketCompleted(bool completed);
+
+ /*!
+ * This returns true if this is the first page of the Ogg (logical) stream.
+ *
+ * \see setFirstPageOfStream()
+ */
+ bool firstPageOfStream() const;
+
+ /*!
+ * Marks this page as the first page of the Ogg stream.
+ *
+ * \see firstPageOfStream()
+ */
+ void setFirstPageOfStream(bool first);
+
+ /*!
+ * This returns true if this is the last page of the Ogg (logical) stream.
+ *
+ * \see setLastPageOfStream()
+ */
+ bool lastPageOfStream() const;
+
+ /*!
+ * Marks this page as the last page of the Ogg stream.
+ *
+ * \see lastPageOfStream()
+ */
+ void setLastPageOfStream(bool last);
+
+ /*!
+ * A special value of containing the position of the packet to be
+ * interpreted by the codec. In the case of Vorbis this contains the PCM
+ * value and is used to calculate the length of the stream.
+ *
+ * \see setAbsoluteGranularPosition()
+ */
+ long long absoluteGranularPosition() const;
+
+ /*!
+ * A special value of containing the position of the packet to be
+ * interpreted by the codec. It is only supported here so that it may be
+ * coppied from one page to another.
+ *
+ * \see absoluteGranularPosition()
+ */
+ void setAbsoluteGranularPosition(long long agp);
+
+ /*!
+ * Every Ogg logical stream is given a random serial number which is common
+ * to every page in that logical stream. This returns the serial number of
+ * the stream associated with this packet.
+ *
+ * \see setStreamSerialNumber()
+ */
+ uint streamSerialNumber() const;
+
+ /*!
+ * Every Ogg logical stream is given a random serial number which is common
+ * to every page in that logical stream. This sets this pages serial
+ * number. This method should be used when adding new pages to a logical
+ * stream.
+ *
+ * \see streamSerialNumber()
+ */
+ void setStreamSerialNumber(uint n);
+
+ /*!
+ * Returns the index of the page within the Ogg stream. This helps make it
+ * possible to determine if pages have been lost.
+ *
+ * \see setPageSequenceNumber()
+ */
+ int pageSequenceNumber() const;
+
+ /*!
+ * Sets the page's position in the stream to \a sequenceNumber.
+ *
+ * \see pageSequenceNumber()
+ */
+ void setPageSequenceNumber(int sequenceNumber);
+
+ /*!
+ * Returns the complete header size.
+ */
+ int size() const;
+
+ /*!
+ * Returns the size of the data portion of the page -- i.e. the size of the
+ * page less the header size.
+ */
+ int dataSize() const;
+
+ /*!
+ * Render the page header to binary data.
+ *
+ * \note The checksum -- bytes 22 - 25 -- will be left empty and must be
+ * filled in when rendering the entire page.
+ */
+ ByteVector render() const;
+
+ private:
+ PageHeader(const PageHeader &);
+ PageHeader &operator=(const PageHeader &);
+
+ void read();
+ ByteVector lacingValues() const;
+
+ class PageHeaderPrivate;
+ PageHeaderPrivate *d;
+ };
+
+ }
+}
+
+#endif
--- /dev/null
+INCLUDES = \
+ -I$(top_srcdir)/taglib \
+ -I$(top_srcdir)/taglib/toolkit \
+ -I$(top_srcdir)/taglib/ogg \
+ $(all_includes)
+
+noinst_LTLIBRARIES = libvorbis.la
+
+libvorbis_la_SOURCES = vorbisfile.cpp vorbisproperties.cpp
+
+taglib_include_HEADERS = vorbisfile.h vorbisproperties.h
+taglib_includedir = $(includedir)/taglib
+
+EXTRA_DIST = $(libvorbis_la_SOURCES) $(taglib_include_HEADERS)
--- /dev/null
+/***************************************************************************
+ copyright : (C) 2003 by Scott Wheeler
+ email : wheeler@kde.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 <bitset>
+
+#include <tstring.h>
+#include <tdebug.h>
+
+#include "vorbisfile.h"
+
+using namespace TagLib;
+
+class Vorbis::File::FilePrivate
+{
+public:
+ FilePrivate() :
+ comment(0),
+ properties(0) {}
+
+ ~FilePrivate()
+ {
+ delete comment;
+ delete properties;
+ }
+
+ Ogg::XiphComment *comment;
+ Properties *properties;
+};
+
+namespace TagLib {
+ /*!
+ * Vorbis headers can be found with one type ID byte and the string "vorbis" in
+ * an Ogg stream. 0x03 indicates the comment header.
+ */
+ static const char vorbisCommentHeaderID[] = { 0x03, 'v', 'o', 'r', 'b', 'i', 's', 0 };
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// public members
+////////////////////////////////////////////////////////////////////////////////
+
+Vorbis::File::File(const char *file, bool readProperties,
+ Properties::ReadStyle propertiesStyle) : Ogg::File(file)
+{
+ d = new FilePrivate;
+ read(readProperties, propertiesStyle);
+}
+
+Vorbis::File::~File()
+{
+ delete d;
+}
+
+Ogg::XiphComment *Vorbis::File::tag() const
+{
+ return d->comment;
+}
+
+Vorbis::Properties *Vorbis::File::audioProperties() const
+{
+ return d->properties;
+}
+
+void Vorbis::File::save()
+{
+ ByteVector v(vorbisCommentHeaderID);
+
+ if(!d->comment)
+ d->comment = new Ogg::XiphComment;
+ v.append(d->comment->render());
+
+ setPacket(1, v);
+
+ Ogg::File::save();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// private members
+////////////////////////////////////////////////////////////////////////////////
+
+void Vorbis::File::read(bool readProperties, Properties::ReadStyle propertiesStyle)
+{
+ ByteVector commentHeaderData = packet(1);
+
+ if(commentHeaderData.mid(0, 7) != vorbisCommentHeaderID) {
+ debug("Vorbis::File::read() - Could not find the Vorbis comment header.");
+ setValid(false);
+ return;
+ }
+
+ d->comment = new Ogg::XiphComment(commentHeaderData.mid(7));
+
+ if(readProperties)
+ d->properties = new Properties(this, propertiesStyle);
+}
--- /dev/null
+/***************************************************************************
+ copyright : (C) 2002 by Scott Wheeler
+ email : wheeler@kde.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_VORBISFILE_H
+#define TAGLIB_VORBISFILE_H
+
+#include <oggfile.h>
+#include <xiphcomment.h>
+
+#include "vorbisproperties.h"
+
+namespace TagLib {
+
+ //! A namespace containing classes for Vorbis metadata
+
+ namespace Vorbis {
+
+
+ //! An implementation of Ogg::File with Vorbis specific methods
+
+ /*!
+ * This is the central class in the Ogg Vorbis metadata processing collection
+ * of classes. It's built upon Ogg::File which handles processing of the Ogg
+ * logical bitstream and breaking it down into pages which are handled by
+ * the codec implementations, in this case Vorbis specifically.
+ */
+
+ class File : public Ogg::File
+ {
+ public:
+ /*!
+ * Contructs a Vorbis 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 XiphComment for this file. XiphComment implements the tag
+ * interface, so this serves as the reimplementation of
+ * TagLib::File::tag().
+ */
+ virtual Ogg::XiphComment *tag() const;
+
+ /*!
+ * Returns the Vorbis::Properties for this file. If no audio properties
+ * were read then this will return a null pointer.
+ */
+ virtual Properties *audioProperties() const;
+
+ virtual void save();
+
+ private:
+ File(const File &);
+ File &operator=(const File &);
+
+ void read(bool readProperties, Properties::ReadStyle propertiesStyle);
+
+ class FilePrivate;
+ FilePrivate *d;
+ };
+ }
+}
+
+#endif
--- /dev/null
+/***************************************************************************
+ copyright : (C) 2003 by Scott Wheeler
+ email : wheeler@kde.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 <oggpageheader.h>
+
+#include "vorbisproperties.h"
+#include "vorbisfile.h"
+
+using namespace TagLib;
+
+class Vorbis::Properties::PropertiesPrivate
+{
+public:
+ PropertiesPrivate(File *f, ReadStyle s) :
+ file(f),
+ style(s),
+ length(0),
+ bitrate(0),
+ sampleRate(0),
+ channels(0),
+ vorbisVersion(0),
+ bitrateMaximum(0),
+ bitrateNominal(0),
+ bitrateMinimum(0) {}
+
+ File *file;
+ ReadStyle style;
+ int length;
+ int bitrate;
+ int sampleRate;
+ int channels;
+ int vorbisVersion;
+ int bitrateMaximum;
+ int bitrateNominal;
+ int bitrateMinimum;
+};
+
+namespace TagLib {
+ /*!
+ * Vorbis headers can be found with one type ID byte and the string "vorbis" in
+ * an Ogg stream. 0x01 indicates the setup header.
+ */
+ static const char vorbisSetupHeaderID[] = { 0x01, 'v', 'o', 'r', 'b', 'i', 's', 0 };
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// public members
+////////////////////////////////////////////////////////////////////////////////
+
+Vorbis::Properties::Properties(File *file, ReadStyle style) : AudioProperties(style)
+{
+ d = new PropertiesPrivate(file, style);
+ read();
+}
+
+Vorbis::Properties::~Properties()
+{
+ delete d;
+}
+
+int Vorbis::Properties::length() const
+{
+ return d->length;
+}
+
+int Vorbis::Properties::bitrate() const
+{
+ return int(float(d->bitrate) / float(1000) + 0.5);
+}
+
+int Vorbis::Properties::sampleRate() const
+{
+ return d->sampleRate;
+}
+
+int Vorbis::Properties::channels() const
+{
+ return d->channels;
+}
+
+int Vorbis::Properties::vorbisVersion() const
+{
+ return d->vorbisVersion;
+}
+
+int Vorbis::Properties::bitrateMaximum() const
+{
+ return d->bitrateMaximum;
+}
+
+int Vorbis::Properties::bitrateNominal() const
+{
+ return d->bitrateNominal;
+}
+
+int Vorbis::Properties::bitrateMinimum() const
+{
+ return d->bitrateMinimum;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// private members
+////////////////////////////////////////////////////////////////////////////////
+
+void Vorbis::Properties::read()
+{
+ // Get the identification header from the Ogg implementation.
+
+ ByteVector data = d->file->packet(0);
+
+ int pos = 0;
+
+ if(data.mid(pos, 7) != vorbisSetupHeaderID) {
+ debug("Vorbis::Properties::read() -- invalid Vorbis identification header");
+ return;
+ }
+
+ pos += 7;
+
+ d->vorbisVersion = data.mid(pos, 4).toUInt(false);
+ pos += 4;
+
+ d->channels = uchar(data[pos]);
+ pos += 1;
+
+ d->sampleRate = data.mid(pos, 4).toUInt(false);
+ pos += 4;
+
+ d->bitrateMaximum = data.mid(pos, 4).toUInt(false);
+ pos += 4;
+
+ d->bitrateNominal = data.mid(pos, 4).toUInt(false);
+ pos += 4;
+
+ d->bitrateMinimum = data.mid(pos, 4).toUInt(false);
+
+ // TODO: Later this should be only the "fast" mode.
+ d->bitrate = d->bitrateNominal;
+
+ // Find the length of the file. See http://wiki.xiph.org/VorbisStreamLength/
+ // for my notes on the topic.
+
+ const Ogg::PageHeader *first = d->file->firstPageHeader();
+ const Ogg::PageHeader *last = d->file->lastPageHeader();
+
+ if(first && last) {
+ long long start = first->absoluteGranularPosition();
+ long long end = last->absoluteGranularPosition();
+
+ if(start >= 0 && end >= 0 && d->sampleRate > 0)
+ d->length = (end - start) / (long long) d->sampleRate;
+ else
+ debug("Vorbis::Properties::read() -- Either the PCM values for the start or "
+ "end of this file was incorrect or the sample rate is zero.");
+ }
+ else
+ debug("Vorbis::Properties::read() -- Could not find valid first and last Ogg pages.");
+}
--- /dev/null
+/***************************************************************************
+ copyright : (C) 2003 by Scott Wheeler
+ email : wheeler@kde.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_VORBISPROPERTIES_H
+#define TAGLIB_VORBISPROPERTIES_H
+
+#include <audioproperties.h>
+
+namespace TagLib {
+
+ namespace Vorbis {
+
+ class File;
+
+ //! An implementation of audio property reading for Ogg Vorbis
+
+ /*!
+ * This reads the data from an Ogg Vorbis stream found in the AudioProperties
+ * API.
+ */
+
+ class Properties : public AudioProperties
+ {
+ public:
+ /*!
+ * Create an instance of Vorbis::Properties with the data read from the
+ * Vorbis::File \a file.
+ */
+ Properties(File *file, ReadStyle style = Average);
+
+ /*!
+ * Destroys this VorbisProperties instance.
+ */
+ virtual ~Properties();
+
+ // Reimplementations.
+
+ virtual int length() const;
+ virtual int bitrate() const;
+ virtual int sampleRate() const;
+ virtual int channels() const;
+
+ /*!
+ * Returns the Vorbis version, currently "0" (as specified by the spec).
+ */
+ int vorbisVersion() const;
+
+ /*!
+ * Returns the maximum bitrate as read from the Vorbis identification
+ * header.
+ */
+ int bitrateMaximum() const;
+
+ /*!
+ * Returns the nominal bitrate as read from the Vorbis identification
+ * header.
+ */
+ int bitrateNominal() const;
+
+ /*!
+ * Returns the minimum bitrate as read from the Vorbis identification
+ * header.
+ */
+ int bitrateMinimum() const;
+
+ private:
+ Properties(const Properties &);
+ Properties &operator=(const Properties &);
+
+ void read();
+
+ class PropertiesPrivate;
+ PropertiesPrivate *d;
+ };
+ }
+}
+
+#endif
--- /dev/null
+/***************************************************************************
+ copyright : (C) 2003 by Scott Wheeler
+ email : wheeler@kde.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 <tdebug.h>
+
+#include <xiphcomment.h>
+
+using namespace TagLib;
+
+class Ogg::XiphComment::XiphCommentPrivate
+{
+public:
+ FieldListMap fieldListMap;
+ String vendorID;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// public members
+////////////////////////////////////////////////////////////////////////////////
+
+Ogg::XiphComment::XiphComment() : TagLib::Tag()
+{
+ d = new XiphCommentPrivate;
+}
+
+Ogg::XiphComment::XiphComment(const ByteVector &data) : TagLib::Tag()
+{
+ d = new XiphCommentPrivate;
+ parse(data);
+}
+
+Ogg::XiphComment::~XiphComment()
+{
+ delete d;
+}
+
+String Ogg::XiphComment::title() const
+{
+ if(d->fieldListMap["TITLE"].isEmpty())
+ return String::null;
+ return d->fieldListMap["TITLE"].front();
+}
+
+String Ogg::XiphComment::artist() const
+{
+ if(d->fieldListMap["ARTIST"].isEmpty())
+ return String::null;
+ return d->fieldListMap["ARTIST"].front();
+}
+
+String Ogg::XiphComment::album() const
+{
+ if(d->fieldListMap["ALBUM"].isEmpty())
+ return String::null;
+ return d->fieldListMap["ALBUM"].front();
+}
+
+String Ogg::XiphComment::comment() const
+{
+ if(d->fieldListMap["DESCRIPTION"].isEmpty())
+ return String::null;
+ return d->fieldListMap["DESCRIPTION"].front();
+}
+
+String Ogg::XiphComment::genre() const
+{
+ if(d->fieldListMap["GENRE"].isEmpty())
+ return String::null;
+ return d->fieldListMap["GENRE"].front();
+}
+
+TagLib::uint Ogg::XiphComment::year() const
+{
+ if(d->fieldListMap["DATE"].isEmpty())
+ return 0;
+ return d->fieldListMap["DATE"].front().toInt();
+}
+
+TagLib::uint Ogg::XiphComment::track() const
+{
+ if(d->fieldListMap["TRACKNUMBER"].isEmpty())
+ return 0;
+ return d->fieldListMap["TRACKNUMBER"].front().toInt();
+}
+
+void Ogg::XiphComment::setTitle(const String &s)
+{
+ addField("TITLE", s);
+}
+
+void Ogg::XiphComment::setArtist(const String &s)
+{
+ addField("ARTIST", s);
+}
+
+void Ogg::XiphComment::setAlbum(const String &s)
+{
+ addField("ALBUM", s);
+}
+
+void Ogg::XiphComment::setComment(const String &s)
+{
+ addField("DESCRIPTION", s);
+}
+
+void Ogg::XiphComment::setGenre(const String &s)
+{
+ addField("GENRE", s);
+}
+
+void Ogg::XiphComment::setYear(uint i)
+{
+ if(i == 0)
+ removeField("DATE");
+ else
+ addField("DATE", String::number(i));
+}
+
+void Ogg::XiphComment::setTrack(uint i)
+{
+ if(i == 0)
+ removeField("TRACKNUMBER");
+ else
+ addField("TRACKNUMBER", String::number(i));
+}
+
+bool Ogg::XiphComment::isEmpty() const
+{
+ FieldListMap::ConstIterator it = d->fieldListMap.begin();
+ for(; it != d->fieldListMap.end(); ++it)
+ if(!(*it).second.isEmpty())
+ return false;
+
+ return true;
+}
+
+TagLib::uint Ogg::XiphComment::fieldCount() const
+{
+ uint count = 0;
+
+ FieldListMap::ConstIterator it = d->fieldListMap.begin();
+ for(; it != d->fieldListMap.end(); ++it)
+ count += (*it).second.size();
+
+ return count;
+}
+
+const Ogg::FieldListMap &Ogg::XiphComment::fieldListMap() const
+{
+ return d->fieldListMap;
+}
+
+String Ogg::XiphComment::vendorID() const
+{
+ return d->vendorID;
+}
+
+void Ogg::XiphComment::addField(const String &key, const String &value, bool replace)
+{
+ if(replace)
+ removeField(key.upper());
+
+ if(!key.isEmpty())
+ d->fieldListMap[key.upper()].append(value);
+}
+
+void Ogg::XiphComment::removeField(const String &key, const String &value)
+{
+ if(!value.isNull()) {
+ StringList::Iterator it = d->fieldListMap[key].begin();
+ for(; it != d->fieldListMap[key].end(); ++it) {
+ if(value == *it)
+ d->fieldListMap[key].erase(it);
+ }
+ }
+ else
+ d->fieldListMap[key].clear();
+}
+
+ByteVector Ogg::XiphComment::render() const
+{
+ ByteVector data;
+
+ // Add the vendor ID length and the vendor ID. It's important to use the
+ // lenght of the data(String::UTF8) rather than the lenght of the the string
+ // since this is UTF8 text and there may be more characters in the data than
+ // in the UTF16 string.
+
+ ByteVector vendorData = d->vendorID.data(String::UTF8);
+
+ data.append(ByteVector::fromUInt(vendorData.size(), false));
+ data.append(vendorData);
+
+ // Add the number of fields.
+
+ data.append(ByteVector::fromUInt(fieldCount(), false));
+
+ // Iterate over the the field lists. Our iterator returns a
+ // std::pair<String, StringList> where the first String is the field name and
+ // the StringList is the values associated with that field.
+
+ FieldListMap::ConstIterator it = d->fieldListMap.begin();
+ for(; it != d->fieldListMap.end(); ++it) {
+
+ // And now iterate over the values of the current list.
+
+ String fieldName = (*it).first;
+ StringList values = (*it).second;
+
+ StringList::ConstIterator valuesIt = values.begin();
+ for(; valuesIt != values.end(); ++valuesIt) {
+ ByteVector fieldData = fieldName.data(String::UTF8);
+ fieldData.append('=');
+ fieldData.append((*valuesIt).data(String::UTF8));
+
+ data.append(ByteVector::fromUInt(fieldData.size(), false));
+ data.append(fieldData);
+ }
+ }
+
+ // Append the "framing bit".
+
+ data.append(char(1));
+
+ return data;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// protected members
+////////////////////////////////////////////////////////////////////////////////
+
+void Ogg::XiphComment::parse(const ByteVector &data)
+{
+ // The first thing in the comment data is the vendor ID length, followed by a
+ // UTF8 string with the vendor ID.
+
+ int pos = 0;
+
+ int vendorLength = data.mid(0, 4).toUInt(false);
+ pos += 4;
+
+ d->vendorID = String(data.mid(pos, vendorLength), String::UTF8);
+ pos += vendorLength;
+
+ // Next the number of fields in the comment vector.
+
+ int commentFields = data.mid(pos, 4).toUInt(false);
+ pos += 4;
+
+ for(int i = 0; i < commentFields; i++) {
+
+ // Each comment field is in the format "KEY=value" in a UTF8 string and has
+ // 4 bytes before the text starts that gives the length.
+
+ int commentLength = data.mid(pos, 4).toUInt(false);
+ pos += 4;
+
+ String comment = String(data.mid(pos, commentLength), String::UTF8);
+ pos += commentLength;
+
+ int commentSeparatorPosition = comment.find("=");
+
+ String key = comment.substr(0, commentSeparatorPosition);
+ String value = comment.substr(commentSeparatorPosition + 1);
+
+ addField(key, value, false);
+ }
+}
--- /dev/null
+/***************************************************************************
+ copyright : (C) 2003 by Scott Wheeler
+ email : wheeler@kde.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_VORBISCOMMENT_H
+#define TAGLIB_VORBISCOMMENT_H
+
+#include <tag.h>
+#include <tlist.h>
+#include <tmap.h>
+#include <tstring.h>
+#include <tstringlist.h>
+#include <tbytevector.h>
+
+namespace TagLib {
+
+ namespace Ogg {
+
+ /*!
+ * A mapping between a list of field names, or keys, and a list of values
+ * associated with that field.
+ *
+ * \see XiphComment::fieldListMap()
+ */
+ typedef Map<String, StringList> FieldListMap;
+
+ //! Ogg Vorbis comment implementation
+
+ /*!
+ * This class is an implementation of the Ogg Vorbis comment specification,
+ * to be found in section 5 of the Ogg Vorbis specification. Because this
+ * format is also used in other (currently unsupported) Xiph.org formats, it
+ * has been made part of a generic implementation rather than being limited
+ * to strictly Vorbis.
+ *
+ * Vorbis comments are a simple vector of keys and values, called fields.
+ * Multiple values for a given key are supported.
+ *
+ * \see fieldListMap()
+ */
+
+ class XiphComment : public TagLib::Tag
+ {
+ public:
+ /*!
+ * Constructs an empty Vorbis comment.
+ */
+ XiphComment();
+
+ /*!
+ * Constructs a Vorbis comment from \a data.
+ */
+ XiphComment(const ByteVector &data);
+
+ /*!
+ * Destroys this instance of the XiphComment.
+ */
+ virtual ~XiphComment();
+
+ 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);
+
+ virtual bool isEmpty() const;
+
+ /*!
+ * Returns the number of fields present in the comment.
+ */
+ uint fieldCount() const;
+
+ /*!
+ * Returns a reference to the map of field lists. Because Xiph comments
+ * support multiple fields with the same key, a pure Map would not work.
+ * As such this is a Map of string lists, keyed on the comment field name.
+ *
+ * The standard set of Xiph/Vorbis fields (which may or may not be
+ * contained in any specific comment) is:
+ *
+ * <ul>
+ * <li>TITLE</li>
+ * <li>VERSION</li>
+ * <li>ALBUM</li>
+ * <li>ARTIST</li>
+ * <li>PERFORMER</li>
+ * <li>COPYRIGHT</li>
+ * <li>ORGANIZATION</li>
+ * <li>DESCRIPTION</li>
+ * <li>GENRE</li>
+ * <li>DATE</li>
+ * <li>LOCATION</li>
+ * <li>CONTACT</li>
+ * <li>ISRC</li>
+ * </ul>
+ *
+ * For a more detailed description of these fields, please see the Ogg
+ * Vorbis specification, section 5.2.2.1.
+ *
+ * \note The Ogg Vorbis comment specification does allow these key values
+ * to be either upper or lower case. However, it is conventional for them
+ * to be upper case. As such, TagLib, when parsing a Xiph/Vorbis comment,
+ * converts all fields to uppercase. When you are using this data
+ * structure, you will need to specify the field name in upper case.
+ *
+ * \warning You should not modify this data structure directly, instead
+ * use addField() and removeField().
+ */
+ const FieldListMap &fieldListMap() const;
+
+ /*!
+ * Returns the vendor ID of the Ogg Vorbis encoder. libvorbis 1.0 as the
+ * most common case always returns "Xiph.Org libVorbis I 20020717".
+ */
+ String vendorID() const;
+
+ /*!
+ * Add the field specified by \a key with the data \a value. If \a replace
+ * is true, then all of the other fields with the same key will be removed
+ * first.
+ *
+ * If the field value is empty, the field will be removed.
+ */
+ void addField(const String &key, const String &value, bool replace = true);
+
+ /*!
+ * Remove the field specified by \a key with the data \a value. If
+ * \a value is null, all of the fields with the given key will be removed.
+ */
+ void removeField(const String &key, const String &value = String::null);
+
+ /*!
+ * Renders the comment to a ByteVector suitable for inserting into a file.
+ */
+ ByteVector render() const;
+
+ protected:
+ /*!
+ * Reads the tag from the file specified in the constructor and fills the
+ * FieldListMap.
+ */
+ void parse(const ByteVector &data);
+
+ private:
+ XiphComment(const XiphComment &);
+ XiphComment &operator=(const XiphComment &);
+
+ class XiphCommentPrivate;
+ XiphCommentPrivate *d;
+ };
+ }
+}
+
+#endif
--- /dev/null
+/***************************************************************************
+ copyright : (C) 2003 by Scott Wheeler
+ email : wheeler@kde.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 "tag.h"
+
+using namespace TagLib;
+
+class Tag::TagPrivate
+{
+
+};
+
+Tag::Tag()
+{
+
+}
+
+Tag::~Tag()
+{
+
+}
+
+bool Tag::isEmpty() const
+{
+ return (title().isEmpty() &&
+ artist().isEmpty() &&
+ album().isEmpty() &&
+ comment().isEmpty() &&
+ genre().isEmpty() &&
+ year() == 0 &&
+ track() == 0);
+}
+
+void Tag::duplicate(const Tag *source, Tag *target, bool overwrite) // static
+{
+ if(overwrite) {
+ target->setTitle(source->title());
+ target->setArtist(source->artist());
+ target->setAlbum(source->album());
+ target->setComment(source->comment());
+ target->setGenre(source->genre());
+ target->setYear(source->year());
+ target->setTrack(source->track());
+ }
+ else {
+ if(target->title().isEmpty())
+ target->setTitle(source->title());
+ if(target->artist().isEmpty())
+ target->setArtist(source->artist());
+ if(target->album().isEmpty())
+ target->setAlbum(source->album());
+ if(target->comment().isEmpty())
+ target->setComment(source->comment());
+ if(target->genre().isEmpty())
+ target->setGenre(source->genre());
+ if(target->year() <= 0)
+ target->setYear(source->year());
+ if(target->track() <= 0)
+ target->setTrack(source->track());
+ }
+}
--- /dev/null
+/***************************************************************************
+ copyright : (C) 2003 by Scott Wheeler
+ email : wheeler@kde.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_TAG_H
+#define TAGLIB_TAG_H
+
+#include <tstring.h>
+
+namespace TagLib {
+
+ //! A simple, generic interface to common audio meta data fields
+
+ /*!
+ * This is an attempt to abstract away the difference in the meta data formats
+ * of various audio codecs and tagging schemes. As such it is generally a
+ * subset of what is available in the specific formats but should be suitable
+ * for most applications. This is meant to complient the generic APIs found
+ * in TagLib::AudioProperties, TagLib::File and TagLib::FileRef.
+ */
+
+ class Tag
+ {
+ public:
+
+ /*!
+ * Detroys this Tag instance.
+ */
+ virtual ~Tag();
+
+ /*!
+ * Returns the track name; if no track name is present in the tag
+ * String::null will be returned.
+ */
+ virtual String title() const = 0;
+
+ /*!
+ * Returns the artist name; if no artist name is present in the tag
+ * String::null will be returned.
+ */
+ virtual String artist() const = 0;
+
+ /*!
+ * Returns the album name; if no album name is present in the tag
+ * String::null will be returned.
+ */
+ virtual String album() const = 0;
+
+ /*!
+ * Returns the track comment; if no comment is present in the tag
+ * String::null will be returned.
+ */
+ virtual String comment() const = 0;
+
+ /*!
+ * Returns the genre name; if no genre is present in the tag String::null
+ * will be returned.
+ */
+ virtual String genre() const = 0;
+
+ /*!
+ * Returns the year; if there is no year set, this will return 0.
+ */
+ virtual uint year() const = 0;
+
+ /*!
+ * Returns the track number; if there is no track number set, this will
+ * return 0.
+ */
+ virtual uint track() const = 0;
+
+ /*!
+ * Sets the title to \a s. If \a s is String::null then this value will be
+ * cleared.
+ */
+ virtual void setTitle(const String &s) = 0;
+
+ /*!
+ * Sets the artist to \a s. If \a s is String::null then this value will be
+ * cleared.
+ */
+ virtual void setArtist(const String &s) = 0;
+
+ /*!
+ * Sets the album to \a s. If \a s is String::null then this value will be
+ * cleared.
+ */
+ virtual void setAlbum(const String &s) = 0;
+
+ /*!
+ * Sets the album to \a s. If \a s is String::null then this value will be
+ * cleared.
+ */
+ virtual void setComment(const String &s) = 0;
+
+ /*!
+ * Sets the genre to \a s. If \a s is String::null then this value will be
+ * cleared. For tag formats that use a fixed set of genres, the appropriate
+ * value will be selected based on a string comparison. A list of available
+ * genres for those formats should be available in that type's
+ * implementation.
+ */
+ virtual void setGenre(const String &s) = 0;
+
+ /*!
+ * Sets the year to \a i. If \a s is 0 then this value will be cleared.
+ */
+ virtual void setYear(uint i) = 0;
+
+ /*!
+ * Sets the track to \a i. If \a s is 0 then this value will be cleared.
+ */
+ virtual void setTrack(uint i) = 0;
+
+ /*!
+ * Returns true if the tag does not contain any data. This should be
+ * reimplemented in subclasses that provide more than the basic tagging
+ * abilities in this class.
+ */
+ virtual bool isEmpty() const;
+
+ /*!
+ * Copies the generic data from one tag to another.
+ *
+ * \note This will no affect any of the lower level details of the tag. For
+ * instance if any of the tag type specific data (maybe a URL for a band) is
+ * set, this will not modify or copy that. This just copies using the API
+ * in this class.
+ *
+ * If \a overwrite is true then the values will be unconditionally copied.
+ * If false only empty values will be overwritten.
+ */
+ static void duplicate(const Tag *source, Tag *target, bool overwrite = true);
+
+ protected:
+ /*!
+ * Construct a Tag. This is protected since tags should only be instantiated
+ * through subclasses.
+ */
+ Tag();
+
+ private:
+ Tag(const Tag &);
+ Tag &operator=(const Tag &);
+
+ class TagPrivate;
+ TagPrivate *d;
+ };
+}
+
+#endif
--- /dev/null
+#!/bin/sh
+
+usage()
+{
+ echo "usage: $0 [OPTIONS]"
+cat << EOH
+
+options:
+ [--libs]
+ [--cflags]
+ [--version]
+ [--prefix]
+EOH
+ exit 1;
+}
+
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+
+flags=""
+
+if test $# -eq 0 ; then
+ usage
+fi
+
+while test $# -gt 0
+do
+ case $1 in
+ --libs)
+ flags="$flags -L$libdir -ltag"
+ ;;
+ --cflags)
+ flags="$flags -I$includedir/taglib"
+ ;;
+ --version)
+ echo 1.0
+ ;;
+ --prefix)
+ echo $prefix
+ ;;
+ *)
+ echo "$0: unknown option $1"
+ echo
+ usage
+ ;;
+ esac
+ shift
+done
+
+if test -n "$flags"
+then
+ echo $flags
+fi
--- /dev/null
+INCLUDES = \
+ -I$(top_srcdir)/taglib\
+ -I$(top_srcdir)/taglib/toolkit \
+ -I$(top_srcdir)/taglib/mpeg/id3v2
+
+LDADD = ../libtag.la
+
+check_PROGRAMS = toolkit-test
+
+toolkit_test_SOURCES = toolkit-test.cpp
--- /dev/null
+INCLUDES = $(all_includes)
+
+noinst_LTLIBRARIES = libtoolkit.la
+
+libtoolkit_la_SOURCES = \
+ tstring.cpp tstringlist.cpp tbytevector.cpp \
+ tbytevectorlist.cpp tfile.cpp tdebug.cpp unicode.cpp
+
+taglib_include_HEADERS = \
+ taglib.h tstring.h tlist.h tlist.tcc tstringlist.h \
+ tbytevector.h tbytevectorlist.h tfile.h tdebug.h \
+ tmap.h tmap.tcc
+
+taglib_includedir = $(includedir)/taglib
+
+EXTRA_DIST = $(libtoolkit_la_SOURCES) $(taglib_include_HEADERS)
--- /dev/null
+/***************************************************************************
+ copyright : (C) 2002 by Scott Wheeler
+ email : wheeler@kde.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_H
+#define TAGLIB_H
+
+#define TAGLIB_MAJOR_VERSION 0
+#define TAGLIB_MINOR_VERSION 96
+
+#include <string>
+
+//! A namespace for all TagLib related classes and functions
+
+/*!
+ * This namespace contains everything in TagLib. For projects working with
+ * TagLib extensively it may be conveniten to add a
+ * \code
+ * using namespace TagLib;
+ * \endcode
+ */
+
+namespace TagLib {
+
+ class String;
+
+ typedef wchar_t wchar;
+ typedef unsigned char uchar;
+ typedef unsigned int uint;
+ typedef unsigned long ulong;
+
+ /*!
+ * Unfortunately std::wstring isn't defined on some systems, (i.e. GCC < 3)
+ * so I'm providing something here that should be constant.
+ */
+ typedef std::basic_string<wchar> wstring;
+
+#ifndef DO_NOT_DOCUMENT // Tell Doxygen to skip this class.
+ /*!
+ * \internal
+ * This is just used as a base class for shared classes in TagLib.
+ *
+ * \warning This <b>is not</b> part of the TagLib public API!
+ */
+
+ class RefCounter
+ {
+ public:
+ RefCounter() : refCount(1) {}
+ void ref() { refCount++; }
+ bool deref() { return ! --refCount ; }
+ int count() { return refCount; }
+ private:
+ uint refCount;
+ };
+
+ /*!
+ * A simple strdup implementation since the standard one creates some wierdness
+ * with delete.
+ */
+ static inline char *strdup(const char *s)
+ {
+ const int l = ::strlen(s);
+ char *buffer = new char[l];
+ ::memcpy(buffer, s, l);
+ return buffer;
+ }
+
+#endif // DO_NOT_DOCUMENT
+
+}
+
+/*!
+ * \mainpage TagLib
+ * \section intro Introduction
+ * TagLib, is well, a library for reading and editing audio meta data, commonly know as \e tags.
+ *
+ * Some goals of TagLib:
+ * - A clean, high level, C++ API to handling audio meta data.
+ * - Support for at least ID3v1, ID3v2 and Ogg Vorbis \e comments.
+ * - A generic, \e simple API for the most common tagging related functions.
+ * - Binary compatibility between minor releases using the standard KDE/Qt techniques for C++ binary compatibility.
+ * - Make the tagging framework extensible by library users; i.e. it will be possible for libarary users to implement
+ * additional ID3v2 frames, without modifying the TagLib source (through the use of <i>Abstract Factories</i> and
+ * such.
+ *
+ * Because TagLib desires to be toolkit agnostic, in hope of being widely adopted and the most flexible in licensing
+ * TagLib provides many of its own toolkit classes; in fact the only external dependancy that TagLib has, it a
+ * semi-sane STL implementation.
+ *
+ * \section why Why TagLib?
+ *
+ * TagLib was written to fill a gap in the Open Source/Free Software community. Currently there is a lack in the
+ * OSS/FS for a homogenous API to the most common music types. In fact the only semi-portable implementation of the
+ * ID3v2 standard available on Linux is id3lib, which unfortunately is poorly written, poorly documented and which
+ * cycles through maintainers at least once a year (I took my turn some time ago.).
+ *
+ * As TagLib will be initially injected into the KDE community, while I am not linking to any of the KDE or Qt libraries
+ * I have tried to follow the coding style of those libraries. Again, this is in sharp contrast to id3lib, which
+ * basically provides a hybrid C/C++ API and uses a dubious object model.
+ *
+ * I get asked rather frequently why I am replacing id3lib (mostly by people that have never worked with id3lib), if
+ * you are concerned about this please email me; I can provide my lengthy standard rant. :-)
+ *
+ * \section examples Examples:
+ *
+ * I've talked a lot about the \e homogenous API to common music formats. Here's an example of how things (will) work:
+ *
+ * \code
+ *
+ * TagLib::FileRef f("Latex Solar Beef.mp3");
+ * TagLib::String artist = f.tag()->artist(); // artist == "Frank Zappa"
+ *
+ * f.tag()->setAlbum("Fillmore East");
+ * f.save();
+ *
+ * TagLib::FileRef g("Free City Rhymes.ogg");
+ * TagLib::String album = g.tag()->album(); // album == "NYC Ghosts & Flowers"
+ *
+ * g.tag()->setTrack(1);
+ * g.save();
+ *
+ * \endcode
+ *
+ * Notice that these high level functions work for both Ogg \e and MP3. For this high level API, which is suitable for
+ * most applications, the differences between ID3v2, ID3v1, MPEG and Ogg Vorbis can all be ignored.
+ *
+ * \author Scott Wheeler <wheeler@kde.org>
+ */
+
+#endif
--- /dev/null
+/***************************************************************************
+ copyright : (C) 2002, 2003 by Scott Wheeler
+ email : wheeler@kde.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 <iostream>
+
+#include <tstring.h>
+#include <tdebug.h>
+
+#include "tbytevector.h"
+
+namespace TagLib {
+ static const uint crcTable[256] = {
+ 0x00000000, 0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b,
+ 0x1a864db2, 0x1e475005, 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, 0x2b4bcb61,
+ 0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd, 0x4c11db70, 0x48d0c6c7,
+ 0x4593e01e, 0x4152fda9, 0x5f15adac, 0x5bd4b01b, 0x569796c2, 0x52568b75,
+ 0x6a1936c8, 0x6ed82b7f, 0x639b0da6, 0x675a1011, 0x791d4014, 0x7ddc5da3,
+ 0x709f7b7a, 0x745e66cd, 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039,
+ 0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5, 0xbe2b5b58, 0xbaea46ef,
+ 0xb7a96036, 0xb3687d81, 0xad2f2d84, 0xa9ee3033, 0xa4ad16ea, 0xa06c0b5d,
+ 0xd4326d90, 0xd0f37027, 0xddb056fe, 0xd9714b49, 0xc7361b4c, 0xc3f706fb,
+ 0xceb42022, 0xca753d95, 0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1,
+ 0xe13ef6f4, 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d, 0x34867077, 0x30476dc0,
+ 0x3d044b19, 0x39c556ae, 0x278206ab, 0x23431b1c, 0x2e003dc5, 0x2ac12072,
+ 0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16, 0x018aeb13, 0x054bf6a4,
+ 0x0808d07d, 0x0cc9cdca, 0x7897ab07, 0x7c56b6b0, 0x71159069, 0x75d48dde,
+ 0x6b93dddb, 0x6f52c06c, 0x6211e6b5, 0x66d0fb02, 0x5e9f46bf, 0x5a5e5b08,
+ 0x571d7dd1, 0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba,
+ 0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, 0xbfa1b04b, 0xbb60adfc,
+ 0xb6238b25, 0xb2e29692, 0x8aad2b2f, 0x8e6c3698, 0x832f1041, 0x87ee0df6,
+ 0x99a95df3, 0x9d684044, 0x902b669d, 0x94ea7b2a, 0xe0b41de7, 0xe4750050,
+ 0xe9362689, 0xedf73b3e, 0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2,
+ 0xc6bcf05f, 0xc27dede8, 0xcf3ecb31, 0xcbffd686, 0xd5b88683, 0xd1799b34,
+ 0xdc3abded, 0xd8fba05a, 0x690ce0ee, 0x6dcdfd59, 0x608edb80, 0x644fc637,
+ 0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb, 0x4f040d56, 0x4bc510e1,
+ 0x46863638, 0x42472b8f, 0x5c007b8a, 0x58c1663d, 0x558240e4, 0x51435d53,
+ 0x251d3b9e, 0x21dc2629, 0x2c9f00f0, 0x285e1d47, 0x36194d42, 0x32d850f5,
+ 0x3f9b762c, 0x3b5a6b9b, 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff,
+ 0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623, 0xf12f560e, 0xf5ee4bb9,
+ 0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2, 0xe6ea3d65, 0xeba91bbc, 0xef68060b,
+ 0xd727bbb6, 0xd3e6a601, 0xdea580d8, 0xda649d6f, 0xc423cd6a, 0xc0e2d0dd,
+ 0xcda1f604, 0xc960ebb3, 0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7,
+ 0xae3afba2, 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b, 0x9b3660c6, 0x9ff77d71,
+ 0x92b45ba8, 0x9675461f, 0x8832161a, 0x8cf30bad, 0x81b02d74, 0x857130c3,
+ 0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640, 0x4e8ee645, 0x4a4ffbf2,
+ 0x470cdd2b, 0x43cdc09c, 0x7b827d21, 0x7f436096, 0x7200464f, 0x76c15bf8,
+ 0x68860bfd, 0x6c47164a, 0x61043093, 0x65c52d24, 0x119b4be9, 0x155a565e,
+ 0x18197087, 0x1cd86d30, 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec,
+ 0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, 0x2497d08d, 0x2056cd3a,
+ 0x2d15ebe3, 0x29d4f654, 0xc5a92679, 0xc1683bce, 0xcc2b1d17, 0xc8ea00a0,
+ 0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, 0xdbee767c, 0xe3a1cbc1, 0xe760d676,
+ 0xea23f0af, 0xeee2ed18, 0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4,
+ 0x89b8fd09, 0x8d79e0be, 0x803ac667, 0x84fbdbd0, 0x9abc8bd5, 0x9e7d9662,
+ 0x933eb0bb, 0x97ffad0c, 0xafb010b1, 0xab710d06, 0xa6322bdf, 0xa2f33668,
+ 0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4
+ };
+
+ /*!
+ * A templatized find that works both with a ByteVector and a ByteVectorMirror.
+ */
+
+ template <class Vector>
+ int vectorFind(const Vector &v, const Vector &pattern, uint offset, int byteAlign)
+ {
+ if(pattern.size() > v.size() || offset >= v.size() - 1)
+ return -1;
+
+ // if an offset was specified, just do a recursive call on the substring
+
+ if(offset > 0) {
+
+ // start at the next byte aligned block
+
+ Vector section = v.mid(offset + byteAlign - 1 - offset % byteAlign);
+ int match = section.find(pattern, 0, byteAlign);
+ return match >= 0 ? int(match + offset) : -1;
+ }
+
+ // this is a simplified Boyer-Moore string searching algorithm
+
+ uchar lastOccurrence[256];
+
+
+ for(uint i = 0; i < 256; ++i)
+ lastOccurrence[i] = uchar(pattern.size());
+
+ for(uint i = 0; i < pattern.size() - 1; ++i)
+ lastOccurrence[unsigned(pattern[i])] = uchar(pattern.size() - i - 1);
+
+ for(uint i = pattern.size() - 1; i < v.size(); i += lastOccurrence[uchar(v.at(i))]) {
+ int iBuffer = i;
+ int iPattern = pattern.size() - 1;
+
+ while(iPattern >= 0 && v.at(iBuffer) == pattern[iPattern]) {
+ --iBuffer;
+ --iPattern;
+ }
+
+ if(-1 == iPattern && (iBuffer + 1) % byteAlign == 0)
+ return iBuffer + 1;
+ }
+
+ return -1;
+ }
+
+ /*!
+ * Wraps the accessors to a ByteVector to make the search algorithm access the
+ * elements in reverse.
+ *
+ * \see vectorFind()
+ * \see ByteVector::rfind()
+ */
+
+ class ByteVectorMirror
+ {
+ public:
+ ByteVectorMirror(const ByteVector &source) : v(source) {}
+ const char operator[](int index) const
+ {
+ return v[v.size() - index - 1];
+ }
+
+ const char at(int index) const
+ {
+ return v.at(v.size() - index - 1);
+ }
+
+ ByteVectorMirror mid(uint index, uint length = 0xffffffff) const
+ {
+ return length == 0xffffffff ? v.mid(0, index) : v.mid(index - length, length);
+ }
+
+ uint size() const
+ {
+ return v.size();
+ }
+
+ int find(const ByteVectorMirror &pattern, uint offset = 0, int byteAlign = 1) const
+ {
+ ByteVectorMirror v(*this);
+
+ const int pos = vectorFind<ByteVectorMirror>(v, pattern, offset, byteAlign);
+
+ // If the offset is zero then we need to adjust the location in the search
+ // to be appropriately reversed. If not we need to account for the fact
+ // that the recursive call (called from the above line) has already ajusted
+ // for this but that the normal templatized find above will add the offset
+ // to the returned value.
+ //
+ // This is a little confusing at first if you don't first stop to think
+ // through the logic involved in the forward search.
+
+ if(pos == -1)
+ return -1;
+
+ if(offset == 0)
+ return size() - pos - pattern.size();
+ else
+ return pos - offset;
+ }
+
+ private:
+ const ByteVector v;
+ };
+}
+
+using namespace TagLib;
+
+class ByteVector::ByteVectorPrivate : public RefCounter
+{
+public:
+ ByteVectorPrivate() : RefCounter() {}
+ ByteVectorPrivate(const std::vector<char> &v) : RefCounter(), data(v) {}
+ ByteVectorPrivate(TagLib::uint size, char value) : RefCounter(), data(size, value) {}
+
+ std::vector<char> data;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// static members
+////////////////////////////////////////////////////////////////////////////////
+
+ByteVector ByteVector::null;
+
+ByteVector ByteVector::fromCString(const char *s, uint length)
+{
+ ByteVector v;
+
+ if(length == 0xffffffff)
+ v.setData(s);
+ else
+ v.setData(s, length);
+
+ return v;
+}
+
+ByteVector ByteVector::fromUInt(uint value, bool mostSignificantByteFirst)
+{
+ ByteVector v(4, 0);
+
+ for(int i = 0; i < 4; i++)
+ v[i] = uchar(value >> ((mostSignificantByteFirst ? 3 - i : i) * 8) & 0xff);
+
+ return v;
+}
+
+ByteVector ByteVector::fromLongLong(long long value, bool mostSignificantByteFirst)
+{
+ ByteVector v(8, 0);
+
+ for(int i = 0; i < 8; i++)
+ v[i] = uchar(value >> ((mostSignificantByteFirst ? 7 - i : i) * 8) & 0xff);
+
+ return v;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// public members
+////////////////////////////////////////////////////////////////////////////////
+
+ByteVector::ByteVector()
+{
+ d = new ByteVectorPrivate;
+}
+
+ByteVector::ByteVector(uint size, char value)
+{
+ d = new ByteVectorPrivate(size, value);
+}
+
+ByteVector::ByteVector(const ByteVector &v) : d(v.d)
+{
+ d->ref();
+}
+
+ByteVector::ByteVector(char c)
+{
+ d = new ByteVectorPrivate;
+ d->data.push_back(c);
+}
+
+ByteVector::ByteVector(const char *data, uint length)
+{
+ d = new ByteVectorPrivate;
+ setData(data, length);
+}
+
+ByteVector::ByteVector(const char *data)
+{
+ d = new ByteVectorPrivate;
+ setData(data);
+}
+
+ByteVector::~ByteVector()
+{
+ if(d->deref())
+ delete d;
+}
+
+void ByteVector::setData(const char *data, uint length)
+{
+ detach();
+
+ for(uint i = 0; i < length; i++)
+ d->data.push_back(data[i]);
+}
+
+void ByteVector::setData(const char *data)
+{
+ detach();
+
+ for(uint i = 0; data[i] != 0; i++)
+ d->data.push_back(data[i]);
+}
+
+char *ByteVector::data()
+{
+ // A rather obscure feature of the C++ spec that I hadn't thought of that makes
+ // working with C libs much more effecient. There's more here:
+ //
+ // http://www.informit.com/isapi/product_id~{9C84DAB4-FE6E-49C5-BB0A-FB50331233EA}/content/index.asp
+
+ detach();
+ return &(d->data[0]);
+}
+
+const char *ByteVector::data() const
+{
+ return &(d->data[0]);
+}
+
+ByteVector ByteVector::mid(uint index, uint length) const
+{
+ ByteVector v;
+
+ ConstIterator endIt;
+
+ if(length < 0xffffffff && length + index < size())
+ endIt = d->data.begin() + index + length;
+ else
+ endIt = d->data.end();
+
+ v.d->data.insert(v.d->data.begin(), ConstIterator(d->data.begin() + index), endIt);
+
+ return v;
+}
+
+char ByteVector::at(uint index) const
+{
+ return index < size() ? d->data[index] : 0;
+}
+
+int ByteVector::find(const ByteVector &pattern, uint offset, int byteAlign) const
+{
+ return vectorFind<ByteVector>(*this, pattern, offset, byteAlign);
+}
+
+int ByteVector::rfind(const ByteVector &pattern, uint offset, int byteAlign) const
+{
+ // Ok, this is a little goofy, but pretty cool after it sinks in. Instead of
+ // reversing the find method's Boyer-Moore search algorithm I created a "mirror"
+ // for a ByteVector to reverse the behavior of the accessors.
+
+ ByteVectorMirror v(*this);
+ ByteVectorMirror p(pattern);
+
+ return v.find(p, offset, byteAlign);
+}
+
+bool ByteVector::containsAt(const ByteVector &pattern, uint offset, uint patternOffset, uint patternLength) const
+{
+ if(pattern.size() < patternLength)
+ patternLength = pattern.size();
+
+ // do some sanity checking -- all of these things are needed for the search to be valid
+
+ if(patternLength > size() || offset >= size() || patternOffset >= pattern.size() || patternLength == 0)
+ return false;
+
+ // loop through looking for a mismatch
+
+ for(uint i = 0; i < patternLength - patternOffset; i++) {
+ if(at(i + offset) != pattern[i + patternOffset])
+ return false;
+ }
+
+ return true;
+}
+
+bool ByteVector::startsWith(const ByteVector &pattern) const
+{
+ return containsAt(pattern, 0);
+}
+
+bool ByteVector::endsWith(const ByteVector &pattern) const
+{
+ return containsAt(pattern, size() - pattern.size());
+}
+
+int ByteVector::endsWithPartialMatch(const ByteVector &pattern) const
+{
+ if(pattern.size() > size())
+ return -1;
+
+ const int startIndex = size() - pattern.size();
+
+ // try to match the last n-1 bytes from the vector (where n is the pattern
+ // size) -- continue trying to match n-2, n-3...1 bytes
+
+ for(uint i = 1; i < pattern.size(); i++) {
+ if(containsAt(pattern, startIndex + i, 0, pattern.size() - i))
+ return startIndex + i;
+ }
+
+ return -1;
+}
+
+void ByteVector::append(const ByteVector &v)
+{
+ detach();
+
+ for(uint i = 0; i < v.size(); i++)
+ d->data.push_back(v[i]);
+}
+
+void ByteVector::clear()
+{
+ detach();
+ d->data.clear();
+}
+
+TagLib::uint ByteVector::size() const
+{
+ return d->data.size();
+}
+
+ByteVector &ByteVector::resize(uint size, char padding)
+{
+ if(d->data.size() < size) {
+ d->data.reserve(size);
+ d->data.insert(d->data.end(), size - d->data.size(), padding);
+ }
+ else
+ d->data.erase(d->data.begin() + size, d->data.end());
+
+ return *this;
+}
+
+ByteVector::Iterator ByteVector::begin()
+{
+ return d->data.begin();
+}
+
+ByteVector::ConstIterator ByteVector::begin() const
+{
+ return d->data.begin();
+}
+
+ByteVector::Iterator ByteVector::end()
+{
+ return d->data.end();
+}
+
+ByteVector::ConstIterator ByteVector::end() const
+{
+ return d->data.end();
+}
+
+bool ByteVector::isNull() const
+{
+ return d == null.d;
+}
+
+bool ByteVector::isEmpty() const
+{
+ return d->data.size() == 0;
+}
+
+TagLib::uint ByteVector::checksum() const
+{
+ uint sum = 0;
+ for(ByteVector::ConstIterator it = begin(); it != end(); ++it)
+ sum = (sum << 8) ^ crcTable[((sum >> 24) & 0xff) ^ uchar(*it)];
+ return sum;
+}
+
+TagLib::uint ByteVector::toUInt(bool mostSignificantByteFirst) const
+{
+ uint sum = 0;
+ int last = d->data.size() > 4 ? 3 : d->data.size() - 1;
+
+ for(int i = 0; i <= last; i++)
+ sum |= uchar(d->data[i]) << ((mostSignificantByteFirst ? last - i : i) * 8);
+
+ return sum;
+}
+
+long long ByteVector::toLongLong(bool mostSignificantByteFirst) const
+{
+ // Just do all of the bit operations on the unsigned value and use an implicit
+ // cast on the way out.
+
+ unsigned long long sum = 0;
+ int last = d->data.size() > 8 ? 7 : d->data.size() - 1;
+
+ for(int i = 0; i <= last; i++)
+ sum |= (unsigned long long) uchar(d->data[i]) << ((mostSignificantByteFirst ? last - i : i) * 8);
+
+ return sum;
+}
+
+const char &ByteVector::operator[](int index) const
+{
+ return d->data[index];
+}
+
+char &ByteVector::operator[](int index)
+{
+ detach();
+
+ return d->data[index];
+}
+
+bool ByteVector::operator==(const ByteVector &v) const
+{
+ if(size() != v.size())
+ return false;
+
+ for(uint i = 0; i < size(); i++) {
+ if(at(i) != v.at(i))
+ return false;
+ }
+
+ return true;
+}
+
+bool ByteVector::operator!=(const ByteVector &v) const
+{
+ return !operator==(v);
+}
+
+bool ByteVector::operator==(const char *s) const
+{
+ return operator==(fromCString(s));
+}
+
+bool ByteVector::operator!=(const char *s) const
+{
+ return !operator==(s);
+}
+
+bool ByteVector::operator<(const ByteVector &v) const
+{
+ for(uint i = 0; i < size() && i < v.size(); i++) {
+ if(at(i) < v.at(i))
+ return true;
+ else if(at(i) > v.at(i))
+ return false;
+ }
+
+ return size() < v.size();
+}
+
+bool ByteVector::operator>(const ByteVector &v) const
+{
+ return !operator<(v);
+}
+
+ByteVector ByteVector::operator+(const ByteVector &v) const
+{
+ ByteVector sum(*this);
+ sum.append(v);
+ return sum;
+}
+
+ByteVector &ByteVector::operator=(const ByteVector &v)
+{
+ if(&v == this)
+ return *this;
+
+ if(d->deref())
+ delete d;
+
+ d = v.d;
+ d->ref();
+ return *this;
+}
+
+ByteVector &ByteVector::operator=(char c)
+{
+ if(d->deref())
+ delete d;
+ *this = ByteVector(c);
+ return *this;
+}
+
+ByteVector &ByteVector::operator=(const char *data)
+{
+ if(d->deref())
+ delete d;
+ *this = ByteVector(data);
+ return *this;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// protected members
+////////////////////////////////////////////////////////////////////////////////
+
+void ByteVector::detach()
+{
+ if(d->count() > 1) {
+ d->deref();
+ d = new ByteVectorPrivate(d->data);
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// related functions
+////////////////////////////////////////////////////////////////////////////////
+
+std::ostream &operator<<(std::ostream &s, const ByteVector &v)
+{
+ for(TagLib::uint i = 0; i < v.size(); i++)
+ s << v[i];
+ return s;
+}
--- /dev/null
+/***************************************************************************
+ copyright : (C) 2002, 2003 by Scott Wheeler
+ email : wheeler@kde.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_BYTEVECTOR_H
+#define TAGLIB_BYTEVECTOR_H
+
+#include "taglib.h"
+
+#include <vector>
+
+namespace TagLib {
+
+ //! A byte vector
+
+ /*!
+ * This class provides a byte vector with some methods that are useful for
+ * tagging purposes. Many of the search functions are tailored to what is
+ * useful for finding tag related paterns in a data array.
+ */
+
+ class ByteVector
+ {
+ public:
+#ifndef DO_NOT_DOCUMENT
+ typedef std::vector<char>::iterator Iterator;
+ typedef std::vector<char>::const_iterator ConstIterator;
+#endif
+
+ /*!
+ * Constructs an empty byte vector.
+ */
+ ByteVector();
+
+ /*!
+ * Construct a vector of size \a size with all values set to \a value by
+ * default.
+ */
+ ByteVector(uint size, char value = 0);
+
+ /*!
+ * Contructs a byte vector that is a copy of \a v.
+ */
+ ByteVector(const ByteVector &v);
+
+ /*!
+ * Contructs a byte vector that contains \a c.
+ */
+ ByteVector(char c);
+
+ /*!
+ * Constructs a byte vector that copies \a data for up to \a length bytes.
+ */
+ ByteVector(const char *data, uint length);
+
+ /*!
+ * Constructs a byte vector that copies \a data up to the first null
+ * byte. The behavior is undefined if \a data is not null terminated.
+ * This is particularly useful for constructing byte arrays from string
+ * constants.
+ */
+ ByteVector(const char *data);
+
+ /*!
+ * Destroys this ByteVector instance.
+ */
+ virtual ~ByteVector();
+
+ /*!
+ * Sets the data for the byte array using the first \a length bytes of \a data
+ */
+ void setData(const char *data, uint length);
+
+ /*!
+ * Sets the data for the byte array copies \a data up to the first null
+ * byte. The behavior is undefined if \a data is not null terminated.
+ */
+ void setData(const char *data);
+
+ /*!
+ * Returns a pointer to the internal data structure.
+ *
+ * \warning Care should be taken when modifying this data structure as it is
+ * easy to corrupt the ByteVector when doing so. Specifically, while the
+ * data may be changed, its length may not be.
+ */
+ char *data();
+
+ /*!
+ * Returns a pointer to the internal data structure which may not be modified.
+ */
+ const char *data() const;
+
+ /*!
+ * Returns a byte vector made up of the bytes starting at \a index and
+ * for \a length bytes. If \a length is not specified it will return the bytes
+ * from \a index to the end of the vector.
+ */
+ ByteVector mid(uint index, uint length = 0xffffffff) const;
+
+ /*!
+ * This essentially performs the same as operator[](), but instead of causing
+ * a runtime error if the index is out of bounds, it will return a null byte.
+ */
+ char at(uint index) const;
+
+ /*!
+ * Searches the ByteVector for \a pattern starting at \a offset and returns
+ * the offset. Returns -1 if the pattern was not found. If \a byteAlign is
+ * specified the pattern will only be matched if it starts on a byteDivisible
+ * by \a byteAlign.
+ */
+ int find(const ByteVector &pattern, uint offset = 0, int byteAlign = 1) const;
+
+ /*!
+ * Searches the ByteVector for \a pattern starting from either the end of the
+ * vector or \a offset and returns the offset. Returns -1 if the pattern was
+ * not found. If \a byteAlign is specified the pattern will only be matched
+ * if it starts on a byteDivisible by \a byteAlign.
+ */
+ int rfind(const ByteVector &pattern, uint offset = 0, int byteAlign = 1) const;
+
+ /*!
+ * Checks to see if the vector contains the \a pattern starting at position
+ * \a offset. Optionally, if you only want to search for part of the pattern
+ * you can specify an offset within the pattern to start from. Also, you can
+ * specify to only check for the first \a patternLength bytes of \a pattern with
+ * the \a patternLength argument.
+ */
+ bool containsAt(const ByteVector &pattern, uint offset, uint patternOffset = 0, uint patternLength = 0xffffffff) const;
+
+ /*!
+ * Returns true if the vector starts with \a pattern.
+ */
+ bool startsWith(const ByteVector &pattern) const;
+
+ /*!
+ * Returns true if the vector ends with \a pattern.
+ */
+ bool endsWith(const ByteVector &pattern) const;
+
+ /*!
+ * Checks for a partial match of \a pattern at the end of the vector. It
+ * returns the offset of the partial match within the vector, or -1 if the
+ * pattern is not found. This method is particularly useful when searching for
+ * patterns that start in one vector and end in another. When combined with
+ * startsWith() it can be used to find a pattern that overlaps two buffers.
+ *
+ * \note This will not match the complete pattern at the end of the string; use
+ * endsWith() for that.
+ */
+ int endsWithPartialMatch(const ByteVector &pattern) const;
+
+ /*!
+ * Appends \a v to the end of the ByteVector.
+ */
+ void append(const ByteVector &v);
+
+ /*!
+ * Clears the data.
+ */
+ void clear();
+
+ /*!
+ * Returns the size of the array.
+ */
+ uint size() const;
+
+ /*!
+ * Resize the vector to \a size. If the vector is currently less than
+ * \a size, pad the remaining spaces with \a padding. Returns a reference
+ * to the resized vector.
+ */
+ ByteVector &resize(uint size, char padding = 0);
+
+ /*!
+ * Returns an Iterator that points to the front of the vector.
+ */
+ Iterator begin();
+
+ /*!
+ * Returns a ConstIterator that points to the front of the vector.
+ */
+ ConstIterator begin() const;
+
+ /*!
+ * Returns an Iterator that points to the back of the vector.
+ */
+ Iterator end();
+
+ /*!
+ * Returns a ConstIterator that points to the back of the vector.
+ */
+ ConstIterator end() const;
+
+ /*!
+ * Returns true if the vector is null.
+ *
+ * \note A vector may be empty without being null.
+ * \see isEmpty()
+ */
+ bool isNull() const;
+
+ /*!
+ * Returns true if the ByteVector is empty.
+ *
+ * \see size()
+ * \see isNull()
+ */
+ bool isEmpty() const;
+
+ /*!
+ * Returns a CRC checksum of the byte vector's data.
+ */
+ uint checksum() const;
+
+ /*!
+ * Converts the first 4 bytes of the vector to an unsigned integer.
+ *
+ * If \a mostSignificantByteFirst is true this will operate left to right
+ * evaluating the integer. For example if \a mostSignificantByteFirst is
+ * true then $00 $00 $00 $01 == 0x00000001 == 1, if false, $01 00 00 00 ==
+ * 0x01000000 == 1.
+ *
+ * \see fromUInt()
+ */
+ uint toUInt(bool mostSignificantByteFirst = true) const;
+
+
+ /*!
+ * Converts the first 8 bytes of the vector to a (signed) long long.
+ *
+ * If \a mostSignificantByteFirst is true this will operate left to right
+ * evaluating the integer. For example if \a mostSignificantByteFirst is
+ * true then $00 00 00 00 00 00 00 01 == 0x0000000000000001 == 1,
+ * if false, $01 00 00 00 00 00 00 00 == 0x0100000000000000 == 1.
+ *
+ * \see fromUInt()
+ */
+ long long toLongLong(bool mostSignificantByteFirst = true) const;
+
+ /*!
+ * Creates a 4 byte ByteVector based on \a value. If
+ * \a mostSignificantByteFirst is true, then this will operate left to right
+ * in building the ByteVector. For example if \a mostSignificantByteFirst is
+ * true then $00 00 00 01 == 0x00000001 == 1, if false, $01 00 00 00 ==
+ * 0x01000000 == 1.
+ *
+ * \see toUInt()
+ */
+ static ByteVector fromUInt(uint value, bool mostSignificantByteFirst = true);
+
+ /*!
+ * Creates a 8 byte ByteVector based on \a value. If
+ * \a mostSignificantByteFirst is true, then this will operate left to right
+ * in building the ByteVector. For example if \a mostSignificantByteFirst is
+ * true then $00 00 00 01 == 0x0000000000000001 == 1, if false,
+ * $01 00 00 00 00 00 00 00 == 0x0100000000000000 == 1.
+ *
+ * \see toLongLong()
+ */
+ static ByteVector fromLongLong(long long value, bool mostSignificantByteFirst = true);
+
+ /*!
+ * Returns a ByteVector based on the CString \a s.
+ */
+ static ByteVector fromCString(const char *s, uint length = 0xffffffff);
+
+ /*!
+ * Returns a const refernence to the byte at \a index.
+ */
+ const char &operator[](int index) const;
+
+ /*!
+ * Returns a reference to the byte at \a index.
+ */
+ char &operator[](int index);
+
+ /*!
+ * Returns true if this ByteVector and \a v are equal.
+ */
+ bool operator==(const ByteVector &v) const;
+
+ /*!
+ * Returns true if this ByteVector and \a v are not equal.
+ */
+ bool operator!=(const ByteVector &v) const;
+
+ /*!
+ * Returns true if this ByteVector and the null terminated C string \a s
+ * contain the same data.
+ */
+ bool operator==(const char *s) const;
+
+ /*!
+ * Returns true if this ByteVector and the null terminated C string \a s
+ * do not contain the same data.
+ */
+ bool operator!=(const char *s) const;
+
+ /*!
+ * Returns true if this ByteVector is less than \a v. The value of the
+ * vectors is determined by evaluating the character from left to right, and
+ * in the event one vector is a superset of the other, the size is used.
+ */
+ bool operator<(const ByteVector &v) const;
+
+ /*!
+ * Returns true if this ByteVector is greater than \a v.
+ */
+ bool operator>(const ByteVector &v) const;
+
+ /*!
+ * Returns a vector that is \a v appended to this vector.
+ */
+ ByteVector operator+(const ByteVector &v) const;
+
+ /*!
+ * Copies ByteVector \a v.
+ */
+ ByteVector &operator=(const ByteVector &v);
+
+ /*!
+ * Copies ByteVector \a v.
+ */
+ ByteVector &operator=(char c);
+
+ /*!
+ * Copies ByteVector \a v.
+ */
+ ByteVector &operator=(const char *data);
+
+ /*!
+ * A static, empty ByteVector which is convenient and fast (since returning
+ * an empty or "null" value does not require instantiating a new ByteVector).
+ */
+ static ByteVector null;
+
+ protected:
+ /*
+ * If this ByteVector is being shared via implicit sharing, do a deep copy
+ * of the data and separate from the shared members. This should be called
+ * by all non-const subclass members.
+ */
+ void detach();
+
+ private:
+ class ByteVectorPrivate;
+ ByteVectorPrivate *d;
+ };
+
+}
+
+/*!
+ * \relates TagLib::ByteVector
+ * Streams the ByteVector \a v to the output stream \a s.
+ */
+std::ostream &operator<<(std::ostream &s, const TagLib::ByteVector &v);
+
+#endif
--- /dev/null
+/***************************************************************************
+ copyright : (C) 2002, 2003 by Scott Wheeler
+ email : wheeler@kde.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_BYTEVECTORLIST_H
+#define TAGLIB_BYTEVECTORLIST_H
+
+#include "tbytevector.h"
+#include "tlist.h"
+
+namespace TagLib {
+
+ //! A list of ByteVectors
+
+ /*!
+ * A List specialization with some handy features useful for ByteVectors.
+ */
+
+ class ByteVectorList : public List<ByteVector>
+ {
+ public:
+
+ /*!
+ * Construct an empty ByteVectorList.
+ */
+ ByteVectorList();
+
+ /*!
+ * Destroys this ByteVectorList instance.
+ */
+ virtual ~ByteVectorList();
+
+ /*!
+ * Make a shallow, implicitly shared, copy of \a l. Because this is
+ * implicitly shared, this method is lightweight and suitable for
+ * pass-by-value usage.
+ */
+ ByteVectorList(const ByteVectorList &l);
+
+ /*!
+ * Convert the ByteVectorList to a ByteVector separated by \a separator. By
+ * default a space is used.
+ */
+ ByteVector toByteVector(const ByteVector &separator = " ") const;
+
+ /*!
+ * Splits the ByteVector \a v into several strings at \a pattern. This will
+ * not include the pattern in the returned ByteVectors.
+ */
+ static ByteVectorList split(const ByteVector &v, const ByteVector &pattern,
+ int byteAlign = 1);
+
+ private:
+ class ByteVectorListPrivate;
+ ByteVectorListPrivate *d;
+ };
+
+}
+
+#endif
--- /dev/null
+/***************************************************************************
+ copyright : (C) 2002, 2003 by Scott Wheeler
+ email : wheeler@kde.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 <iostream>
+#include <bitset>
+
+#include "tdebug.h"
+#include "tstring.h"
+
+using namespace TagLib;
+
+#ifndef NDEBUG
+void TagLib::debug(const String &s)
+{
+ std::cerr << "TagLib: " << s << std::endl;
+}
+
+void TagLib::debugData(const ByteVector &v)
+{
+ for(uint i = 0; i < v.size(); i++) {
+
+ std::cout << "*** [" << i << "] - '" << char(v[i]) << "' - int " << int(v[i])
+ << std::endl;
+
+ std::bitset<8> b(v[i]);
+
+ for(int j = 0; j < 8; j++)
+ std::cout << i << ":" << j << " " << b.test(j) << std::endl;
+
+ std::cout << std::endl;
+ }
+}
+#endif
--- /dev/null
+/***************************************************************************
+ copyright : (C) 2002, 2003 by Scott Wheeler
+ email : wheeler@kde.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_DEBUG_H
+#define TAGLIB_DEBUG_H
+
+namespace TagLib {
+
+ class String;
+ class ByteVector;
+
+#ifndef DO_NOT_DOCUMENT
+#ifndef NDEBUG
+
+ /*!
+ * A simple function that prints debugging output to cerr if debugging is
+ * not disabled.
+ *
+ * \warning Do not use this outside of TagLib, it could lead to undefined
+ * symbols in your build if TagLib is built with NDEBUG defined and your
+ * application is not.
+ *
+ * \internal
+ */
+ void debug(const String &s);
+
+ /*!
+ * For debugging binary data.
+ *
+ * \warning Do not use this outside of TagLib, it could lead to undefined
+ * symbols in your build if TagLib is built with NDEBUG defined and your
+ * application is not.
+ *
+ * \internal
+ */
+ void debugData(const ByteVector &v);
+
+#else
+
+ // Define these to an empty statement if debugging is disabled.
+
+#define debug(x)
+#define debugData(x)
+
+#endif
+#endif
+}
+
+#endif
--- /dev/null
+/***************************************************************************
+ copyright : (C) 2002, 2003 by Scott Wheeler
+ email : wheeler@kde.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 "tfile.h"
+#include "tstring.h"
+#include "tdebug.h"
+
+#include <stdio.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+using namespace TagLib;
+
+class File::FilePrivate
+{
+public:
+ FilePrivate(const char *fileName) :
+ file(0),
+ name(fileName),
+ readOnly(true),
+ valid(true)
+ {}
+
+ ~FilePrivate()
+ {
+ delete [] name;
+ }
+
+ FILE *file;
+ const char *name;
+ bool readOnly;
+ bool valid;
+ static const uint bufferSize = 1024;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// public members
+////////////////////////////////////////////////////////////////////////////////
+
+File::File(const char *file)
+{
+ d = new FilePrivate(strdup(file));
+
+ d->readOnly = !isWritable(file);
+ d->file = fopen(file, d->readOnly ? "r" : "r+");
+
+ if(!d->file)
+ debug("Could not open file " + String(file));
+}
+
+File::~File()
+{
+ if(d->file)
+ fclose(d->file);
+ delete d;
+}
+
+const char *File::name() const
+{
+ return d->name;
+}
+
+ByteVector File::readBlock(ulong length)
+{
+ if(!d->file) {
+ debug("File::readBlock() -- Invalid File");
+ return ByteVector::null;
+ }
+
+ ByteVector v(static_cast<uint>(length));
+ const int count = fread(v.data(), sizeof(char), length, d->file);
+ v.resize(count);
+ return v;
+}
+
+void File::writeBlock(const ByteVector &data)
+{
+ if(!d->file)
+ return;
+
+ if(d->readOnly) {
+ debug("File::writeBlock() -- attempted to write to a file that is not writable");
+ return;
+ }
+
+ fwrite(data.data(), sizeof(char), data.size(), d->file);
+}
+
+long File::find(const ByteVector &pattern, long fromOffset, const ByteVector &before)
+{
+ if(!d->file || pattern.size() > d->bufferSize)
+ return -1;
+
+ // The position in the file that the current buffer starts at.
+
+ long bufferOffset = fromOffset;
+ ByteVector buffer;
+
+ // These variables are used to keep track of a partial match that happens at
+ // the end of a buffer.
+
+ int previousPartialMatch = -1;
+ int beforePreviousPartialMatch = -1;
+
+ // Save the location of the current read pointer. We will restore the
+ // position using seek() before all returns.
+
+ long originalPosition = tell();
+
+ // Start the search at the offset.
+
+ seek(fromOffset);
+
+ // This loop is the crux of the find method. There are three cases that we
+ // want to account for:
+ //
+ // (1) The previously searched buffer contained a partial match of the search
+ // pattern and we want to see if the next one starts with the remainder of
+ // that pattern.
+ //
+ // (2) The search pattern is wholly contained within the current buffer.
+ //
+ // (3) The current buffer ends with a partial match of the pattern. We will
+ // note this for use in the next itteration, where we will check for the rest
+ // of the pattern.
+ //
+ // All three of these are done in two steps. First we check for the pattern
+ // and do things appropriately if a match (or partial match) is found. We
+ // then check for "before". The order is important because it gives priority
+ // to "real" matches.
+
+ for(buffer = readBlock(d->bufferSize); buffer.size() > 0; buffer = readBlock(d->bufferSize)) {
+
+ // (1) previous partial match
+
+ if(previousPartialMatch >= 0 && int(d->bufferSize) > previousPartialMatch) {
+ const int patternOffset = (d->bufferSize - previousPartialMatch);
+ if(buffer.containsAt(pattern, 0, patternOffset)) {
+ seek(originalPosition);
+ return bufferOffset - d->bufferSize + previousPartialMatch;
+ }
+ }
+
+ if(!before.isNull() && beforePreviousPartialMatch >= 0 && int(d->bufferSize) > beforePreviousPartialMatch) {
+ const int beforeOffset = (d->bufferSize - beforePreviousPartialMatch);
+ if(buffer.containsAt(before, 0, beforeOffset)) {
+ seek(originalPosition);
+ return -1;
+ }
+ }
+
+ // (2) pattern contained in current buffer
+
+ long location = buffer.find(pattern);
+ if(location >= 0) {
+ seek(originalPosition);
+ return bufferOffset + location;
+ }
+
+ if(!before.isNull() && buffer.find(before) >= 0) {
+ seek(originalPosition);
+ return -1;
+ }
+
+ // (3) partial match
+
+ previousPartialMatch = buffer.endsWithPartialMatch(pattern);
+
+ if(!before.isNull())
+ beforePreviousPartialMatch = buffer.endsWithPartialMatch(before);
+
+ bufferOffset += d->bufferSize;
+ }
+
+ // Since we hit the end of the file, reset the status before continuing.
+
+ clear();
+
+ seek(originalPosition);
+
+ return -1;
+}
+
+
+long File::rfind(const ByteVector &pattern, long fromOffset, const ByteVector &before)
+{
+ if(!d->file || pattern.size() > d->bufferSize)
+ return -1;
+
+ // The position in the file that the current buffer starts at.
+
+ ByteVector buffer;
+
+ // These variables are used to keep track of a partial match that happens at
+ // the end of a buffer.
+
+ /*
+ int previousPartialMatch = -1;
+ int beforePreviousPartialMatch = -1;
+ */
+
+ // Save the location of the current read pointer. We will restore the
+ // position using seek() before all returns.
+
+ long originalPosition = tell();
+
+ // Start the search at the offset.
+
+ long bufferOffset;
+ if(fromOffset == 0) {
+ seek(-1 * d->bufferSize, End);
+ bufferOffset = tell();
+ }
+ else {
+ seek(fromOffset + -1 * d->bufferSize, Beginning);
+ bufferOffset = tell();
+ }
+
+ // See the notes in find() for an explanation of this algorithm.
+
+ for(buffer = readBlock(d->bufferSize); buffer.size() > 0; buffer = readBlock(d->bufferSize)) {
+
+ // TODO: (1) previous partial match
+
+ // (2) pattern contained in current buffer
+
+ long location = buffer.rfind(pattern);
+ if(location >= 0) {
+ seek(originalPosition);
+ return bufferOffset + location;
+ }
+
+ if(!before.isNull() && buffer.find(before) >= 0) {
+ seek(originalPosition);
+ return -1;
+ }
+
+ // TODO: (3) partial match
+
+ bufferOffset -= d->bufferSize;
+ seek(bufferOffset);
+ }
+
+ // Since we hit the end of the file, reset the status before continuing.
+
+ clear();
+
+ seek(originalPosition);
+
+ return -1;
+}
+
+void File::insert(const ByteVector &data, ulong start, ulong replace)
+{
+ if(!d->file)
+ return;
+
+ if(data.size() == replace) {
+ seek(start);
+ writeBlock(data);
+ return;
+ }
+ else if(data.size() < replace) {
+ seek(start);
+ writeBlock(data);
+ removeBlock(start + data.size(), replace - data.size());
+ return;
+ }
+
+ // Woohoo! Faster (about 20%) than id3lib at last. I had to get hardcore
+ // and avoid TagLib's high level API for rendering just copying parts of
+ // the file that don't contain tag data.
+ //
+ // Now I'll explain the steps in this ugliness:
+
+ // First, make sure that we're working with a buffer that is longer than
+ // the *differnce* in the tag sizes. We want to avoid overwriting parts
+ // that aren't yet in memory, so this is necessary.
+
+ ulong bufferLength = bufferSize();
+ while(data.size() - replace > bufferLength)
+ bufferLength += bufferSize();
+
+ // Set where to start the reading and writing.
+
+ long readPosition = start + replace;
+ long writePosition = start;
+
+ ByteVector buffer;
+ ByteVector aboutToOverwrite(static_cast<uint>(bufferLength));
+
+ // This is basically a special case of the loop below. Here we're just
+ // doing the same steps as below, but since we aren't using the same buffer
+ // size -- instead we're using the tag size -- this has to be handled as a
+ // special case. We're also using File::writeBlock() just for the tag.
+ // That's a bit slower than using char *'s so, we're only doing it here.
+
+ seek(readPosition);
+ int bytesRead = fread(aboutToOverwrite.data(), sizeof(char), bufferLength, d->file);
+ readPosition += bufferLength;
+
+ seek(writePosition);
+ writeBlock(data);
+ writePosition += data.size();
+
+ buffer = aboutToOverwrite;
+
+ // Ok, here's the main loop. We want to loop until the read fails, which
+ // means that we hit the end of the file.
+
+ while(bytesRead != 0) {
+
+ // Seek to the current read position and read the data that we're about
+ // to overwrite. Appropriately increment the readPosition.
+
+ seek(readPosition);
+ bytesRead = fread(aboutToOverwrite.data(), sizeof(char), bufferLength, d->file);
+ aboutToOverwrite.resize(bytesRead);
+ readPosition += bufferLength;
+
+ // Check to see if we just read the last block. We need to call clear()
+ // if we did so that the last write succeeds.
+
+ if(ulong(bytesRead) < bufferLength)
+ clear();
+
+ // Seek to the write position and write our buffer. Increment the
+ // writePosition.
+
+ seek(writePosition);
+ fwrite(buffer.data(), sizeof(char), bufferLength, d->file);
+ writePosition += bufferLength;
+
+ // Make the current buffer the data that we read in the beginning.
+
+ buffer = aboutToOverwrite;
+
+ // Again, we need this for the last write. We don't want to write garbage
+ // at the end of our file, so we need to set the buffer size to the amount
+ // that we actually read.
+
+ bufferLength = bytesRead;
+ }
+}
+
+void File::removeBlock(ulong start, ulong length)
+{
+ if(!d->file)
+ return;
+
+ ulong bufferLength = bufferSize();
+
+ long readPosition = start + length;
+ long writePosition = start;
+
+ ByteVector buffer(static_cast<uint>(bufferLength));
+
+ ulong bytesRead = true;
+
+ while(bytesRead != 0) {
+ seek(readPosition);
+ bytesRead = fread(buffer.data(), sizeof(char), bufferLength, d->file);
+ buffer.resize(bytesRead);
+ readPosition += bytesRead;
+
+ // Check to see if we just read the last block. We need to call clear()
+ // if we did so that the last write succeeds.
+
+ if(bytesRead < bufferLength)
+ clear();
+
+ seek(writePosition);
+ fwrite(buffer.data(), sizeof(char), bytesRead, d->file);
+ writePosition += bytesRead;
+ }
+ truncate(writePosition);
+}
+
+bool File::readOnly() const
+{
+ return d->readOnly;
+}
+
+bool File::isReadable(const char *file)
+{
+ return access(file, R_OK) == 0;
+}
+
+bool File::isOpen() const
+{
+ return d->file;
+}
+
+bool File::isValid() const
+{
+ return d->file && d->valid;
+}
+
+void File::seek(long offset, Position p)
+{
+ if(!d->file) {
+ debug("File::seek() -- trying to seek in a file that isn't opened.");
+ return;
+ }
+
+ switch(p) {
+ case Beginning:
+ fseek(d->file, offset, SEEK_SET);
+ break;
+ case Current:
+ fseek(d->file, offset, SEEK_CUR);
+ break;
+ case End:
+ fseek(d->file, offset, SEEK_END);
+ break;
+ }
+}
+
+void File::clear()
+{
+ clearerr(d->file);
+}
+
+long File::tell() const
+{
+ return ftell(d->file);
+}
+
+long File::length()
+{
+ if(!d->file)
+ return 0;
+
+ long curpos = tell();
+
+ seek(0, End);
+ long endpos = tell();
+
+ seek(curpos, Beginning);
+
+ return endpos;
+}
+
+bool File::isWritable(const char *file)
+{
+ return access(file, W_OK) == 0;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// protected members
+////////////////////////////////////////////////////////////////////////////////
+
+void File::setValid(bool valid)
+{
+ d->valid = valid;
+}
+
+void File::truncate(long length)
+{
+ ftruncate(fileno(d->file), length);
+}
+
+TagLib::uint File::bufferSize()
+{
+ return FilePrivate::bufferSize;
+}
--- /dev/null
+/***************************************************************************
+ copyright : (C) 2002, 2003 by Scott Wheeler
+ email : wheeler@kde.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_FILE_H
+#define TAGLIB_FILE_H
+
+#include "taglib.h"
+#include "tbytevector.h"
+
+namespace TagLib {
+
+ class String;
+ class Tag;
+ class AudioProperties;
+
+ //! A file class with some useful methods for tag manipulation
+
+ /*!
+ * This class is a basic file class with some methods that are particularly
+ * useful for tag editors. It has methods to take advantage of
+ * ByteVector and a binary search method for finding patterns in a file.
+ */
+
+ class File
+ {
+ public:
+ /*!
+ * Position in the file used for seeking.
+ */
+ enum Position {
+ //! Seek from the beginning of the file.
+ Beginning,
+ //! Seek from the current position in the file.
+ Current,
+ //! Seek from the end of the file.
+ End
+ };
+
+ /*!
+ * Destroys this File instance.
+ */
+ virtual ~File();
+
+ /*!
+ * Returns the file name in the local file system encoding.
+ */
+ const char *name() const;
+
+ /*!
+ * Returns a pointer to this file's tag. This should be reimplemented in
+ * the concrete subclasses.
+ */
+ virtual Tag *tag() const = 0;
+
+ /*!
+ * Returns a pointer to this file's audio properties. This should be
+ * reimplemented in the concrete subclasses. If no audio properties were
+ * read then this will return a null pointer.
+ */
+ virtual AudioProperties *audioProperties() const = 0;
+
+ /*!
+ * Save the file and its associated tags. This should be reimplemented in
+ * the concrete subclasses.
+ */
+ virtual void save() = 0;
+
+ /*!
+ * Reads a block of size \a length at the current get pointer.
+ */
+ ByteVector readBlock(ulong length);
+
+ /*!
+ * Attempts to write the block \a data at the current get pointer. If the
+ * file is currently only opened read only -- i.e. readOnly() returns true --
+ * this attempts to reopen the file in read/write mode.
+ *
+ * \note This should be used instead of using the streaming output operator
+ * for a ByteVector. And even this function is significantly slower than
+ * doing output with a char[].
+ */
+ void writeBlock(const ByteVector &data);
+
+ /*!
+ * Returns the offset in the file that \a pattern occurs at or -1 if it can
+ * not be found. If \a before is set, the search will only continue until the
+ * pattern \a before is found. This is useful for tagging purposes to search
+ * for a tag before the synch frame.
+ *
+ * Searching starts at \a fromOffset, which defaults to the beginning of the
+ * file.
+ *
+ * \note This has the practial limitation that \a pattern can not be longer
+ * than the buffer size used by readBlock(). Currently this is 1024 bytes.
+ */
+ long find(const ByteVector &pattern,
+ long fromOffset = 0,
+ const ByteVector &before = ByteVector::null);
+
+ /*!
+ * Returns the offset in the file that \a pattern occurs at or -1 if it can
+ * not be found. If \a before is set, the search will only continue until the
+ * pattern \a before is found. This is useful for tagging purposes to search
+ * for a tag before the synch frame.
+ *
+ * Searching starts at \a fromOffset and proceeds from the that point to the
+ * beginning of the file and defaults to the end of the file.
+ *
+ * \note This has the practial limitation that \a pattern can not be longer
+ * than the buffer size used by readBlock(). Currently this is 1024 bytes.
+ */
+ long rfind(const ByteVector &pattern,
+ long fromOffset = 0,
+ const ByteVector &before = ByteVector::null);
+
+ /*!
+ * Insert \a data at position \a start in the file overwriting \a replace
+ * bytes of the original content.
+ *
+ * \note This method is slow since it requires rewriting all of the file
+ * after the insertion point.
+ */
+ void insert(const ByteVector &data, ulong start = 0, ulong replace = 0);
+
+ /*!
+ * Removes a block of the file starting a \a start and continuing for
+ * \a length bytes.
+ *
+ * \note This method is slow since it involves rewriting all of the file
+ * after the removed portion.
+ */
+ void removeBlock(ulong start = 0, ulong length = 0);
+
+ /*!
+ * Returns true if the file is read only (or if the file can not be opened).
+ */
+ bool readOnly() const;
+
+ /*!
+ * Since the file can currently only be opened as an argument to the
+ * constructor (sort-of by design), this returns if that open succeeded.
+ */
+ bool isOpen() const;
+
+ /*!
+ * Returns true if the file is open and readble and valid information for
+ * the Tag and / or AudioProperties was found.
+ */
+ bool isValid() const;
+
+ /*!
+ * Move the I/O pointer to \a offset in the file from position \a p. This
+ * defaults to seeking from the beginning of the file.
+ *
+ * \see Position
+ */
+ void seek(long offset, Position p = Beginning);
+
+ /*!
+ * Reset the end-of-file and error flags on the file.
+ */
+ void clear();
+
+ /*!
+ * Returns the current offset withing the file.
+ */
+ long tell() const;
+
+ /*!
+ * Returns the length of the file.
+ */
+ long length();
+
+ /*!
+ * Returns true if \a file can be opened for reading. If the file does not
+ * exist, this will return false.
+ */
+ static bool isReadable(const char *file);
+
+ /*!
+ * Returns true if \a file can be opened for writing.
+ */
+ static bool isWritable(const char *name);
+
+ protected:
+ /*!
+ * Construct a File object and opens the \a file. \a file should be a
+ * be a C-string in the local file system encoding.
+ *
+ * \note Constructor is protected since this class should only be
+ * instantiated through subclasses.
+ */
+ File(const char *file);
+
+ /*!
+ * Marks the file as valid or invalid.
+ *
+ * \see isValid()
+ */
+ void setValid(bool valid);
+
+ /*!
+ * Truncates the file to a \a length.
+ */
+ void truncate(long length);
+
+ /*!
+ * Returns the buffer size that is used for internal buffering.
+ */
+ static uint bufferSize();
+
+ private:
+ File(const File &);
+ File &operator=(const File &);
+
+ class FilePrivate;
+ FilePrivate *d;
+ };
+
+}
+
+#endif
--- /dev/null
+/***************************************************************************
+ copyright : (C) 2002 by Scott Wheeler
+ email : wheeler@kde.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_LIST_H
+#define TAGLIB_LIST_H
+
+#include "taglib.h"
+
+#include <list>
+
+namespace TagLib {
+
+ //! A generic, implicitly shared list.
+
+ /*!
+ * This is basic generic list that's somewhere between a std::list and a
+ * QValueList. This class is implicitly shared. For example:
+ *
+ * \code
+ *
+ * TagLib::List<int> l = someOtherIntList;
+ *
+ * \endcode
+ *
+ * The above example is very cheap. This also makes lists suitable for the
+ * return types of functions. The above example will just copy a pointer rather
+ * than copying the data in the list. When your \e shared list's data changes,
+ * only \e then will the data be copied.
+ */
+
+ template <class T> class List
+ {
+ public:
+#ifndef DO_NOT_DOCUMENT
+ typedef typename std::list<T>::iterator Iterator;
+ typedef typename std::list<T>::const_iterator ConstIterator;
+#endif
+
+ /*!
+ * Constructs an empty list.
+ */
+ List();
+
+ /*!
+ * Make a shallow, implicitly shared, copy of \a l. Because this is
+ * implicitly shared, this method is lightweight and suitable for
+ * pass-by-value usage.
+ */
+ List(const List<T> &l);
+
+ /*!
+ * Destroys this List instance. If auto deletion is enabled and this list
+ * contains a pointer type all of the memebers are also deleted.
+ */
+ virtual ~List();
+
+ /*!
+ * Returns an STL style iterator to the beginning of the list. See
+ * std::list::const_iterator for the semantics.
+ */
+ Iterator begin();
+
+ /*!
+ * Returns an STL style constant iterator to the beginning of the list. See
+ * std::list::iterator for the semantics.
+ */
+ ConstIterator begin() const;
+
+ /*!
+ * Returns an STL style iterator to the end of the list. See
+ * std::list::iterator for the semantics.
+ */
+ Iterator end();
+
+ /*!
+ * Returns an STL style constant iterator to the end of the list. See
+ * std::list::const_iterator for the semantics.
+ */
+ ConstIterator end() const;
+
+ /*!
+ * Inserts a copy of \a value before \a it.
+ */
+ void insert(Iterator it, const T &value);
+
+ /*!
+ * Inserts the \a value into the list. This assumes that the list is
+ * currently sorted. If \a unique is true then the value will not
+ * be inserted if it is already in the list.
+ */
+ void sortedInsert(const T &value, bool unique = false);
+
+ /*!
+ * Appends \a item to the end of the list.
+ */
+ void append(const T &item);
+
+ /*!
+ * Appends all of the values in \a l to the end of the list.
+ */
+ void append(const List<T> &l);
+
+ /*!
+ * Clears the list. If auto deletion is enabled and this list contains a
+ * pointer type the members are also deleted.
+ *
+ * \see setAutoDelete()
+ */
+ void clear();
+
+ /*!
+ * Returns the number of elements in the list.
+ */
+ uint size() const;
+ bool isEmpty() const;
+
+ /*!
+ * Find the first occurance of \a value.
+ */
+ Iterator find(const T &value);
+
+ /*!
+ * Find the first occurance of \a value.
+ */
+ ConstIterator find(const T &value) const;
+
+ /*!
+ * Returns true if the list contains \a value.
+ */
+ bool contains(const T &value) const;
+
+ /*!
+ * Erase the item at \a it from the list.
+ */
+ void erase(Iterator it);
+
+ /*!
+ * Returns a reference to the first item in the list.
+ */
+ const T &front() const;
+
+ /*!
+ * Returns a reference to the first item in the list.
+ */
+ T &front();
+
+ /*!
+ * Returns a reference to the last item in the list.
+ */
+ const T &back() const;
+
+ /*!
+ * Returns a reference to the last item in the list.
+ */
+ T &back();
+
+ /*!
+ * Auto delete the members of the list when the last reference to the list
+ * passes out of scope. This will have no effect on lists which do not
+ * contain a pointer type.
+ *
+ * \note This relies on partial template instantiation -- most modern C++
+ * compilers should now support this.
+ */
+ void setAutoDelete(bool autoDelete);
+
+ /*!
+ * Returns a reference to item \a i in the list.
+ *
+ * \warning This method is slow. Use iterators to loop through the list.
+ */
+ T &operator[](uint i);
+
+ /*!
+ * Returns a const reference to item \a i in the list.
+ *
+ * \warning This method is slow. Use iterators to loop through the list.
+ */
+ const T &operator[](uint i) const;
+
+ /*!
+ * Make a shallow, implicitly shared, copy of \a l. Because this is
+ * implicitly shared, this method is lightweight and suitable for
+ * pass-by-value usage.
+ */
+ List<T> &operator=(const List<T> &l);
+
+ /*!
+ * Compares this list with \a l and returns true if all of the elements are
+ * the same.
+ */
+ bool operator==(const List<T> &l) const;
+
+ protected:
+ /*
+ * If this List is being shared via implicit sharing, do a deep copy of the
+ * data and separate from the shared members. This should be called by all
+ * non-const subclass members.
+ */
+ void detach();
+
+ private:
+#ifndef DO_NOT_DOCUMENT
+ template <class TP> class ListPrivate;
+ ListPrivate<T> *d;
+#endif
+ };
+
+}
+
+// Since GCC doesn't support the "export" keyword, we have to include the
+// implementation.
+
+#include "tlist.tcc"
+
+#endif
--- /dev/null
+/***************************************************************************
+ copyright : (C) 2002 by Scott Wheeler
+ email : wheeler@kde.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 <tdebug.h>
+
+#include <algorithm>
+
+namespace TagLib {
+
+////////////////////////////////////////////////////////////////////////////////
+// public members
+////////////////////////////////////////////////////////////////////////////////
+
+// The functionality of List<T>::setAutoDelete() is implemented here partial
+// template specialization. This is implemented in such a way that calling
+// setAutoDelete() on non-pointer types will simply have no effect.
+
+// A base for the generic and specialized private class types. New
+// non-templatized members should be added here.
+
+class ListPrivateBase : public RefCounter
+{
+public:
+ ListPrivateBase() : autoDelete(false) {}
+ bool autoDelete;
+};
+
+// A generic implementation
+
+template <class T>
+template <class TP> class List<T>::ListPrivate : public ListPrivateBase
+{
+public:
+ ListPrivate() : ListPrivateBase() {}
+ ListPrivate(const std::list<TP> &l) : ListPrivateBase(), list(l) {}
+ void clear() {
+ list.clear();
+ }
+ std::list<TP> list;
+};
+
+// A partial specialization for all pointer types that implements the
+// setAutoDelete() functionality.
+
+template <class T>
+template <class TP> class List<T>::ListPrivate<TP *> : public ListPrivateBase
+{
+public:
+ ListPrivate() : ListPrivateBase() {}
+ ListPrivate(const std::list<TP *> &l) : ListPrivateBase(), list(l) {}
+ ~ListPrivate() {
+ clear();
+ }
+ void clear() {
+ if(autoDelete) {
+ typename std::list<TP *>::const_iterator it = list.begin();
+ for(; it != list.end(); ++it)
+ delete *it;
+ }
+ list.clear();
+ }
+ std::list<TP *> list;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// public members
+////////////////////////////////////////////////////////////////////////////////
+
+template <class T>
+List<T>::List()
+{
+ d = new ListPrivate<T>;
+}
+
+template <class T>
+List<T>::List(const List<T> &l) : d(l.d)
+{
+ d->ref();
+}
+
+template <class T>
+List<T>::~List()
+{
+ if(d->deref())
+ delete d;
+}
+
+template <class T>
+typename List<T>::Iterator List<T>::begin()
+{
+ detach();
+ return d->list.begin();
+}
+
+template <class T>
+typename List<T>::ConstIterator List<T>::begin() const
+{
+ return d->list.begin();
+}
+
+template <class T>
+typename List<T>::Iterator List<T>::end()
+{
+ detach();
+ return d->list.end();
+}
+
+template <class T>
+typename List<T>::ConstIterator List<T>::end() const
+{
+ return d->list.end();
+}
+
+template <class T>
+void List<T>::insert(Iterator it, const T &item)
+{
+ detach();
+ d->list.insert(it, item);
+}
+
+template <class T>
+void List<T>::sortedInsert(const T &value, bool unique)
+{
+ detach();
+ Iterator it = begin();
+ while(*it < value && it != end())
+ ++it;
+ if(unique && it != end() && *it == value)
+ return;
+ insert(it, value);
+}
+
+template <class T>
+void List<T>::append(const T &item)
+{
+ detach();
+ d->list.push_back(item);
+}
+
+template <class T>
+void List<T>::append(const List<T> &l)
+{
+ detach();
+ d->list.insert(d->list.end(), l.begin(), l.end());
+}
+
+template <class T>
+void List<T>::clear()
+{
+ detach();
+ d->clear();
+}
+
+template <class T>
+TagLib::uint List<T>::size() const
+{
+ return d->list.size();
+}
+
+template <class T>
+bool List<T>::isEmpty() const
+{
+ return d->list.empty();
+}
+
+template <class T>
+typename List<T>::Iterator List<T>::find(const T &value)
+{
+ return std::find(d->list.begin(), d->list.end(), value);
+}
+
+template <class T>
+typename List<T>::ConstIterator List<T>::find(const T &value) const
+{
+ return std::find(d->list.begin(), d->list.end(), value);
+}
+
+template <class T>
+bool List<T>::contains(const T &value) const
+{
+ return std::find(d->list.begin(), d->list.end(), value) != d->list.end();
+}
+
+template <class T>
+void List<T>::erase(Iterator it)
+{
+ d->list.erase(it);
+}
+
+template <class T>
+const T &List<T>::front() const
+{
+ return d->list.front();
+}
+
+template <class T>
+T &List<T>::front()
+{
+ detach();
+ return d->list.front();
+}
+
+template <class T>
+const T &List<T>::back() const
+{
+ return d->list.back();
+}
+
+template <class T>
+void List<T>::setAutoDelete(bool autoDelete)
+{
+ d->autoDelete = autoDelete;
+}
+
+template <class T>
+T &List<T>::back()
+{
+ detach();
+ return d->list.back();
+}
+
+template <class T>
+T &List<T>::operator[](uint i)
+{
+ Iterator it = d->list.begin();
+
+ for(uint j = 0; j < i; j++)
+ ++it;
+
+ return *it;
+}
+
+template <class T>
+const T &List<T>::operator[](uint i) const
+{
+ ConstIterator it = d->list.begin();
+
+ for(uint j = 0; j < i; j++)
+ ++it;
+
+ return *it;
+}
+
+template <class T>
+List<T> &List<T>::operator=(const List<T> &l)
+{
+ if(&l == this)
+ return *this;
+
+ if(d->deref())
+ delete d;
+ d = l.d;
+ d->ref();
+ return *this;
+}
+
+template <class T>
+bool List<T>::operator==(const List<T> &l) const
+{
+ return d->list == l.d->list;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// protected members
+////////////////////////////////////////////////////////////////////////////////
+
+template <class T>
+void List<T>::detach()
+{
+ if(d->count() > 1) {
+ d->deref();
+ d = new ListPrivate<T>(d->list);
+ }
+}
+
+} // namespace TagLib
--- /dev/null
+/***************************************************************************
+ copyright : (C) 2003 by Scott Wheeler
+ email : wheeler@kde.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_MAP_H
+#define TAGLIB_MAP_H
+
+#include "taglib.h"
+
+#include <map>
+
+namespace TagLib {
+
+ //! A generic, implicitly shared map.
+
+ /*!
+ * This implements a standard map container that associates a key with a value
+ * and has fast key-based lookups. This map is also implicitly shared making
+ * it suitable for pass-by-value usage.
+ */
+
+ template <class Key, class T> class Map
+ {
+ public:
+#ifndef DO_NOT_DOCUMENT
+ typedef typename std::map<Key, T>::iterator Iterator;
+ typedef typename std::map<Key, T>::const_iterator ConstIterator;
+#endif
+
+ /*!
+ * Constructs an empty Map.
+ */
+ Map();
+
+ /*!
+ * Make a shallow, implicitly shared, copy of \a m. Because this is
+ * implicitly shared, this method is lightweight and suitable for
+ * pass-by-value usage.
+ */
+ Map(const Map<Key, T> &m);
+
+ /*!
+ * Destroys this instance of the Map.
+ */
+ virtual ~Map();
+
+ /*!
+ * Returns an STL style iterator to the beginning of the map. See
+ * std::map::iterator for the semantics.
+ */
+ Iterator begin();
+
+ /*!
+ * Returns an STL style iterator to the beginning of the map. See
+ * std::map::const_iterator for the semantics.
+ */
+ ConstIterator begin() const;
+
+ /*!
+ * Returns an STL style iterator to the end of the map. See
+ * std::map::iterator for the semantics.
+ */
+ Iterator end();
+
+ /*!
+ * Returns an STL style iterator to the end of the map. See
+ * std::map::const_iterator for the semantics.
+ */
+ ConstIterator end() const;
+
+ /*!
+ * Inserts \a value under \a key in the map. If a value for \a key already
+ * exists it will be overwritten.
+ */
+ void insert(const Key &key, const T &value);
+
+ /*!
+ * Removes all of the elements from elements from the map. This however
+ * will not delete pointers if the mapped type is a pointer type.
+ */
+ void clear();
+
+ /*!
+ * The number of elements in the map.
+ *
+ * \see isEmpty()
+ */
+ uint size() const;
+
+ /*!
+ * Returns true if the map is empty.
+ *
+ * \see size()
+ */
+ bool isEmpty() const;
+
+ /*!
+ * Returns true if the map contains an instance of \a key.
+ */
+ bool contains(const Key &key) const;
+
+ /*!
+ * Returns a reference to the value associated with \a key.
+ *
+ * \note This has undefined behavior if the key is not present in the map.
+ */
+ const T &operator[](const Key &key) const;
+
+ /*!
+ * Returns a reference to the value associated with \a key.
+ *
+ * \note This has undefined behavior if the key is not present in the map.
+ */
+ T &operator[](const Key &key);
+
+ /*!
+ * Make a shallow, implicitly shared, copy of \a m. Because this is
+ * implicitly shared, this method is lightweight and suitable for
+ * pass-by-value usage.
+ */
+ Map<Key, T> &operator=(const Map<Key, T> &m);
+
+ protected:
+ /*
+ * If this List is being shared via implicit sharing, do a deep copy of the
+ * data and separate from the shared members. This should be called by all
+ * non-const subclass members.
+ */
+ void detach();
+
+ private:
+#ifndef DO_NOT_DOCUMENT
+ template <class KeyP, class TP> class MapPrivate;
+ MapPrivate<Key, T> *d;
+#endif
+ };
+
+}
+
+// Since GCC doesn't support the "export" keyword, we have to include the
+// implementation.
+
+#include "tmap.tcc"
+
+#endif
--- /dev/null
+/***************************************************************************
+ copyright : (C) 2002 by Scott Wheeler
+ email : wheeler@kde.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 *
+ ***************************************************************************/
+
+namespace TagLib {
+
+////////////////////////////////////////////////////////////////////////////////
+// public members
+////////////////////////////////////////////////////////////////////////////////
+
+template <class Key, class T>
+template <class KeyP, class TP> class Map<Key, T>::MapPrivate : public RefCounter
+{
+public:
+ MapPrivate() : RefCounter() {}
+ MapPrivate(const std::map<KeyP, TP> &m) : RefCounter(), map(m) {}
+
+ std::map<KeyP, TP> map;
+};
+
+template <class Key, class T>
+Map<Key, T>::Map()
+{
+ d = new MapPrivate<Key, T>;
+}
+
+template <class Key, class T>
+Map<Key, T>::Map(const Map<Key, T> &m) : d(m.d)
+{
+ d->ref();
+}
+
+template <class Key, class T>
+Map<Key, T>::~Map()
+{
+ if(d->deref())
+ delete(d);
+}
+
+template <class Key, class T>
+typename Map<Key, T>::Iterator Map<Key, T>::begin()
+{
+ detach();
+ return d->map.begin();
+}
+
+template <class Key, class T>
+typename Map<Key, T>::ConstIterator Map<Key, T>::begin() const
+{
+ return d->map.begin();
+}
+
+template <class Key, class T>
+typename Map<Key, T>::Iterator Map<Key, T>::end()
+{
+ detach();
+ return d->map.end();
+}
+
+template <class Key, class T>
+typename Map<Key, T>::ConstIterator Map<Key, T>::end() const
+{
+ return d->map.end();
+}
+
+template <class Key, class T>
+void Map<Key, T>::insert(const Key &key, const T &value)
+{
+ detach();
+ std::pair<Key, T> item(key, value);
+ d->map.insert(item);
+}
+
+template <class Key, class T>
+void Map<Key, T>::clear()
+{
+ detach();
+ d->map.clear();
+}
+
+template <class Key, class T>
+bool Map<Key, T>::isEmpty() const
+{
+ return d->map.empty();
+}
+
+template <class Key, class T>
+bool Map<Key, T>::contains(const Key &key) const
+{
+ return d->map.find(key) != d->map.end();
+}
+
+template <class Key, class T>
+TagLib::uint Map<Key, T>::size() const
+{
+ return d->map.size();
+}
+
+template <class Key, class T>
+const T &Map<Key, T>::operator[](const Key &key) const
+{
+ return d->map[key];
+}
+
+template <class Key, class T>
+T &Map<Key, T>::operator[](const Key &key)
+{
+ return d->map[key];
+}
+
+template <class Key, class T>
+Map<Key, T> &Map<Key, T>::operator=(const Map<Key, T> &m)
+{
+ if(&m == this)
+ return *this;
+
+ if(d->deref())
+ delete(d);
+ d = m.d;
+ d->ref();
+ return *this;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// protected members
+////////////////////////////////////////////////////////////////////////////////
+
+template <class Key, class T>
+void Map<Key, T>::detach()
+{
+ if(d->count() > 1) {
+ d->deref();
+ d = new MapPrivate<Key, T>(d->map);
+ }
+}
+
+} // namespace TagLib
--- /dev/null
+/***************************************************************************
+ copyright : (C) 2002, 2003 by Scott Wheeler
+ email : wheeler@kde.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 "unicode.h"
+#include "tdebug.h"
+
+#include <iostream>
+
+namespace TagLib {
+
+ inline unsigned short byteSwap(unsigned short x)
+ {
+ return ((x) >> 8) & 0xff | ((x) & 0xff) << 8;
+ }
+
+ inline unsigned short combine(unsigned char c1, unsigned char c2)
+ {
+ return (c1 << 8) | c2;
+ }
+}
+
+using namespace TagLib;
+
+class String::StringPrivate : public RefCounter
+{
+public:
+ StringPrivate(const wstring &s) :
+ RefCounter(),
+ data(s),
+ CString(0) {}
+
+ StringPrivate() :
+ RefCounter(),
+ CString(0) {}
+
+ ~StringPrivate() {
+ delete [] CString;
+ }
+
+ wstring data;
+
+ /*!
+ * This is only used to hold the a pointer to the most recent value of
+ * toCString.
+ */
+ char *CString;
+};
+
+String String::null;
+
+////////////////////////////////////////////////////////////////////////////////
+
+String::String()
+{
+ d = new StringPrivate;
+}
+
+String::String(const String &s) : d(s.d)
+{
+ d->ref();
+}
+
+String::String(const std::string &s, Type t)
+{
+ d = new StringPrivate;
+
+ if(t == UTF16 || t == UTF16BE) {
+ debug("String::String() -- A std::string should not contain UTF16.");
+ return;
+ }
+
+ for(std::string::const_iterator it = s.begin(); it != s.end(); it++)
+ d->data += uchar(*it);
+
+ prepare(t);
+}
+
+String::String(const wstring &s, Type t)
+{
+ d = new StringPrivate(s);
+ prepare(t);
+}
+
+String::String(const wchar_t *s, Type t)
+{
+ d = new StringPrivate(s);
+ prepare(t);
+}
+
+String::String(const char *s, Type t)
+{
+ d = new StringPrivate;
+
+ if(t == UTF16 || t == UTF16BE) {
+ debug("String::String() -- A const char * should not contain UTF16.");
+ return;
+ }
+
+ for(int i = 0; s[i] != 0; i++)
+ d->data += uchar(s[i]);
+
+ prepare(t);
+}
+
+String::String(wchar_t c, Type t)
+{
+ d = new StringPrivate;
+ d->data += c;
+ prepare(t);
+}
+
+String::String(char c, Type t)
+{
+ d = new StringPrivate;
+
+ if(t == UTF16 || t == UTF16BE) {
+ debug("String::String() -- A std::string should not contain UTF16.");
+ return;
+ }
+
+ d->data += uchar(c);
+ prepare(t);
+}
+
+String::String(const ByteVector &v, Type t)
+{
+ d = new StringPrivate;
+
+ if(t == Latin1 || t == UTF8) {
+ for(uint i = 0; i < v.size() && v[i]; i++)
+ d->data += uchar(v[i]);
+ }
+ else {
+ for(uint i = 0; i + 1 < v.size() && combine(v[i], v[i + 1]); i += 2)
+ d->data += combine(v[i], v[i + 1]);
+ }
+ prepare(t);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+String::~String()
+{
+ if(d->deref())
+ delete d;
+}
+
+std::string String::to8Bit(bool unicode) const
+{
+ std::string s;
+ s.resize(d->data.size());
+
+ if(!unicode) {
+ std::string::iterator targetIt = s.begin();
+ for(wstring::const_iterator it = d->data.begin(); it != d->data.end(); it++) {
+ *targetIt = char(*it);
+ ++targetIt;
+ }
+ return s;
+ }
+
+ const int outputBufferSize = d->data.size() * 3 + 1;
+
+ Unicode::UTF16 *sourceBuffer = new Unicode::UTF16[d->data.size() + 1];
+ Unicode::UTF8 *targetBuffer = new Unicode::UTF8[outputBufferSize];
+
+ for(unsigned int i = 0; i < d->data.size(); i++)
+ sourceBuffer[i] = Unicode::UTF16(d->data[i]);
+
+ const Unicode::UTF16 *source = sourceBuffer;
+ Unicode::UTF8 *target = targetBuffer;
+
+ Unicode::ConversionResult result =
+ Unicode::ConvertUTF16toUTF8(&source, sourceBuffer + d->data.size(),
+ &target, targetBuffer + outputBufferSize,
+ Unicode::lenientConversion);
+
+ if(result != Unicode::conversionOK)
+ debug("String::to8Bit() - Unicode conversion error.");
+
+ int newSize = target - targetBuffer;
+ s.resize(newSize);
+ targetBuffer[newSize] = 0;
+
+ s = (char *) targetBuffer;
+
+ delete [] sourceBuffer;
+ delete [] targetBuffer;
+
+ return s;
+}
+
+const char *String::toCString(bool unicode) const
+{
+ delete [] d->CString;
+
+ std::string buffer = to8Bit(unicode);
+ d->CString = new char[buffer.size() + 1];
+ strcpy(d->CString, buffer.c_str());
+
+ return d->CString;
+}
+
+int String::find(const String &s, int offset) const
+{
+ wstring::size_type position = d->data.find(s.d->data, offset);
+
+ if(position != wstring::npos)
+ return position;
+ else
+ return -1;
+}
+
+String String::substr(uint position, uint n) const
+{
+ if(n > position + d->data.size())
+ n = d->data.size() - position;
+
+ String s;
+ s.d->data = d->data.substr(position, n);
+ return s;
+}
+
+String &String::append(const String &s)
+{
+ detach();
+ d->data += s.d->data;
+ return *this;
+}
+
+String String::upper() const
+{
+ String s;
+
+ static int shift = 'A' - 'a';
+
+ for(wstring::const_iterator it = d->data.begin(); it != d->data.end(); ++it) {
+ if(*it >= 'a' && *it <= 'z')
+ s.d->data.push_back(*it + shift);
+ else
+ s.d->data.push_back(*it);
+ }
+
+ return s;
+}
+
+TagLib::uint String::size() const
+{
+ return d->data.size();
+}
+
+bool String::isEmpty() const
+{
+ return d->data.size() == 0;
+}
+
+bool String::isNull() const
+{
+ return d == null.d;
+}
+
+ByteVector String::data(Type t) const
+{
+ ByteVector v;
+
+ switch(t) {
+
+ case Latin1:
+ {
+ for(wstring::const_iterator it = d->data.begin(); it != d->data.end(); it++)
+ v.append(char(*it));
+ break;
+ }
+ case UTF8:
+ {
+ std::string s = to8Bit(true);
+ v.setData(s.c_str(), s.length());
+ break;
+ }
+ case UTF16:
+ {
+ // Assume that if we're doing UTF16 and not UTF16BE that we want little
+ // endian encoding. (Byte Order Mark)
+
+ v.append(char(0xff));
+ v.append(char(0xfe));
+
+ for(wstring::const_iterator it = d->data.begin(); it != d->data.end(); it++) {
+
+ char c1 = *it & 0xff;
+ char c2 = *it >> 8;
+
+ v.append(c1);
+ v.append(c2);
+ }
+ }
+ case UTF16BE:
+ {
+ for(wstring::const_iterator it = d->data.begin(); it != d->data.end(); it++) {
+
+ char c1 = *it >> 8;
+ char c2 = *it & 0xff;
+
+ v.append(c2);
+ v.append(c1);
+ }
+ break;
+ }
+ }
+
+ return v;
+}
+
+int String::toInt() const
+{
+ int value = 0;
+
+ bool negative = d->data[0] == '-';
+ uint i = negative ? 1 : 0;
+
+ for(; i < d->data.size() && d->data[i] >= '0' && d->data[i] <= '9'; i++)
+ value = value * 10 + (d->data[i] - '0');
+
+ if(negative)
+ value = value * -1;
+
+ return value;
+}
+
+String String::stripWhiteSpace() const
+{
+ wstring::const_iterator begin = d->data.begin();
+ wstring::const_iterator end = d->data.end();
+
+ while(*begin == '\t' || *begin == '\n' || *begin == '\f' ||
+ *begin == '\r' || *begin == ' ' && begin != end)
+ {
+ ++begin;
+ }
+
+ if(begin == end)
+ return null;
+
+ // There must be at least one non-whitespace charater here for us to have
+ // gotten this far, so we should be safe not doing bounds checking.
+
+ do {
+ --end;
+ } while(*end == '\t' || *end == '\n' ||
+ *end == '\f' || *end == '\r' || *end == ' ');
+
+ return String(wstring(begin, end + 1));
+}
+
+String String::number(int n) // static
+{
+ if(n == 0)
+ return String("0");
+
+ String charStack;
+
+ bool negative = n < 0;
+
+ if(negative)
+ n = n * -1;
+
+ while(n > 0) {
+ int remainder = n % 10;
+ charStack += char(remainder + '0');
+ n = (n - remainder) / 10;
+ }
+
+ String s;
+
+ if(negative)
+ s += '-';
+
+ for(int i = charStack.d->data.size() - 1; i >= 0; i--)
+ s += charStack.d->data[i];
+
+ return s;
+}
+
+bool String::operator==(const String &s) const
+{
+ return d == s.d || d->data == s.d->data;
+}
+
+String &String::operator+=(const String &s)
+{
+ detach();
+
+ d->data += s.d->data;
+ return *this;
+}
+
+String &String::operator+=(const wchar_t *s)
+{
+ detach();
+
+ d->data += s;
+ return *this;
+}
+
+String &String::operator+=(const char *s)
+{
+ detach();
+
+ for(int i = 0; s[i] != 0; i++)
+ d->data += uchar(s[i]);
+ return *this;
+}
+
+String &String::operator+=(wchar_t c)
+{
+ detach();
+
+ d->data += c;
+ return *this;
+}
+
+String &String::operator+=(char c)
+{
+ d->data += uchar(c);
+ return *this;
+}
+
+String &String::operator=(const String &s)
+{
+ if(&s == this)
+ return *this;
+
+ if(d->deref())
+ delete d;
+ d = s.d;
+ d->ref();
+ return *this;
+}
+
+String &String::operator=(const std::string &s)
+{
+ if(d->deref())
+ delete d;
+
+ d = new StringPrivate;
+
+ d->data.resize(s.size());
+
+ wstring::iterator targetIt = d->data.begin();
+ for(std::string::const_iterator it = s.begin(); it != s.end(); it++) {
+ *targetIt = uchar(*it);
+ ++targetIt;
+ }
+
+ return *this;
+}
+
+String &String::operator=(const wstring &s)
+{
+ if(d->deref())
+ delete d;
+ d = new StringPrivate(s);
+ return *this;
+}
+
+String &String::operator=(const wchar_t *s)
+{
+ if(d->deref())
+ delete d;
+ d = new StringPrivate(s);
+ return *this;
+}
+
+String &String::operator=(char c)
+{
+ if(d->deref())
+ delete d;
+ d = new StringPrivate;
+ d->data += uchar(c);
+ return *this;
+}
+
+String &String::operator=(wchar_t c)
+{
+ if(d->deref())
+ delete d;
+ d = new StringPrivate;
+ d->data += c;
+ return *this;
+}
+
+String &String::operator=(const char *s)
+{
+ if(d->deref())
+ delete d;
+
+ d = new StringPrivate;
+
+ for(int i = 0; s[i] != 0; i++)
+ d->data += uchar(s[i]);
+
+ return *this;
+}
+
+String &String::operator=(const ByteVector &v)
+{
+ if(d->deref())
+ delete d;
+
+ d = new StringPrivate;
+ d->data.resize(v.size());
+ wstring::iterator targetIt = d->data.begin();
+
+ uint i = 0;
+ for(; i < v.size() && v[i]; i++) {
+ *targetIt = uchar(v[i]);
+ ++targetIt;
+ }
+
+ // If we hit a null in the ByteVector, shrink the string again.
+
+ d->data.resize(i);
+
+ return *this;
+}
+
+bool String::operator<(const String &s) const
+{
+ return d->data < s.d->data;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// protected members
+////////////////////////////////////////////////////////////////////////////////
+
+void String::detach()
+{
+ if(d->count() > 1) {
+ d->deref();
+ d = new StringPrivate(d->data);
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// private members
+////////////////////////////////////////////////////////////////////////////////
+
+void String::prepare(Type t)
+{
+ switch(t) {
+ case UTF16:
+ {
+ if(d->data.size() > 1) {
+ bool swap = d->data[0] != 0xfeff;
+ d->data.erase(d->data.begin(), d->data.begin() + 1);
+ if(swap) {
+ for(uint i = 0; i < d->data.size(); i++)
+ d->data[i] = byteSwap((unsigned short)d->data[i]);
+ }
+ }
+ else {
+ debug("String::prepare() - Invalid UTF16 string.");
+ d->data.erase(d->data.begin(), d->data.end());
+ }
+ break;
+ }
+ case UTF8:
+ {
+ int bufferSize = d->data.size() + 1;
+ Unicode::UTF8 *sourceBuffer = new Unicode::UTF8[bufferSize];
+ Unicode::UTF16 *targetBuffer = new Unicode::UTF16[bufferSize];
+
+ unsigned int i = 0;
+ for(; i < d->data.size(); i++)
+ sourceBuffer[i] = Unicode::UTF8(d->data[i]);
+ sourceBuffer[i] = 0;
+
+ const Unicode::UTF8 *source = sourceBuffer;
+ Unicode::UTF16 *target = targetBuffer;
+
+ Unicode::ConversionResult result =
+ Unicode::ConvertUTF8toUTF16(&source, sourceBuffer + bufferSize,
+ &target, targetBuffer + bufferSize,
+ Unicode::lenientConversion);
+
+ if(result != Unicode::conversionOK)
+ debug("String::prepare() - Unicode conversion error.");
+
+
+ int newSize = target - targetBuffer - 1;
+ d->data.resize(newSize);
+
+ for(int i = 0; i < newSize; i++)
+ d->data[i] = targetBuffer[i];
+
+ delete [] sourceBuffer;
+ delete [] targetBuffer;
+ }
+ default:
+ break;
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// related functions
+////////////////////////////////////////////////////////////////////////////////
+
+const TagLib::String operator+(const TagLib::String &s1, const TagLib::String &s2)
+{
+ String s(s1);
+ s.append(s2);
+ return s;
+}
+
+const TagLib::String operator+(const char *s1, const TagLib::String &s2)
+{
+ String s(s1);
+ s.append(s2);
+ return s;
+}
+
+const TagLib::String operator+(const TagLib::String &s1, const char *s2)
+{
+ String s(s1);
+ s.append(s2);
+ return s;
+}
+
+std::ostream &operator<<(std::ostream &s, const String &str)
+{
+ s << str.to8Bit();
+ return s;
+}
--- /dev/null
+/***************************************************************************
+ copyright : (C) 2002, 2003 by Scott Wheeler
+ email : wheeler@kde.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_STRING_H
+#define TAGLIB_STRING_H
+
+#include "taglib.h"
+#include "tbytevector.h"
+
+#include <string>
+
+/*!
+ * Converts a TagLib::String to a QString without a requirement to link to Qt.
+ */
+#define QStringToTString(s) TagLib::String(s.utf8().data(), TagLib::String::UTF8)
+
+/*!
+ * Converts a TagLib::String to a QString without a requirement to link to Qt.
+ */
+#define TStringToQString(s) QString::fromUtf8(s.toCString(true))
+
+namespace TagLib {
+
+ //! A \e wide string class suitable for unicode.
+
+ /*!
+ * This is an implicitly shared \e wide string. For storage it uses
+ * TagLib::wstring, but as this is an <i>implementation detail</i> this of
+ * course could change. Strings are stored internally as UTF-16BE. (Without
+ * the BOM (Byte Order Mark)
+ *
+ * The use of implicit sharing means that copying a string is cheap, the only
+ * \e cost comes into play when the copy is modified. Prior to that the string
+ * just has a pointer to the data of the \e parent String. This also makes
+ * this class suitable as a function return type.
+ *
+ * In addition to adding implicit sharing, this class keeps track of four
+ * possible encodings, which are the four supported by the ID3v2 standard.
+ */
+
+ class String
+ {
+ public:
+ /**
+ * The four types of string encodings supported by the ID3v2 specification.
+ * ID3v1 is assumed to be Latin1 and Ogg Vorbis comments use UTF8.
+ */
+ enum Type {
+ /*!
+ * IS08859-1, or <i>Latin1</i> encoding. 8 bit characters.
+ */
+ Latin1 = 0,
+ /*!
+ * UTF16 with a <i>byte order mark</i>. 16 bit characters.
+ */
+ UTF16 = 1,
+ /*!
+ * UTF16 <i>big endian</i>. 16 bit characters. This is the encoding used
+ * internally by TagLib.
+ */
+ UTF16BE = 2,
+ /*!
+ * UTF8 encoding. Characters are usually 8 bits but can be up to 32.
+ */
+ UTF8 = 3
+ };
+
+ /*!
+ * Constructs an empty String.
+ */
+ String();
+
+ /*!
+ * Make a shallow, implicitly shared, copy of \a s. Because this is
+ * implicitly shared, this method is lightweight and suitable for
+ * pass-by-value usage.
+ */
+ String(const String &s);
+
+ /*!
+ * Makes a deep copy of the data in \a s.
+ *
+ * \note This should only be used with the 8-bit codecs Latin1 and UTF8, when
+ * used with other codecs it will simply print a warning and exit.
+ */
+ String(const std::string &s, Type t = Latin1);
+
+ /*!
+ * Makes a deep copy of the data in \a s.
+ */
+ String(const wstring &s, Type t = UTF16BE);
+
+ /*!
+ * Makes a deep copy of the data in \a s.
+ */
+ String(const wchar_t *s, Type t = UTF16BE);
+
+ /*!
+ * Makes a deep copy of the data in \a c.
+ *
+ * \note This should only be used with the 8-bit codecs Latin1 and UTF8, when
+ * used with other codecs it will simply print a warning and exit.
+ */
+ String(char c, Type t = Latin1);
+
+ /*!
+ * Makes a deep copy of the data in \a c.
+ */
+ String(wchar_t c, Type t = Latin1);
+
+
+ /*!
+ * Makes a deep copy of the data in \a s.
+ *
+ * \note This should only be used with the 8-bit codecs Latin1 and UTF8, when
+ * used with other codecs it will simply print a warning and exit.
+ */
+ String(const char *s, Type t = Latin1);
+
+ /*!
+ * Makes a deep copy of the data in \a s.
+ *
+ * \note This should only be used with the 8-bit codecs Latin1 and UTF8, when
+ * used with other codecs it will simply print a warning and exit.
+ */
+ String(const ByteVector &v, Type t = Latin1);
+
+ /*!
+ * Destroys this String instance.
+ */
+ virtual ~String();
+
+ /*!
+ * If \a unicode if false (the default) this will return a \e Latin1 encoded
+ * std::string. If it is true the returned std::wstring will be UTF-8
+ * encoded.
+ */
+ std::string to8Bit(bool unicode = false) const;
+
+ /*!
+ * Creates and returns a C-String based on the data. This string is still
+ * owned by the String (class) and as such should not be deleted by the user.
+ *
+ * If \a unicode if false (the default) this string will be encoded in
+ * \e Latin1. If it is true the returned C-String will be UTF-8 encoded.
+ *
+ * This string remains valid until the String instance is destroyed or
+ * another export method is called.
+ *
+ * \warning This however has the side effect that this C-String will remain
+ * in memory <b>in addition to</b> other memory that is consumed by the
+ * String instance. So, this method should not be used on large strings or
+ * where memory is critical.
+ */
+ const char *toCString(bool unicode = false) const;
+
+ /*!
+ * Finds the first occurance of pattern \a s in this string starting from
+ * \a offset. If the pattern is not found, -1 is returned.
+ */
+ int find(const String &s, int offset = 0) const;
+
+ /*!
+ * Extract a substring from this string starting at \a position and
+ * continuing for \a n characters.
+ */
+ String substr(uint position, uint n = 0xffffffff) const;
+
+ /*!
+ * Append \a s to the current string and return a reference to the current
+ * string.
+ */
+ String &append(const String &s);
+
+ /*!
+ * Returns an upper case version of the string.
+ *
+ * \warning This only works for the characters in US-ASCII, i.e. A-Z.
+ */
+ String upper() const;
+
+ /*!
+ * Returns the size of the string.
+ */
+ uint size() const;
+
+ /*!
+ * Returns true if the string is empty.
+ *
+ * \see isNull()
+ */
+ bool isEmpty() const;
+
+ /*!
+ * Returns true if this string is null -- i.e. it is a copy of the
+ * String::null string.
+ *
+ * \note A string can be empty and not null.
+ * \see isEmpty()
+ */
+ bool isNull() const;
+
+ /*!
+ * Returns a ByteVector containing the string's data. If \a t is Latin1 or
+ * UTF8, this will return a vector of 8 bit characters, otherwise it will use
+ * 16 bit characters.
+ */
+ ByteVector data(Type t) const;
+
+ /*!
+ * Convert the string to an integer.
+ */
+ int toInt() const;
+
+ /*!
+ * Returns a string with the leading and trailing whitespace stripped.
+ */
+ String stripWhiteSpace() const;
+
+ /*!
+ * Converts the base-10 integer \a n to a string.
+ */
+ static String number(int n);
+
+ /*!
+ * Compares each character of the String with each character of \a s and
+ * returns true if the strings match.
+ */
+ bool operator==(const String &s) const;
+
+ /*!
+ * Appends \a s to the end of the String.
+ */
+ String &operator+=(const String &s);
+
+ /*!
+ * Appends \a s to the end of the String.
+ */
+ String &operator+=(const wchar_t* s);
+
+ /*!
+ * Appends \a s to the end of the String.
+ */
+ String &operator+=(const char* s);
+
+ /*!
+ * Appends \a s to the end of the String.
+ */
+ String &operator+=(wchar_t c);
+
+ /*!
+ * Appends \a c to the end of the String.
+ */
+ String &operator+=(char c);
+
+ /*!
+ * Performs a shallow, implicitly shared, copy of \a s, overwriting the
+ * String's current data.
+ */
+ String &operator=(const String &s);
+
+ /*!
+ * Performs a deep copy of the data in \a s.
+ */
+ String &operator=(const std::string &s);
+
+ /*!
+ * Performs a deep copy of the data in \a s.
+ */
+ String &operator=(const wstring &s);
+
+ /*!
+ * Performs a deep copy of the data in \a s.
+ */
+ String &operator=(const wchar_t *s);
+
+ /*!
+ * Performs a deep copy of the data in \a s.
+ */
+ String &operator=(char c);
+
+ /*!
+ * Performs a deep copy of the data in \a s.
+ */
+ String &operator=(wchar_t c);
+
+ /*!
+ * Performs a deep copy of the data in \a s.
+ */
+ String &operator=(const char *s);
+
+ /*!
+ * Performs a deep copy of the data in \a v.
+ */
+ String &operator=(const ByteVector &v);
+
+ /*!
+ * To be able to use this class in a Map, this operator needed to be
+ * implemented. Returns true if \a s is less than this string in a bytewise
+ * comparison.
+ */
+ bool operator<(const String &s) const;
+
+ /*!
+ * A null string provided for convenience.
+ */
+ static String null;
+
+ protected:
+ /*
+ * If this String is being shared via implicit sharing, do a deep copy of the
+ * data and separate from the shared members. This should be called by all
+ * non-const subclass members.
+ */
+ void detach();
+
+ private:
+ /*!
+ * This checks to see if the string is in \e UTF-16 (with BOM) or \e UTF-8
+ * format and if so converts it to \e UTF-16BE for internal use. \e Latin1
+ * does not require conversion since it is a subset of \e UTF-16BE and
+ * \e UTF16-BE requires no conversion since it is used internally.
+ */
+ void prepare(Type t);
+
+ class StringPrivate;
+ StringPrivate *d;
+ };
+
+}
+
+/*!
+ * \relates TagLib::String
+ */
+const TagLib::String operator+(const TagLib::String &s1, const TagLib::String &s2);
+
+/*!
+ * \relates TagLib::String
+ */
+const TagLib::String operator+(const char *s1, const TagLib::String &s2);
+
+/*!
+ * \relates TagLib::String
+ */
+const TagLib::String operator+(const TagLib::String &s1, const char *s2);
+
+
+/*!
+ * \relates TagLib::String
+ * Send the string to an output stream.
+ */
+std::ostream &operator<<(std::ostream &s, const TagLib::String &str);
+
+#endif
--- /dev/null
+/***************************************************************************
+ copyright : (C) 2002, 2003 by Scott Wheeler
+ email : wheeler@kde.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 "tstringlist.h"
+
+using namespace TagLib;
+
+class StringListPrivate
+{
+
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// static members
+////////////////////////////////////////////////////////////////////////////////
+
+StringList StringList::split(const String &s, const String &pattern)
+{
+ StringList l;
+
+ int previousOffset = 0;
+ for(int offset = s.find(pattern); offset != -1; offset = s.find(pattern, offset + 1)) {
+ l.append(s.substr(previousOffset, offset - previousOffset));
+ previousOffset = offset + 1;
+ }
+
+ l.append(s.substr(previousOffset, s.size() - previousOffset));
+
+ return l;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// public members
+////////////////////////////////////////////////////////////////////////////////
+
+StringList::StringList() : List<String>()
+{
+
+}
+
+StringList::StringList(const StringList &l) : List<String>(l)
+{
+
+}
+
+StringList::StringList(const String &s) : List<String>()
+{
+ append(s);
+}
+
+StringList::~StringList()
+{
+
+}
+
+String StringList::toString(const String &separator) const
+{
+ String s;
+
+ ConstIterator it = begin();
+
+ while(it != end()) {
+ s += *it;
+ it++;
+ if(it != end())
+ s += separator;
+ }
+
+ return s;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// related functions
+////////////////////////////////////////////////////////////////////////////////
+
+std::ostream &operator<<(std::ostream &s, const StringList &l)
+{
+ s << l.toString();
+ return s;
+}
--- /dev/null
+/***************************************************************************
+ copyright : (C) 2002, 2003 by Scott Wheeler
+ email : wheeler@kde.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_STRINGLIST_H
+#define TAGLIB_STRINGLIST_H
+
+#include "tstring.h"
+#include "tlist.h"
+
+#include <iostream>
+
+namespace TagLib {
+
+ //! A list of strings
+
+ /*!
+ * This is a spcialization of the List class with some members convention for
+ * string operations.
+ */
+
+ class StringList : public List<String>
+ {
+ public:
+
+ /*!
+ * Constructs an empty StringList.
+ */
+ StringList();
+
+ /*!
+ * Make a shallow, implicitly shared, copy of \a l. Because this is
+ * implicitly shared, this method is lightweight and suitable for
+ * pass-by-value usage.
+ */
+ StringList(const StringList &l);
+
+ /*!
+ * Constructs a StringList with \a s as a member.
+ */
+ StringList(const String &s);
+
+ /*!
+ * Destroys this StringList instance.
+ */
+ virtual ~StringList();
+
+ /*!
+ * Concatenate the list of strings into one string separated by \a separator.
+ */
+ String toString(const String &separator = " ") const;
+
+ /*!
+ * Splits the String \a s into several strings at \a pattern. This will not include
+ * the pattern in the returned strings.
+ */
+ static StringList split(const String &s, const String &pattern);
+
+ private:
+ class StringListPrivate;
+ StringListPrivate *d;
+ };
+
+}
+
+/*!
+ * \related TagLib::StringList
+ * Send the StringList to an output stream.
+ */
+std::ostream &operator<<(std::ostream &s, const TagLib::StringList &l);
+
+#endif
--- /dev/null
+/*******************************************************************************
+ * *
+ * THIS FILE IS INCLUDED IN TAGLIB, BUT IS NOT COPYRIGHTED BY THE TAGLIB *
+ * AUTHORS, NOT PART OF THE TAGLIB API AND COULD GO AWAY AT ANY POINT IN TIME. *
+ * AS SUCH IT SHOULD BE CONSIERED FOR INTERNAL USE ONLY. *
+ * *
+ *******************************************************************************/
+
+/*
+ * Copyright 2001 Unicode, Inc.
+ *
+ * Disclaimer
+ *
+ * This source code is provided as is by Unicode, Inc. No claims are
+ * made as to fitness for any particular purpose. No warranties of any
+ * kind are expressed or implied. The recipient agrees to determine
+ * applicability of information provided. If this file has been
+ * purchased on magnetic or optical media from Unicode, Inc., the
+ * sole remedy for any claim will be exchange of defective media
+ * within 90 days of receipt.
+ *
+ * Limitations on Rights to Redistribute This Code
+ *
+ * Unicode, Inc. hereby grants the right to freely use the information
+ * supplied in this file in the creation of products supporting the
+ * Unicode Standard, and to make copies of this file in any form
+ * for internal or external distribution as long as this notice
+ * remains attached.
+ */
+
+/*
+ * This file has been modified by Scott Wheeler <wheeler@kde.org> to remove
+ * the UTF32 conversion functions and to place the appropriate functions
+ * in their own C++ namespace.
+ */
+
+/* ---------------------------------------------------------------------
+
+ Conversions between UTF32, UTF-16, and UTF-8. Source code file.
+ Author: Mark E. Davis, 1994.
+ Rev History: Rick McGowan, fixes & updates May 2001.
+ Sept 2001: fixed const & error conditions per
+ mods suggested by S. Parent & A. Lillich.
+
+ See the header file "ConvertUTF.h" for complete documentation.
+
+------------------------------------------------------------------------ */
+
+
+#include "unicode.h"
+#include <stdio.h>
+
+#define UNI_SUR_HIGH_START (UTF32)0xD800
+#define UNI_SUR_HIGH_END (UTF32)0xDBFF
+#define UNI_SUR_LOW_START (UTF32)0xDC00
+#define UNI_SUR_LOW_END (UTF32)0xDFFF
+#define false 0
+#define true 1
+
+namespace Unicode {
+
+static const int halfShift = 10; /* used for shifting by 10 bits */
+
+static const UTF32 halfBase = 0x0010000UL;
+static const UTF32 halfMask = 0x3FFUL;
+
+/*
+ * Index into the table below with the first byte of a UTF-8 sequence to
+ * get the number of trailing bytes that are supposed to follow it.
+ */
+static const char trailingBytesForUTF8[256] = {
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
+ 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 3,3,3,3,3,3,3,3,4,4,4,4,5,5,5,5
+};
+
+/*
+ * Magic values subtracted from a buffer value during UTF8 conversion.
+ * This table contains as many values as there might be trailing bytes
+ * in a UTF-8 sequence.
+ */
+static const UTF32 offsetsFromUTF8[6] = { 0x00000000UL, 0x00003080UL, 0x000E2080UL,
+ 0x03C82080UL, 0xFA082080UL, 0x82082080UL };
+
+/*
+ * Once the bits are split out into bytes of UTF-8, this is a mask OR-ed
+ * into the first byte, depending on how many bytes follow. There are
+ * as many entries in this table as there are UTF-8 sequence types.
+ * (I.e., one byte sequence, two byte... six byte sequence.)
+ */
+static const UTF8 firstByteMark[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
+
+/* --------------------------------------------------------------------- */
+
+/* The interface converts a whole buffer to avoid function-call overhead.
+ * Constants have been gathered. Loops & conditionals have been removed as
+ * much as possible for efficiency, in favor of drop-through switches.
+ * (See "Note A" at the bottom of the file for equivalent code.)
+ * If your compiler supports it, the "isLegalUTF8" call can be turned
+ * into an inline function.
+ */
+
+/* --------------------------------------------------------------------- */
+
+ConversionResult ConvertUTF16toUTF8 (
+ const UTF16** sourceStart, const UTF16* sourceEnd,
+ UTF8** targetStart, UTF8* targetEnd, ConversionFlags flags) {
+ ConversionResult result = conversionOK;
+ const UTF16* source = *sourceStart;
+ UTF8* target = *targetStart;
+ while (source < sourceEnd) {
+ UTF32 ch;
+ unsigned short bytesToWrite = 0;
+ const UTF32 byteMask = 0xBF;
+ const UTF32 byteMark = 0x80;
+ const UTF16* oldSource = source; /* In case we have to back up because of target overflow. */
+ ch = *source++;
+ /* If we have a surrogate pair, convert to UTF32 first. */
+ if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_HIGH_END && source < sourceEnd) {
+ UTF32 ch2 = *source;
+ if (ch2 >= UNI_SUR_LOW_START && ch2 <= UNI_SUR_LOW_END) {
+ ch = ((ch - UNI_SUR_HIGH_START) << halfShift)
+ + (ch2 - UNI_SUR_LOW_START) + halfBase;
+ ++source;
+ } else if (flags == strictConversion) { /* it's an unpaired high surrogate */
+ --source; /* return to the illegal value itself */
+ result = sourceIllegal;
+ break;
+ }
+ } else if ((flags == strictConversion) && (ch >= UNI_SUR_LOW_START && ch <= UNI_SUR_LOW_END)) {
+ --source; /* return to the illegal value itself */
+ result = sourceIllegal;
+ break;
+ }
+ /* Figure out how many bytes the result will require */
+ if (ch < (UTF32)0x80) { bytesToWrite = 1;
+ } else if (ch < (UTF32)0x800) { bytesToWrite = 2;
+ } else if (ch < (UTF32)0x10000) { bytesToWrite = 3;
+ } else if (ch < (UTF32)0x200000) { bytesToWrite = 4;
+ } else { bytesToWrite = 2;
+ ch = UNI_REPLACEMENT_CHAR;
+ }
+ // printf("bytes to write = %i\n", bytesToWrite);
+ target += bytesToWrite;
+ if (target > targetEnd) {
+ source = oldSource; /* Back up source pointer! */
+ target -= bytesToWrite; result = targetExhausted; break;
+ }
+ switch (bytesToWrite) { /* note: everything falls through. */
+ case 4: *--target = (ch | byteMark) & byteMask; ch >>= 6;
+ case 3: *--target = (ch | byteMark) & byteMask; ch >>= 6;
+ case 2: *--target = (ch | byteMark) & byteMask; ch >>= 6;
+ case 1: *--target = ch | firstByteMark[bytesToWrite];
+ }
+ target += bytesToWrite;
+ }
+ *sourceStart = source;
+ *targetStart = target;
+ return result;
+}
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * Utility routine to tell whether a sequence of bytes is legal UTF-8.
+ * This must be called with the length pre-determined by the first byte.
+ * If not calling this from ConvertUTF8to*, then the length can be set by:
+ * length = trailingBytesForUTF8[*source]+1;
+ * and the sequence is illegal right away if there aren't that many bytes
+ * available.
+ * If presented with a length > 4, this returns false. The Unicode
+ * definition of UTF-8 goes up to 4-byte sequences.
+ */
+
+static Boolean isLegalUTF8(const UTF8 *source, int length) {
+ UTF8 a;
+ const UTF8 *srcptr = source+length;
+ switch (length) {
+ default: return false;
+ /* Everything else falls through when "true"... */
+ case 4: if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return false;
+ case 3: if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return false;
+ case 2: if ((a = (*--srcptr)) > 0xBF) return false;
+ switch (*source) {
+ /* no fall-through in this inner switch */
+ case 0xE0: if (a < 0xA0) return false; break;
+ case 0xF0: if (a < 0x90) return false; break;
+ case 0xF4: if (a > 0x8F) return false; break;
+ default: if (a < 0x80) return false;
+ }
+ case 1: if (*source >= 0x80 && *source < 0xC2) return false;
+ if (*source > 0xF4) return false;
+ }
+ return true;
+}
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * Exported function to return whether a UTF-8 sequence is legal or not.
+ * This is not used here; it's just exported.
+ */
+Boolean isLegalUTF8Sequence(const UTF8 *source, const UTF8 *sourceEnd) {
+ int length = trailingBytesForUTF8[*source]+1;
+ if (source+length > sourceEnd) {
+ return false;
+ }
+ return isLegalUTF8(source, length);
+}
+
+/* --------------------------------------------------------------------- */
+
+ConversionResult ConvertUTF8toUTF16 (
+ const UTF8** sourceStart, const UTF8* sourceEnd,
+ UTF16** targetStart, UTF16* targetEnd, ConversionFlags flags) {
+ ConversionResult result = conversionOK;
+ const UTF8* source = *sourceStart;
+ UTF16* target = *targetStart;
+ while (source < sourceEnd) {
+ UTF32 ch = 0;
+ unsigned short extraBytesToRead = trailingBytesForUTF8[*source];
+ if (source + extraBytesToRead >= sourceEnd) {
+ result = sourceExhausted; break;
+ }
+ /* Do this check whether lenient or strict */
+ if (! isLegalUTF8(source, extraBytesToRead+1)) {
+ result = sourceIllegal;
+ break;
+ }
+ /*
+ * The cases all fall through. See "Note A" below.
+ */
+ switch (extraBytesToRead) {
+ case 3: ch += *source++; ch <<= 6;
+ case 2: ch += *source++; ch <<= 6;
+ case 1: ch += *source++; ch <<= 6;
+ case 0: ch += *source++;
+ }
+ ch -= offsetsFromUTF8[extraBytesToRead];
+
+ if (target >= targetEnd) {
+ source -= (extraBytesToRead+1); /* Back up source pointer! */
+ result = targetExhausted; break;
+ }
+ if (ch <= UNI_MAX_BMP) { /* Target is a character <= 0xFFFF */
+ if ((flags == strictConversion) && (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END)) {
+ source -= (extraBytesToRead+1); /* return to the illegal value itself */
+ result = sourceIllegal;
+ break;
+ } else {
+ *target++ = ch; /* normal case */
+ }
+ } else if (ch > UNI_MAX_UTF16) {
+ if (flags == strictConversion) {
+ result = sourceIllegal;
+ source -= (extraBytesToRead+1); /* return to the start */
+ break; /* Bail out; shouldn't continue */
+ } else {
+ *target++ = UNI_REPLACEMENT_CHAR;
+ }
+ } else {
+ /* target is a character in range 0xFFFF - 0x10FFFF. */
+ if (target + 1 >= targetEnd) {
+ source -= (extraBytesToRead+1); /* Back up source pointer! */
+ result = targetExhausted; break;
+ }
+ ch -= halfBase;
+ *target++ = (ch >> halfShift) + UNI_SUR_HIGH_START;
+ *target++ = (ch & halfMask) + UNI_SUR_LOW_START;
+ }
+ }
+ *sourceStart = source;
+ *targetStart = target;
+ return result;
+}
+
+}
+
+/* ---------------------------------------------------------------------
+
+ Note A.
+ The fall-through switches in UTF-8 reading code save a
+ temp variable, some decrements & conditionals. The switches
+ are equivalent to the following loop:
+ {
+ int tmpBytesToRead = extraBytesToRead+1;
+ do {
+ ch += *source++;
+ --tmpBytesToRead;
+ if (tmpBytesToRead) ch <<= 6;
+ } while (tmpBytesToRead > 0);
+ }
+ In UTF-8 writing code, the switches on "bytesToWrite" are
+ similarly unrolled loops.
+
+ --------------------------------------------------------------------- */
+
+
--- /dev/null
+#ifndef TAGLIB_UNICODE_H
+#define TAGLIB_UNICODE_H
+
+/*******************************************************************************
+ * *
+ * THIS FILE IS INCLUDED IN TAGLIB, BUT IS NOT COPYRIGHTED BY THE TAGLIB *
+ * AUTHORS, NOT PART OF THE TAGLIB API AND COULD GO AWAY AT ANY POINT IN TIME. *
+ * AS SUCH IT SHOULD BE CONSIERED FOR INTERNAL USE ONLY. *
+ * *
+ *******************************************************************************/
+
+#ifndef DO_NOT_DOCUMENT // tell Doxygen not to document this header
+
+/*
+ * Copyright 2001 Unicode, Inc.
+ *
+ * Disclaimer
+ *
+ * This source code is provided as is by Unicode, Inc. No claims are
+ * made as to fitness for any particular purpose. No warranties of any
+ * kind are expressed or implied. The recipient agrees to determine
+ * applicability of information provided. If this file has been
+ * purchased on magnetic or optical media from Unicode, Inc., the
+ * sole remedy for any claim will be exchange of defective media
+ * within 90 days of receipt.
+ *
+ * Limitations on Rights to Redistribute This Code
+ *
+ * Unicode, Inc. hereby grants the right to freely use the information
+ * supplied in this file in the creation of products supporting the
+ * Unicode Standard, and to make copies of this file in any form
+ * for internal or external distribution as long as this notice
+ * remains attached.
+ */
+
+/*
+ * This file has been modified by Scott Wheeler <wheeler@kde.org> to remove
+ * the UTF32 conversion functions and to place the appropriate functions
+ * in their own C++ namespace.
+ */
+
+/* ---------------------------------------------------------------------
+
+ Conversions between UTF32, UTF-16, and UTF-8. Header file.
+
+ Several funtions are included here, forming a complete set of
+ conversions between the three formats. UTF-7 is not included
+ here, but is handled in a separate source file.
+
+ Each of these routines takes pointers to input buffers and output
+ buffers. The input buffers are const.
+
+ Each routine converts the text between *sourceStart and sourceEnd,
+ putting the result into the buffer between *targetStart and
+ targetEnd. Note: the end pointers are *after* the last item: e.g.
+ *(sourceEnd - 1) is the last item.
+
+ The return result indicates whether the conversion was successful,
+ and if not, whether the problem was in the source or target buffers.
+ (Only the first encountered problem is indicated.)
+
+ After the conversion, *sourceStart and *targetStart are both
+ updated to point to the end of last text successfully converted in
+ the respective buffers.
+
+ Input parameters:
+ sourceStart - pointer to a pointer to the source buffer.
+ The contents of this are modified on return so that
+ it points at the next thing to be converted.
+ targetStart - similarly, pointer to pointer to the target buffer.
+ sourceEnd, targetEnd - respectively pointers to the ends of the
+ two buffers, for overflow checking only.
+
+ These conversion functions take a ConversionFlags argument. When this
+ flag is set to strict, both irregular sequences and isolated surrogates
+ will cause an error. When the flag is set to lenient, both irregular
+ sequences and isolated surrogates are converted.
+
+ Whether the flag is strict or lenient, all illegal sequences will cause
+ an error return. This includes sequences such as: <F4 90 80 80>, <C0 80>,
+ or <A0> in UTF-8, and values above 0x10FFFF in UTF-32. Conformant code
+ must check for illegal sequences.
+
+ When the flag is set to lenient, characters over 0x10FFFF are converted
+ to the replacement character; otherwise (when the flag is set to strict)
+ they constitute an error.
+
+ Output parameters:
+ The value "sourceIllegal" is returned from some routines if the input
+ sequence is malformed. When "sourceIllegal" is returned, the source
+ value will point to the illegal value that caused the problem. E.g.,
+ in UTF-8 when a sequence is malformed, it points to the start of the
+ malformed sequence.
+
+ Author: Mark E. Davis, 1994.
+ Rev History: Rick McGowan, fixes & updates May 2001.
+ Fixes & updates, Sept 2001.
+
+------------------------------------------------------------------------ */
+
+/* ---------------------------------------------------------------------
+ The following 4 definitions are compiler-specific.
+ The C standard does not guarantee that wchar_t has at least
+ 16 bits, so wchar_t is no less portable than unsigned short!
+ All should be unsigned values to avoid sign extension during
+ bit mask & shift operations.
+------------------------------------------------------------------------ */
+
+/* Some fundamental constants */
+#define UNI_REPLACEMENT_CHAR (UTF32)0x0000FFFD
+#define UNI_MAX_BMP (UTF32)0x0000FFFF
+#define UNI_MAX_UTF16 (UTF32)0x0010FFFF
+#define UNI_MAX_UTF32 (UTF32)0x7FFFFFFF
+
+namespace Unicode {
+
+typedef unsigned long UTF32; /* at least 32 bits */
+typedef unsigned short UTF16; /* at least 16 bits */
+typedef unsigned char UTF8; /* typically 8 bits */
+typedef unsigned char Boolean; /* 0 or 1 */
+
+typedef enum {
+ conversionOK = 0, /* conversion successful */
+ sourceExhausted = 1, /* partial character in source, but hit end */
+ targetExhausted = 2, /* insuff. room in target for conversion */
+ sourceIllegal = 3 /* source sequence is illegal/malformed */
+} ConversionResult;
+
+typedef enum {
+ strictConversion = 0,
+ lenientConversion
+} ConversionFlags;
+
+ConversionResult ConvertUTF8toUTF16 (
+ const UTF8** sourceStart, const UTF8* sourceEnd,
+ UTF16** targetStart, UTF16* targetEnd, ConversionFlags flags);
+
+ConversionResult ConvertUTF16toUTF8 (
+ const UTF16** sourceStart, const UTF16* sourceEnd,
+ UTF8** targetStart, UTF8* targetEnd, ConversionFlags flags);
+
+Boolean isLegalUTF8Sequence(const UTF8 *source, const UTF8 *sourceEnd);
+
+} // namespace Unicode
+
+/* --------------------------------------------------------------------- */
+
+#endif
+#endif