]> granicus.if.org Git - taglib/commitdiff
Add my old WMA and MP4 code. It is disabled by default, must be explicitly enabled...
authorLukáš Lalinský <lalinsky@gmail.com>
Wed, 12 Nov 2008 08:17:11 +0000 (08:17 +0000)
committerLukáš Lalinský <lalinsky@gmail.com>
Wed, 12 Nov 2008 08:17:11 +0000 (08:17 +0000)
Scott: If you think this is really a bad idea, please revert.

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

41 files changed:
CMakeLists.txt
bindings/c/CMakeLists.txt
bindings/c/Makefile.am
bindings/c/tag_c.cpp
bindings/c/tag_c.h
config-taglib.h.cmake
taglib/CMakeLists.txt
taglib/Makefile.am
taglib/asf/CMakeLists.txt [new file with mode: 0644]
taglib/asf/Makefile.am [new file with mode: 0644]
taglib/asf/asfattribute.cpp [new file with mode: 0644]
taglib/asf/asfattribute.h [new file with mode: 0644]
taglib/asf/asffile.cpp [new file with mode: 0644]
taglib/asf/asffile.h [new file with mode: 0644]
taglib/asf/asfproperties.cpp [new file with mode: 0644]
taglib/asf/asfproperties.h [new file with mode: 0644]
taglib/asf/asftag.cpp [new file with mode: 0644]
taglib/asf/asftag.h [new file with mode: 0644]
taglib/fileref.cpp
taglib/mp4/CMakeLists.txt [new file with mode: 0644]
taglib/mp4/Makefile.am [new file with mode: 0644]
taglib/mp4/mp4atom.cpp [new file with mode: 0644]
taglib/mp4/mp4atom.h [new file with mode: 0644]
taglib/mp4/mp4file.cpp [new file with mode: 0644]
taglib/mp4/mp4file.h [new file with mode: 0644]
taglib/mp4/mp4item.cpp [new file with mode: 0644]
taglib/mp4/mp4item.h [new file with mode: 0644]
taglib/mp4/mp4properties.cpp [new file with mode: 0644]
taglib/mp4/mp4properties.h [new file with mode: 0644]
taglib/mp4/mp4tag.cpp [new file with mode: 0644]
taglib/mp4/mp4tag.h [new file with mode: 0644]
taglib/taglib.pro
tests/CMakeLists.txt
tests/data/click.wv [new file with mode: 0644]
tests/data/has-tags.m4a [new file with mode: 0644]
tests/data/no-tags.3g2 [new file with mode: 0644]
tests/data/no-tags.m4a [new file with mode: 0644]
tests/data/silence-1.wma [new file with mode: 0644]
tests/test_asf.cpp [new file with mode: 0644]
tests/test_fileref.cpp
tests/test_mp4.cpp [new file with mode: 0644]

index 3128948bca509d7645e971cc530a13224b4fe533..b21e9dc4007badb61a3816bc048de52560d9dc53 100644 (file)
@@ -4,6 +4,10 @@ OPTION(BUILD_TESTS "Build the test suite"  OFF)
 OPTION(BUILD_EXAMPLES "Build the examples"  OFF)
 
 OPTION(NO_ITUNES_HACKS "Disable workarounds for iTunes bugs"  OFF)
+OPTION(WITH_ASF "Enable ASF tag reading/writing code"  OFF)
+OPTION(WITH_MP4 "Enable MP4 tag reading/writing code"  OFF)
+
+add_definitions(-DHAVE_CONFIG_H)
 
 #add some KDE specific stuff
 set(LIB_SUFFIX "" CACHE STRING "Define suffix of directory name (32/64)" )
index 7dd5a16419ffe7ec0fc8cf0f133f33cddffb88a1..3546bbc7e32d79987388e6eff2298cf07a72d145 100644 (file)
@@ -1,11 +1,13 @@
 INCLUDE_DIRECTORIES( ${CMAKE_CURRENT_SOURCE_DIR}/../../taglib
                     ${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/toolkit
+                    ${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/asf
                     ${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/mpeg
                     ${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/ogg
                     ${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/ogg/vorbis
                     ${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/ogg/flac
                     ${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/flac
                     ${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/mpc
+                    ${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/mp4
                     ${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/mpeg/id3v2
                     ${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/wavpack
                     ${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/ogg/speex
index 09e89703fbfcf99307d7059fcd248cbb10497d2f..8bb011025bd0b580d859373c61773beef0fa3fe6 100644 (file)
@@ -1,6 +1,7 @@
 INCLUDES = \
        -I$(top_srcdir)/taglib \
        -I$(top_srcdir)/taglib/toolkit \
+       -I$(top_srcdir)/taglib/asf \
        -I$(top_srcdir)/taglib/mpeg \
        -I$(top_srcdir)/taglib/ogg \
        -I$(top_srcdir)/taglib/ogg/vorbis \
@@ -8,6 +9,7 @@ INCLUDES = \
        -I$(top_srcdir)/taglib/ogg/flac \
        -I$(top_srcdir)/taglib/flac \
        -I$(top_srcdir)/taglib/mpc \
+       -I$(top_srcdir)/taglib/mp4 \
        -I$(top_srcdir)/taglib/mpeg/id3v2 \
        -I$(top_srcdir)/taglib/wavpack \
        -I$(top_srcdir)/taglib/trueaudio \
index 377d92c733f027384046c52ab2b7e8972ae11c54..f5f17656dbcf5918fcb7629f8703628692bd522e 100644 (file)
  *   USA                                                                   *
  ***************************************************************************/
 
+#ifdef HAVE_CONFIG_H
+#include "../../config.h"
+#endif
+
 #include "tag_c.h"
 
 #include <stdlib.h>
 #include <fileref.h>
 #include <tfile.h>
+#include <asffile.h>
 #include <vorbisfile.h>
 #include <mpegfile.h>
 #include <flacfile.h>
@@ -32,6 +37,7 @@
 #include <wavpackfile.h>
 #include <speexfile.h>
 #include <trueaudiofile.h>
+#include <mp4file.h>
 #include <tag.h>
 #include <string.h>
 #include <id3v2framefactory.h>
@@ -80,6 +86,14 @@ TagLib_File *taglib_file_new_type(const char *filename, TagLib_File_Type type)
     return reinterpret_cast<TagLib_File *>(new Ogg::Speex::File(filename));
   case TagLib_File_TrueAudio:
     return reinterpret_cast<TagLib_File *>(new TrueAudio::File(filename));
+#ifdef WITH_MP4
+  case TagLib_File_MP4:
+    return reinterpret_cast<TagLib_File *>(new MP4::File(filename));
+#endif
+#ifdef WITH_ASF
+  case TagLib_File_ASF:
+    return reinterpret_cast<TagLib_File *>(new ASF::File(filename));
+#endif
   }
 
   return 0;
index d70629ece096110de939ab90111d386a8cd801bf..055da4e35c47d318c92753cdadcd89ae38b1fa4e 100644 (file)
@@ -89,7 +89,9 @@ typedef enum {
   TagLib_File_OggFlac,
   TagLib_File_WavPack,
   TagLib_File_Speex,
-  TagLib_File_TrueAudio
+  TagLib_File_TrueAudio,
+  TagLib_File_MP4,
+  TagLib_File_ASF
 } TagLib_File_Type;
 
 /*!
index 56674d6bc3950661e63a61374ed0f1dccd64ae1a..69c1adbbdaa1a72ccdab3fe81663af9d95fef1f5 100644 (file)
@@ -7,3 +7,5 @@
 #cmakedefine   HAVE_ZLIB 1
 
 #cmakedefine   NO_ITUNES_HACKS 1
+#cmakedefine   WITH_ASF 1
+#cmakedefine   WITH_MP4 1
index 4e9b7139cae8d80c12bae1404365f2f5619034e7..c0a18cbc981446523fc8c8f36d3b277b8ae83979 100644 (file)
@@ -2,11 +2,13 @@
 set(CMAKE_INCLUDE_CURRENT_DIR ON)
 INCLUDE_DIRECTORIES(
     ${CMAKE_CURRENT_SOURCE_DIR}/toolkit
+    ${CMAKE_CURRENT_SOURCE_DIR}/asf
     ${CMAKE_CURRENT_SOURCE_DIR}/mpeg
     ${CMAKE_CURRENT_SOURCE_DIR}/ogg
     ${CMAKE_CURRENT_SOURCE_DIR}/ogg/flac
     ${CMAKE_CURRENT_SOURCE_DIR}/flac
     ${CMAKE_CURRENT_SOURCE_DIR}/mpc
+    ${CMAKE_CURRENT_SOURCE_DIR}/mp4
     ${CMAKE_CURRENT_SOURCE_DIR}/ogg/vorbis
     ${CMAKE_CURRENT_SOURCE_DIR}/ogg/speex
     ${CMAKE_CURRENT_SOURCE_DIR}/mpeg/id3v2
@@ -25,11 +27,13 @@ if(ZLIB_FOUND)
 endif(ZLIB_FOUND)
 
 ADD_SUBDIRECTORY( toolkit )
+ADD_SUBDIRECTORY( asf )
 ADD_SUBDIRECTORY( mpeg )
 ADD_SUBDIRECTORY( ogg )
 ADD_SUBDIRECTORY( flac )
 ADD_SUBDIRECTORY( ape )
 ADD_SUBDIRECTORY( mpc )
+ADD_SUBDIRECTORY( mp4 )
 ADD_SUBDIRECTORY( wavpack )
 ADD_SUBDIRECTORY( trueaudio )
 ADD_SUBDIRECTORY( riff )
@@ -101,6 +105,18 @@ mpc/mpcfile.cpp
 mpc/mpcproperties.cpp
 )
 
+IF(WITH_MP4)
+SET(mp4_SRCS
+mp4/mp4file.cpp
+mp4/mp4atom.cpp
+mp4/mp4tag.cpp
+mp4/mp4item.cpp
+mp4/mp4properties.cpp
+)
+ELSE(WITH_MP4)
+SET(mp4_SRCS)
+ENDIF(WITH_MP4)
+
 SET(ape_SRCS
 ape/apetag.cpp
 ape/apefooter.cpp
@@ -122,6 +138,17 @@ trueaudio/trueaudiofile.cpp
 trueaudio/trueaudioproperties.cpp
 )
 
+IF(WITH_ASF)
+SET(asf_SRCS
+asf/asftag.cpp
+asf/asffile.cpp
+asf/asfproperties.cpp
+asf/asfattribute.cpp
+)
+ELSE(WITH_ASF)
+SET(asf_SRCS)
+ENDIF(WITH_ASF)
+
 SET(riff_SRCS
 riff/rifffile.cpp
 )
@@ -149,6 +176,7 @@ toolkit/unicode.cpp
 SET(tag_LIB_SRCS ${mpeg_SRCS} ${id3v1_SRCS} ${id3v2_SRCS} ${frames_SRCS} ${ogg_SRCS}
                 ${vorbis_SRCS} ${oggflacs_SRCS} ${mpc_SRCS} ${ape_SRCS} ${toolkit_SRCS} ${flacs_SRCS}
                 ${wavpack_SRCS} ${speex_SRCS} ${trueaudio_SRCS} ${riff_SRCS} ${aiff_SRCS} ${wav_SRCS}
+                ${mp4_SRCS} ${asf_SRCS}
                 tag.cpp
                 tagunion.cpp
                 fileref.cpp
index a75a549547e06295fc3357d67bb7dd4a5be8d86c..38f9d70edfe72430a5ac39cd6904321a570a7a7f 100644 (file)
@@ -1,4 +1,4 @@
-SUBDIRS = toolkit mpeg ogg flac ape mpc wavpack trueaudio riff
+SUBDIRS = toolkit mpeg ogg flac ape mpc wavpack trueaudio riff asf mp4
 
 INCLUDES = \
        -I$(top_srcdir)/taglib \
@@ -8,6 +8,8 @@ INCLUDES = \
        -I$(top_srcdir)/taglib/ogg/flac \
        -I$(top_srcdir)/taglib/flac \
        -I$(top_srcdir)/taglib/mpc \
+       -I$(top_srcdir)/taglib/asf \
+       -I$(top_srcdir)/taglib/mp4 \
        -I$(top_srcdir)/taglib/ogg/vorbis \
        -I$(top_srcdir)/taglib/ogg/speex \
        -I$(top_srcdir)/taglib/wavpack \
@@ -27,4 +29,5 @@ taglib_includedir = $(includedir)/taglib
 libtag_la_LDFLAGS = $(all_libraries) -no-undefined -version-info 6:0:5
 libtag_la_LIBADD = ./mpeg/libmpeg.la ./ogg/libogg.la ./flac/libflac.la ./mpc/libmpc.la \
        ./ape/libape.la ./toolkit/libtoolkit.la ./wavpack/libwavpack.la \
-       ./trueaudio/libtrueaudio.la ./riff/libriff.la
+       ./trueaudio/libtrueaudio.la ./riff/libriff.la \
+       ./mp4/libmp4.la ./asf/libasf.la
diff --git a/taglib/asf/CMakeLists.txt b/taglib/asf/CMakeLists.txt
new file mode 100644 (file)
index 0000000..1cf3569
--- /dev/null
@@ -0,0 +1 @@
+INSTALL( FILES  asffile.h asfproperties.h asftag.h asfattribute.h DESTINATION ${INCLUDE_INSTALL_DIR}/taglib)
diff --git a/taglib/asf/Makefile.am b/taglib/asf/Makefile.am
new file mode 100644 (file)
index 0000000..0147dca
--- /dev/null
@@ -0,0 +1,11 @@
+INCLUDES = \
+       -I$(top_srcdir)/taglib \
+       -I$(top_srcdir)/taglib/toolkit \
+       $(all_includes)
+
+noinst_LTLIBRARIES = libasf.la
+
+libasf_la_SOURCES = asfattribute.cpp asffile.cpp asfproperties.cpp asftag.cpp
+
+taglib_include_HEADERS = asfattribute.h asffile.h asfproperties.h asftag.h
+taglib_includedir = $(includedir)/taglib
diff --git a/taglib/asf/asfattribute.cpp b/taglib/asf/asfattribute.cpp
new file mode 100644 (file)
index 0000000..a4a4c17
--- /dev/null
@@ -0,0 +1,316 @@
+/**************************************************************************
+    copyright            : (C) 2005-2007 by Lukáš Lalinský
+    email                : lalinsky@gmail.com
+ **************************************************************************/
+
+/***************************************************************************
+ *   This library is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License version   *
+ *   2.1 as published by the Free Software Foundation.                     *
+ *                                                                         *
+ *   This library is distributed in the hope that it will be useful, but   *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   Lesser General Public License for more details.                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU Lesser General Public      *
+ *   License along with this library; if not, write to the Free Software   *
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
+ *   USA                                                                   *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifdef WITH_ASF
+
+#include <taglib.h>
+#include "asfattribute.h"
+#include "asffile.h"
+
+using namespace TagLib;
+
+class ASF::Attribute::AttributePrivate : public RefCounter
+{
+public:
+  AttributePrivate()
+    : stream(0),
+      language(0) {}
+  AttributeTypes type;
+  String stringValue;
+  ByteVector byteVectorValue;
+  union {
+    unsigned int intValue;
+    unsigned short shortValue;
+    unsigned long long longLongValue;
+    bool boolValue;
+  };
+  int stream;
+  int language;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// public members
+////////////////////////////////////////////////////////////////////////////////
+
+ASF::Attribute::Attribute()
+{
+  d = new AttributePrivate;
+  d->type = UnicodeType;
+}
+
+ASF::Attribute::Attribute(const ASF::Attribute &other)
+  : d(other.d)
+{
+  d->ref();
+}
+
+ASF::Attribute &
+ASF::Attribute::operator=(const ASF::Attribute &other)
+{
+  if(d->deref())
+    delete d;
+  d = other.d;
+  d->ref();
+  return *this;
+}
+
+ASF::Attribute::~Attribute()
+{
+  if(d->deref())
+    delete d;
+}
+
+ASF::Attribute::Attribute(const String &value)
+{
+  d = new AttributePrivate;
+  d->type = UnicodeType;
+  d->stringValue = value;
+}
+
+ASF::Attribute::Attribute(const ByteVector &value)
+{
+  d = new AttributePrivate;
+  d->type = BytesType;
+  d->byteVectorValue = value;
+}
+
+ASF::Attribute::Attribute(unsigned int value)
+{
+  d = new AttributePrivate;
+  d->type = DWordType;
+  d->intValue = value;
+}
+
+ASF::Attribute::Attribute(unsigned long long value)
+{
+  d = new AttributePrivate;
+  d->type = QWordType;
+  d->longLongValue = value;
+}
+
+ASF::Attribute::Attribute(unsigned short value)
+{
+  d = new AttributePrivate;
+  d->type = WordType;
+  d->shortValue = value;
+}
+
+ASF::Attribute::Attribute(bool value)
+{
+  d = new AttributePrivate;
+  d->type = BoolType;
+  d->boolValue = value;
+}
+
+ASF::Attribute::AttributeTypes
+ASF::Attribute::type() const
+{
+  return d->type;
+}
+
+String
+ASF::Attribute::toString() const
+{
+  return d->stringValue;
+}
+
+ByteVector
+ASF::Attribute::toByteVector() const
+{
+  return d->byteVectorValue;
+}
+
+unsigned short
+ASF::Attribute::toBool() const
+{
+  return d->shortValue;
+}
+
+unsigned short
+ASF::Attribute::toUShort() const
+{
+  return d->shortValue;
+}
+
+unsigned int
+ASF::Attribute::toUInt() const
+{
+  return d->intValue;
+}
+
+unsigned long long
+ASF::Attribute::toULongLong() const
+{
+  return d->longLongValue;
+}
+
+String
+ASF::Attribute::parse(ASF::File &f, int kind)
+{
+  int size, nameLength;
+  String name;
+
+  // extended content descriptor
+  if(kind == 0) {
+    nameLength = f.readWORD();
+    name = f.readString(nameLength);
+    d->type = ASF::Attribute::AttributeTypes(f.readWORD());
+    size = f.readWORD();
+  }
+  // metadata & metadata library
+  else {
+    int temp = f.readWORD();
+    // metadata library
+    if(kind == 2) {
+      d->language = temp;
+    }
+    d->stream = f.readWORD();
+    nameLength = f.readWORD();
+    d->type = ASF::Attribute::AttributeTypes(f.readWORD());
+    size = f.readDWORD();
+    name = f.readString(nameLength);
+  }
+
+  switch(d->type) {
+  case WordType:
+    d->shortValue = f.readWORD();
+    break;
+
+  case BoolType:
+    if(kind == 0) {
+      d->boolValue = f.readDWORD() == 1;
+    }
+    else {
+      d->boolValue = f.readWORD() == 1;
+    }
+    break;
+
+  case DWordType:
+    d->intValue = f.readDWORD();
+    break;
+
+  case QWordType:
+    d->longLongValue = f.readQWORD();
+    break;
+
+  case UnicodeType:
+    d->stringValue = f.readString(size);
+    break;
+
+  case BytesType:
+  case GuidType:
+    d->byteVectorValue = f.readBlock(size);
+    break;
+  }
+
+  return name;
+}
+
+ByteVector
+ASF::Attribute::render(const String &name, int kind) const
+{
+  ByteVector data;
+
+  switch (d->type) {
+  case WordType:
+    data.append(ByteVector::fromShort(d->shortValue, false));
+    break;
+
+  case BoolType:
+    if(kind == 0) {
+      data.append(ByteVector::fromUInt(d->boolValue ? 1 : 0, false));
+    }
+    else {
+      data.append(ByteVector::fromShort(d->boolValue ? 1 : 0, false));
+    }
+    break;
+
+  case DWordType:
+    data.append(ByteVector::fromUInt(d->intValue, false));
+    break;
+
+  case QWordType:
+    data.append(ByteVector::fromLongLong(d->longLongValue, false));
+    break;
+
+  case UnicodeType:
+    data.append(File::renderString(d->stringValue));
+    break;
+
+  case BytesType:
+  case GuidType:
+    data.append(d->byteVectorValue);
+    break;
+  }
+
+  if(kind == 0) {
+    data = File::renderString(name, true) +
+           ByteVector::fromShort((int)d->type, false) +
+           ByteVector::fromShort(data.size(), false) +
+           data;
+  }
+  else {
+    ByteVector nameData = File::renderString(name);
+    data = ByteVector::fromShort(kind == 2 ? d->language : 0, false) +
+           ByteVector::fromShort(d->stream, false) +
+           ByteVector::fromShort(nameData.size(), false) +
+           ByteVector::fromShort((int)d->type, false) +
+           ByteVector::fromUInt(data.size(), false) +
+           nameData +
+           data;
+  }
+
+  return data;
+}
+
+int
+ASF::Attribute::language() const
+{
+  return d->language;
+}
+
+void
+ASF::Attribute::setLanguage(int value)
+{
+  d->language = value;
+}
+
+int
+ASF::Attribute::stream() const
+{
+  return d->stream;
+}
+
+void
+ASF::Attribute::setStream(int value)
+{
+  d->stream = value;
+}
+
+#endif
diff --git a/taglib/asf/asfattribute.h b/taglib/asf/asfattribute.h
new file mode 100644 (file)
index 0000000..7278e90
--- /dev/null
@@ -0,0 +1,181 @@
+/**************************************************************************
+    copyright            : (C) 2005-2007 by Lukáš Lalinský
+    email                : lalinsky@gmail.com
+ **************************************************************************/
+
+/***************************************************************************
+ *   This library is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License version   *
+ *   2.1 as published by the Free Software Foundation.                     *
+ *                                                                         *
+ *   This library is distributed in the hope that it will be useful, but   *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   Lesser General Public License for more details.                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU Lesser General Public      *
+ *   License along with this library; if not, write to the Free Software   *
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
+ *   USA                                                                   *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+#ifndef TAGLIB_ASFATTRIBUTE_H
+#define TAGLIB_ASFATTRIBUTE_H
+
+#include <tstring.h>
+#include <tbytevector.h>
+#include "taglib_export.h"
+
+namespace TagLib
+{
+
+  namespace ASF
+  {
+
+    class File;
+
+    class TAGLIB_EXPORT Attribute
+    {
+    public:
+
+      /*!
+       * Enum of types an Attribute can have.
+       */
+      enum AttributeTypes {
+        UnicodeType = 0,
+        BytesType   = 1,
+        BoolType    = 2,
+        DWordType   = 3,
+        QWordType   = 4,
+        WordType    = 5,
+        GuidType    = 6
+      };
+
+      /*!
+       * Constructs an empty attribute.
+       */
+      Attribute();
+
+      /*!
+       * Constructs an attribute with \a key and a UnicodeType \a value.
+       */
+      Attribute(const String &value);
+
+      /*!
+       * Constructs an attribute with \a key and a BytesType \a value.
+       */
+      Attribute(const ByteVector &value);
+
+      /*!
+       * Constructs an attribute with \a key and a DWordType \a value.
+       */
+      Attribute(unsigned int value);
+
+      /*!
+       * Constructs an attribute with \a key and a QWordType \a value.
+       */
+      Attribute(unsigned long long value);
+
+      /*!
+       * Constructs an attribute with \a key and a WordType \a value.
+       */
+      Attribute(unsigned short value);
+
+      /*!
+       * Constructs an attribute with \a key and a BoolType \a value.
+       */
+      Attribute(bool value);
+
+      /*!
+       * Construct an attribute as a copy of \a other.
+       */
+      Attribute(const Attribute &item);
+
+      /*!
+       * Copies the contents of \a other into this item.
+       */
+      ASF::Attribute &operator=(const Attribute &other);
+
+      /*!
+       * Destroys the attribute.
+       */
+      virtual ~Attribute();
+
+      /*!
+       * Returns type of the value.
+       */
+      AttributeTypes type() const;
+
+      /*!
+       * Returns the BoolType \a value.
+       */
+      unsigned short toBool() const;
+
+      /*!
+       * Returns the WordType \a value.
+       */
+      unsigned short toUShort() const;
+
+      /*!
+       * Returns the DWordType \a value.
+       */
+      unsigned int toUInt() const;
+
+      /*!
+       * Returns the QWordType \a value.
+       */
+      unsigned long long toULongLong() const;
+
+      /*!
+       * Returns the UnicodeType \a value.
+       */
+      String toString() const;
+
+      /*!
+       * Returns the BytesType \a value.
+       */
+      ByteVector toByteVector() const;
+
+      /*!
+       * Returns the language number, or 0 is no stream number was set.
+       */
+      int language() const;
+
+      /*!
+       * Sets the language number.
+       */
+      void setLanguage(int value);
+
+      /*!
+       * Returns the stream number, or 0 is no stream number was set.
+       */
+      int stream() const;
+
+      /*!
+       * Sets the stream number.
+       */
+      void setStream(int value);
+
+#ifndef DO_NOT_DOCUMENT
+      /* THIS IS PRIVATE, DON'T TOUCH IT! */
+      String parse(ASF::File &file, int kind = 0);
+#endif
+
+    private:
+      friend class File;
+
+      ByteVector render(const String &name, int kind = 0) const;
+
+      class AttributePrivate;
+      AttributePrivate *d;
+    };
+
+  }
+
+}
+
+#endif
diff --git a/taglib/asf/asffile.cpp b/taglib/asf/asffile.cpp
new file mode 100644 (file)
index 0000000..e403934
--- /dev/null
@@ -0,0 +1,562 @@
+/**************************************************************************
+    copyright            : (C) 2005-2007 by Lukáš Lalinský
+    email                : lalinsky@gmail.com
+ **************************************************************************/
+
+/***************************************************************************
+ *   This library is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License version   *
+ *   2.1 as published by the Free Software Foundation.                     *
+ *                                                                         *
+ *   This library is distributed in the hope that it will be useful, but   *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   Lesser General Public License for more details.                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU Lesser General Public      *
+ *   License along with this library; if not, write to the Free Software   *
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
+ *   USA                                                                   *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifdef WITH_ASF
+
+#include <tdebug.h>
+#include <tbytevectorlist.h>
+#include <tstring.h>
+#include "asffile.h"
+#include "asftag.h"
+#include "asfproperties.h"
+
+using namespace TagLib;
+
+class ASF::File::FilePrivate
+{
+public:
+  FilePrivate():
+    size(0),
+    tag(0),
+    properties(0),
+    contentDescriptionObject(0),
+    extendedContentDescriptionObject(0),
+    headerExtensionObject(0),
+    metadataObject(0),
+    metadataLibraryObject(0) {}
+  unsigned long long size;
+  ASF::Tag *tag;
+  ASF::Properties *properties;
+  List<ASF::File::BaseObject *> objects;
+  ASF::File::ContentDescriptionObject *contentDescriptionObject;
+  ASF::File::ExtendedContentDescriptionObject *extendedContentDescriptionObject;
+  ASF::File::HeaderExtensionObject *headerExtensionObject;
+  ASF::File::MetadataObject *metadataObject;
+  ASF::File::MetadataLibraryObject *metadataLibraryObject;
+};
+
+static ByteVector headerGuid("\x30\x26\xB2\x75\x8E\x66\xCF\x11\xA6\xD9\x00\xAA\x00\x62\xCE\x6C", 16);
+static ByteVector filePropertiesGuid("\xA1\xDC\xAB\x8C\x47\xA9\xCF\x11\x8E\xE4\x00\xC0\x0C\x20\x53\x65", 16);
+static ByteVector streamPropertiesGuid("\x91\x07\xDC\xB7\xB7\xA9\xCF\x11\x8E\xE6\x00\xC0\x0C\x20\x53\x65", 16);
+static ByteVector contentDescriptionGuid("\x33\x26\xB2\x75\x8E\x66\xCF\x11\xA6\xD9\x00\xAA\x00\x62\xCE\x6C", 16);
+static ByteVector extendedContentDescriptionGuid("\x40\xA4\xD0\xD2\x07\xE3\xD2\x11\x97\xF0\x00\xA0\xC9\x5E\xA8\x50", 16);
+static ByteVector headerExtensionGuid("\xb5\x03\xbf_.\xa9\xcf\x11\x8e\xe3\x00\xc0\x0c Se", 16);
+static ByteVector metadataGuid("\xEA\xCB\xF8\xC5\xAF[wH\204g\xAA\214D\xFAL\xCA", 16);
+static ByteVector metadataLibraryGuid("\224\034#D\230\224\321I\241A\x1d\x13NEpT", 16);
+
+class ASF::File::BaseObject
+{
+public:
+  ByteVector data;
+  virtual ~BaseObject() {}
+  virtual ByteVector guid() = 0;
+  virtual void parse(ASF::File *file, unsigned int size);
+  virtual ByteVector render(ASF::File *file);
+};
+
+class ASF::File::UnknownObject : public ASF::File::BaseObject
+{
+  ByteVector myGuid;
+public:
+  UnknownObject(const ByteVector &guid);
+  ByteVector guid();
+};
+
+class ASF::File::FilePropertiesObject : public ASF::File::BaseObject
+{
+public:
+  ByteVector guid();
+  void parse(ASF::File *file, uint size);
+};
+
+class ASF::File::StreamPropertiesObject : public ASF::File::BaseObject
+{
+public:
+  ByteVector guid();
+  void parse(ASF::File *file, uint size);
+};
+
+class ASF::File::ContentDescriptionObject : public ASF::File::BaseObject
+{
+public:
+  ByteVector guid();
+  void parse(ASF::File *file, uint size);
+  ByteVector render(ASF::File *file);
+};
+
+class ASF::File::ExtendedContentDescriptionObject : public ASF::File::BaseObject
+{
+public:
+  ByteVectorList attributeData;
+  ByteVector guid();
+  void parse(ASF::File *file, uint size);
+  ByteVector render(ASF::File *file);
+};
+
+class ASF::File::MetadataObject : public ASF::File::BaseObject
+{
+public:
+  ByteVectorList attributeData;
+  ByteVector guid();
+  void parse(ASF::File *file, uint size);
+  ByteVector render(ASF::File *file);
+};
+
+class ASF::File::MetadataLibraryObject : public ASF::File::BaseObject
+{
+public:
+  ByteVectorList attributeData;
+  ByteVector guid();
+  void parse(ASF::File *file, uint size);
+  ByteVector render(ASF::File *file);
+};
+
+class ASF::File::HeaderExtensionObject : public ASF::File::BaseObject
+{
+public:
+  List<ASF::File::BaseObject *> objects;
+  ByteVector guid();
+  void parse(ASF::File *file, uint size);
+  ByteVector render(ASF::File *file);
+};
+
+void
+ASF::File::BaseObject::parse(ASF::File *file, unsigned int size)
+{
+  data = file->readBlock(size - 24);
+}
+
+ByteVector
+ASF::File::BaseObject::render(ASF::File * /*file*/)
+{
+  return guid() + ByteVector::fromLongLong(data.size() + 24, false) + data;
+}
+
+ASF::File::UnknownObject::UnknownObject(const ByteVector &guid) : myGuid(guid)
+{
+}
+
+ByteVector
+ASF::File::UnknownObject::guid()
+{
+  return myGuid;
+}
+
+ByteVector
+ASF::File::FilePropertiesObject::guid()
+{
+  return filePropertiesGuid;
+}
+
+void
+ASF::File::FilePropertiesObject::parse(ASF::File *file, uint size)
+{
+  BaseObject::parse(file, size);
+  file->d->properties->setLength((int)(data.mid(40, 8).toLongLong(false) / 10000000L - data.mid(56, 8).toLongLong(false) / 1000L));
+}
+
+ByteVector
+ASF::File::StreamPropertiesObject::guid()
+{
+  return streamPropertiesGuid;
+}
+
+void
+ASF::File::StreamPropertiesObject::parse(ASF::File *file, uint size)
+{
+  BaseObject::parse(file, size);
+  file->d->properties->setChannels(data.mid(56, 2).toShort(false));
+  file->d->properties->setSampleRate(data.mid(58, 4).toUInt(false));
+  file->d->properties->setBitrate(data.mid(62, 4).toUInt(false) * 8 / 1000);
+}
+
+ByteVector
+ASF::File::ContentDescriptionObject::guid()
+{
+  return contentDescriptionGuid;
+}
+
+void
+ASF::File::ContentDescriptionObject::parse(ASF::File *file, uint /*size*/)
+{
+  file->d->contentDescriptionObject = this;
+  int titleLength = file->readWORD();
+  int artistLength = file->readWORD();
+  int copyrightLength = file->readWORD();
+  int commentLength = file->readWORD();
+  int ratingLength = file->readWORD();
+  file->d->tag->setTitle(file->readString(titleLength));
+  file->d->tag->setArtist(file->readString(artistLength));
+  file->d->tag->setCopyright(file->readString(copyrightLength));
+  file->d->tag->setComment(file->readString(commentLength));
+  file->d->tag->setRating(file->readString(ratingLength));
+}
+
+ByteVector
+ASF::File::ContentDescriptionObject::render(ASF::File *file)
+{
+  ByteVector v1 = file->renderString(file->d->tag->title());
+  ByteVector v2 = file->renderString(file->d->tag->artist());
+  ByteVector v3 = file->renderString(file->d->tag->copyright());
+  ByteVector v4 = file->renderString(file->d->tag->comment());
+  ByteVector v5 = file->renderString(file->d->tag->rating());
+  data.clear();
+  data.append(ByteVector::fromShort(v1.size(), false));
+  data.append(ByteVector::fromShort(v2.size(), false));
+  data.append(ByteVector::fromShort(v3.size(), false));
+  data.append(ByteVector::fromShort(v4.size(), false));
+  data.append(ByteVector::fromShort(v5.size(), false));
+  data.append(v1);
+  data.append(v2);
+  data.append(v3);
+  data.append(v4);
+  data.append(v5);
+  return BaseObject::render(file);
+}
+
+ByteVector
+ASF::File::ExtendedContentDescriptionObject::guid()
+{
+  return extendedContentDescriptionGuid;
+}
+
+void
+ASF::File::ExtendedContentDescriptionObject::parse(ASF::File *file, uint /*size*/)
+{
+  file->d->extendedContentDescriptionObject = this;
+  int count = file->readWORD();
+  while(count--) {
+    ASF::Attribute attribute;
+    String name = attribute.parse(*file);
+    file->d->tag->addAttribute(name, attribute);
+  }
+}
+
+ByteVector
+ASF::File::ExtendedContentDescriptionObject::render(ASF::File *file)
+{
+  data.clear();
+  data.append(ByteVector::fromShort(attributeData.size(), false));
+  data.append(attributeData.toByteVector(ByteVector::null));
+  return BaseObject::render(file);
+}
+
+ByteVector
+ASF::File::MetadataObject::guid()
+{
+  return metadataGuid;
+}
+
+void
+ASF::File::MetadataObject::parse(ASF::File *file, uint /*size*/)
+{
+  file->d->metadataObject = this;
+  int count = file->readWORD();
+  while(count--) {
+    ASF::Attribute attribute;
+    String name = attribute.parse(*file, 1);
+    file->d->tag->addAttribute(name, attribute);
+  }
+}
+
+ByteVector
+ASF::File::MetadataObject::render(ASF::File *file)
+{
+  data.clear();
+  data.append(ByteVector::fromShort(attributeData.size(), false));
+  data.append(attributeData.toByteVector(ByteVector::null));
+  return BaseObject::render(file);
+}
+
+ByteVector
+ASF::File::MetadataLibraryObject::guid()
+{
+  return metadataLibraryGuid;
+}
+
+void
+ASF::File::MetadataLibraryObject::parse(ASF::File *file, uint /*size*/)
+{
+  file->d->metadataLibraryObject = this;
+  int count = file->readWORD();
+  while(count--) {
+    ASF::Attribute attribute;
+    String name = attribute.parse(*file, 2);
+    file->d->tag->addAttribute(name, attribute);
+  }
+}
+
+ByteVector
+ASF::File::MetadataLibraryObject::render(ASF::File *file)
+{
+  data.clear();
+  data.append(ByteVector::fromShort(attributeData.size(), false));
+  data.append(attributeData.toByteVector(ByteVector::null));
+  return BaseObject::render(file);
+}
+
+ByteVector
+ASF::File::HeaderExtensionObject::guid()
+{
+  return headerExtensionGuid;
+}
+
+void
+ASF::File::HeaderExtensionObject::parse(ASF::File *file, uint /*size*/)
+{
+  file->d->headerExtensionObject = this;
+  file->seek(18, File::Current);
+  long long dataSize = file->readDWORD();
+  long long dataPos = 0;
+  while(dataPos < dataSize) {
+    ByteVector guid = file->readBlock(16);
+    long long size = file->readQWORD();
+    BaseObject *obj;
+    if(guid == metadataGuid) {
+      obj = new MetadataObject();
+    }
+    else if(guid == metadataLibraryGuid) {
+      obj = new MetadataLibraryObject();
+    }
+    else {
+      obj = new UnknownObject(guid);
+    }
+    obj->parse(file, size);
+    objects.append(obj);
+    dataPos += size;
+  }
+}
+
+ByteVector
+ASF::File::HeaderExtensionObject::render(ASF::File *file)
+{
+  data.clear();
+  for(unsigned int i = 0; i < objects.size(); i++) {
+    data.append(objects[i]->render(file));
+  }
+  data = ByteVector("\x11\xD2\xD3\xAB\xBA\xA9\xcf\x11\x8E\xE6\x00\xC0\x0C\x20\x53\x65\x06\x00", 18) + ByteVector::fromUInt(data.size(), false) + data;
+  return BaseObject::render(file);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// public members
+////////////////////////////////////////////////////////////////////////////////
+
+ASF::File::File(FileName file, bool readProperties, Properties::ReadStyle propertiesStyle) 
+  : TagLib::File(file)
+{
+  d = new FilePrivate;
+  read(readProperties, propertiesStyle);
+}
+
+ASF::File::~File()
+{
+  for(unsigned int i = 0; i < d->objects.size(); i++) {
+    delete d->objects[i];
+  }
+  if(d->tag) {
+    delete d->tag;
+  }
+  if(d->properties) {
+    delete d->properties;
+  }
+  delete d;
+}
+
+ASF::Tag *ASF::File::tag() const
+{
+  return d->tag;
+}
+
+ASF::Properties *ASF::File::audioProperties() const
+{
+  return d->properties;
+}
+
+void ASF::File::read(bool /*readProperties*/, Properties::ReadStyle /*propertiesStyle*/)
+{
+  if(!isValid())
+    return;
+
+  ByteVector guid = readBlock(16);
+  if(guid != headerGuid) {
+    debug("ASF: Not an ASF file.");
+    return;
+  }
+
+  d->tag = new ASF::Tag();
+  d->properties = new ASF::Properties();
+
+  d->size = readQWORD();
+  int numObjects = readDWORD();
+  seek(2, Current);
+
+  for(int i = 0; i < numObjects; i++) {
+    ByteVector guid = readBlock(16);
+    long size = (long)readQWORD();
+    BaseObject *obj;
+    if(guid == filePropertiesGuid) {
+      obj = new FilePropertiesObject();
+    }
+    else if(guid == streamPropertiesGuid) {
+      obj = new StreamPropertiesObject();
+    }
+    else if(guid == contentDescriptionGuid) {
+      obj = new ContentDescriptionObject();
+    }
+    else if(guid == extendedContentDescriptionGuid) {
+      obj = new ExtendedContentDescriptionObject();
+    }
+    else if(guid == headerExtensionGuid) {
+      obj = new HeaderExtensionObject();
+    }
+    else {
+      obj = new UnknownObject(guid);
+    }
+    obj->parse(this, size);
+    d->objects.append(obj);
+  }
+}
+
+bool ASF::File::save()
+{
+  if(readOnly()) {
+    debug("ASF: File is read-only.");
+    return false;
+  }
+
+  if(!d->contentDescriptionObject) {
+    d->contentDescriptionObject = new ContentDescriptionObject();
+    d->objects.append(d->contentDescriptionObject);
+  }
+  if(!d->extendedContentDescriptionObject) {
+    d->extendedContentDescriptionObject = new ExtendedContentDescriptionObject();
+    d->objects.append(d->extendedContentDescriptionObject);
+  }
+  if(!d->headerExtensionObject) {
+    d->headerExtensionObject = new HeaderExtensionObject();
+    d->objects.append(d->headerExtensionObject);
+  }
+  if(!d->metadataObject) {
+    d->metadataObject = new MetadataObject();
+    d->headerExtensionObject->objects.append(d->metadataObject);
+  }
+  if(!d->metadataLibraryObject) {
+    d->metadataLibraryObject = new MetadataLibraryObject();
+    d->headerExtensionObject->objects.append(d->metadataLibraryObject);
+  }
+
+  ASF::AttributeListMap::ConstIterator it = d->tag->attributeListMap().begin();
+  for(; it != d->tag->attributeListMap().end(); it++) {
+    const String &name = it->first;
+    const AttributeList &attributes = it->second;
+    bool inExtendedContentDescriptionObject = false;
+    bool inMetadataObject = false;
+    for(unsigned int j = 0; j < attributes.size(); j++) {
+      const Attribute &attribute = attributes[j];
+      if(!inExtendedContentDescriptionObject && attribute.language() == 0 && attribute.stream() == 0) {
+        d->extendedContentDescriptionObject->attributeData.append(attribute.render(name));
+        inExtendedContentDescriptionObject = true;
+      }
+      else if(!inMetadataObject && attribute.language() == 0 && attribute.stream() != 0) {
+        d->metadataObject->attributeData.append(attribute.render(name, 1));
+        inMetadataObject = true;
+      }
+      else {
+        d->metadataLibraryObject->attributeData.append(attribute.render(name, 2));
+      }
+    }
+  }
+
+  ByteVector data;
+  for(unsigned int i = 0; i < d->objects.size(); i++) {
+    data.append(d->objects[i]->render(this));
+  }
+  data = headerGuid + ByteVector::fromLongLong(data.size() + 30, false) + ByteVector::fromUInt(d->objects.size(), false) + ByteVector("\x01\x02", 2) + data;
+  insert(data, 0, d->size);
+
+  return true;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// protected members
+////////////////////////////////////////////////////////////////////////////////
+
+int ASF::File::readBYTE()
+{
+  ByteVector v = readBlock(1);
+  return v[0];
+}
+
+int ASF::File::readWORD()
+{
+  ByteVector v = readBlock(2);
+  return v.toShort(false);
+}
+
+unsigned int ASF::File::readDWORD()
+{
+  ByteVector v = readBlock(4);
+  return v.toUInt(false);
+}
+
+long long ASF::File::readQWORD()
+{
+  ByteVector v = readBlock(8);
+  return v.toLongLong(false);
+}
+
+String
+ASF::File::readString(int length)
+{
+  ByteVector data = readBlock(length);
+  unsigned int size = data.size();
+  while (size >= 2) {
+    if(data[size - 1] != '\0' || data[size - 2] != '\0') {
+      break;
+    }
+    size -= 2;
+  }
+  if(size != data.size()) {
+    data.resize(size);
+  }
+  return String(data, String::UTF16LE);
+}
+
+ByteVector
+ASF::File::renderString(const String &str, bool includeLength)
+{
+  ByteVector data = str.data(String::UTF16LE) + ByteVector::fromShort(0, false);
+  if(includeLength) {
+    data = ByteVector::fromShort(data.size(), false) + data;
+  }
+  return data;
+}
+
+#endif
diff --git a/taglib/asf/asffile.h b/taglib/asf/asffile.h
new file mode 100644 (file)
index 0000000..9d2913d
--- /dev/null
@@ -0,0 +1,119 @@
+/**************************************************************************
+    copyright            : (C) 2005-2007 by Lukáš Lalinský
+    email                : lalinsky@gmail.com
+ **************************************************************************/
+
+/***************************************************************************
+ *   This library is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License version   *
+ *   2.1 as published by the Free Software Foundation.                     *
+ *                                                                         *
+ *   This library is distributed in the hope that it will be useful, but   *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   Lesser General Public License for more details.                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU Lesser General Public      *
+ *   License along with this library; if not, write to the Free Software   *
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
+ *   USA                                                                   *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+#ifndef TAGLIB_ASFFILE_H
+#define TAGLIB_ASFFILE_H
+
+#include <tag.h>
+#include <tfile.h>
+#include "taglib_export.h"
+#include "asfproperties.h"
+#include "asftag.h"
+
+namespace TagLib {
+
+  //! An implementation of ASF (WMA) metadata
+  namespace ASF {
+
+    /*!
+     * This implements and provides an interface for ASF 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 ASF files.
+     */
+    class TAGLIB_EXPORT File : public TagLib::File
+    {
+    public:
+
+      /*!
+       * Contructs an ASF 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 In the current implementation, both \a readProperties and
+       * \a propertiesStyle are ignored.
+       */
+      File(FileName file, bool readProperties = true, Properties::ReadStyle propertiesStyle = Properties::Average);
+
+      /*!
+       * Destroys this instance of the File.
+       */
+      virtual ~File();
+
+      /*!
+       * Returns a pointer to the ASF tag of the file.
+       *
+       * ASF::Tag implements the tag interface, so this serves as the
+       * reimplementation of TagLib::File::tag().
+       *
+       * \note The Tag <b>is still</b> owned by the ASF::File and should not be
+       * deleted by the user.  It will be deleted when the file (object) is
+       * destroyed.
+       */
+      virtual Tag *tag() const;
+
+      /*!
+       * Returns the ASF audio properties for this file.
+       */
+      virtual Properties *audioProperties() const;
+
+      /*!
+       * Save the file.
+       *
+       * This returns true if the save was successful.
+       */
+      virtual bool save();
+
+    private:
+
+      int readBYTE();
+      int readWORD();
+      unsigned int readDWORD();
+      long long readQWORD();
+      static ByteVector renderString(const String &str, bool includeLength = false);
+      String readString(int len);
+      void read(bool readProperties, Properties::ReadStyle propertiesStyle);
+
+      friend class Attribute;
+
+      class BaseObject;
+      class UnknownObject;
+      class FilePropertiesObject;
+      class StreamPropertiesObject;
+      class ContentDescriptionObject;
+      class ExtendedContentDescriptionObject;
+      class HeaderExtensionObject;
+      class MetadataObject;
+      class MetadataLibraryObject;
+
+      class FilePrivate;
+      FilePrivate *d;
+    };
+
+  }
+
+}
+
+#endif
diff --git a/taglib/asf/asfproperties.cpp b/taglib/asf/asfproperties.cpp
new file mode 100644 (file)
index 0000000..68e2d4c
--- /dev/null
@@ -0,0 +1,107 @@
+/**************************************************************************
+    copyright            : (C) 2005-2007 by Lukáš Lalinský
+    email                : lalinsky@gmail.com
+ **************************************************************************/
+
+/***************************************************************************
+ *   This library is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License version   *
+ *   2.1 as published by the Free Software Foundation.                     *
+ *                                                                         *
+ *   This library is distributed in the hope that it will be useful, but   *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   Lesser General Public License for more details.                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU Lesser General Public      *
+ *   License along with this library; if not, write to the Free Software   *
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
+ *   USA                                                                   *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifdef WITH_ASF
+
+#include <tdebug.h>
+#include <tstring.h>
+#include "asfproperties.h"
+
+using namespace TagLib;
+
+class ASF::Properties::PropertiesPrivate
+{
+public:
+  PropertiesPrivate(): length(0), bitrate(0), sampleRate(0), channels(0) {}
+  int length;
+  int bitrate;
+  int sampleRate;
+  int channels;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// public members
+////////////////////////////////////////////////////////////////////////////////
+
+ASF::Properties::Properties() : AudioProperties(AudioProperties::Average)
+{
+  d = new PropertiesPrivate;
+}
+
+ASF::Properties::~Properties()
+{
+  if(d)
+    delete d;  
+}
+
+int ASF::Properties::length() const
+{
+  return d->length;
+}
+
+int ASF::Properties::bitrate() const
+{
+  return d->bitrate;
+}
+
+int ASF::Properties::sampleRate() const
+{
+  return d->sampleRate;
+}
+
+int ASF::Properties::channels() const
+{
+  return d->channels;
+} 
+
+////////////////////////////////////////////////////////////////////////////////
+// private members
+////////////////////////////////////////////////////////////////////////////////
+
+void ASF::Properties::setLength(int length)
+{
+  d->length = length;
+}
+
+void ASF::Properties::setBitrate(int length)
+{
+  d->bitrate = length;
+}
+
+void ASF::Properties::setSampleRate(int length)
+{
+  d->sampleRate = length;
+}
+
+void ASF::Properties::setChannels(int length)
+{
+  d->channels = length;
+}
+
+#endif
diff --git a/taglib/asf/asfproperties.h b/taglib/asf/asfproperties.h
new file mode 100644 (file)
index 0000000..938b0c9
--- /dev/null
@@ -0,0 +1,74 @@
+/**************************************************************************
+    copyright            : (C) 2005-2007 by Lukáš Lalinský
+    email                : lalinsky@gmail.com
+ **************************************************************************/
+
+/***************************************************************************
+ *   This library is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License version   *
+ *   2.1 as published by the Free Software Foundation.                     *
+ *                                                                         *
+ *   This library is distributed in the hope that it will be useful, but   *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   Lesser General Public License for more details.                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU Lesser General Public      *
+ *   License along with this library; if not, write to the Free Software   *
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
+ *   USA                                                                   *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+#ifndef TAGLIB_ASFPROPERTIES_H
+#define TAGLIB_ASFPROPERTIES_H
+
+#include <audioproperties.h>
+#include <tstring.h>
+#include "taglib_export.h"
+
+namespace TagLib {
+
+  namespace ASF {
+
+    //! An implementation of ASF audio properties
+    class TAGLIB_EXPORT Properties : public AudioProperties
+    {
+    public:
+
+      /*!
+       * Create an instance of ASF::Properties.
+       */
+      Properties();
+
+      /*!
+       * Destroys this ASF::Properties instance.
+       */
+      virtual ~Properties();
+
+      // Reimplementations.
+      virtual int length() const;
+      virtual int bitrate() const;
+      virtual int sampleRate() const;
+      virtual int channels() const;
+
+#ifndef DO_NOT_DOCUMENT
+      void setLength(int value);
+      void setBitrate(int value);
+      void setSampleRate(int value);
+      void setChannels(int value);
+#endif
+
+    private:
+      class PropertiesPrivate;
+      PropertiesPrivate *d;
+    };
+
+  }
+
+}
+
+#endif 
diff --git a/taglib/asf/asftag.cpp b/taglib/asf/asftag.cpp
new file mode 100644 (file)
index 0000000..6bea247
--- /dev/null
@@ -0,0 +1,214 @@
+/**************************************************************************
+    copyright            : (C) 2005-2007 by Lukáš Lalinský
+    email                : lalinsky@gmail.com
+ **************************************************************************/
+
+/***************************************************************************
+ *   This library is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License version   *
+ *   2.1 as published by the Free Software Foundation.                     *
+ *                                                                         *
+ *   This library is distributed in the hope that it will be useful, but   *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   Lesser General Public License for more details.                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU Lesser General Public      *
+ *   License along with this library; if not, write to the Free Software   *
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
+ *   USA                                                                   *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifdef WITH_ASF
+
+#include "asftag.h"
+
+using namespace TagLib;
+
+class ASF::Tag::TagPrivate
+{
+public:
+  String title;
+  String artist;
+  String copyright;
+  String comment;
+  String rating;
+  AttributeListMap attributeListMap;
+};
+
+ASF::Tag::Tag()
+: TagLib::Tag()
+{
+  d = new TagPrivate;
+}
+
+ASF::Tag::~Tag()
+{
+  if(d)
+    delete d;
+}
+
+String
+ASF::Tag::title() const
+{
+  return d->title;
+}
+
+String
+ASF::Tag::artist() const
+{
+  return d->artist;
+}
+
+String
+ASF::Tag::album() const
+{
+  if(d->attributeListMap.contains("WM/AlbumTitle"))
+    return d->attributeListMap["WM/AlbumTitle"][0].toString();
+  return String::null;
+}
+
+String
+ASF::Tag::copyright() const
+{
+  return d->copyright;
+}
+
+String
+ASF::Tag::comment() const
+{
+  return d->comment;
+}
+
+String
+ASF::Tag::rating() const
+{
+  return d->rating;
+}
+
+unsigned int
+ASF::Tag::year() const
+{
+  if(d->attributeListMap.contains("WM/Year"))
+    return d->attributeListMap["WM/Year"][0].toString().toInt();
+  return 0;
+}
+
+unsigned int
+ASF::Tag::track() const
+{
+  if(d->attributeListMap.contains("WM/TrackNumber"))
+    return d->attributeListMap["WM/TrackNumber"][0].toString().toInt();
+  if(d->attributeListMap.contains("WM/Track"))
+    return d->attributeListMap["WM/Track"][0].toUInt();
+  return 0;
+}
+
+String
+ASF::Tag::genre() const
+{
+  if(d->attributeListMap.contains("WM/Genre"))
+    return d->attributeListMap["WM/Genre"][0].toString();
+  return String::null;
+}
+
+void
+ASF::Tag::setTitle(const String &value)
+{
+  d->title = value;
+}
+
+void
+ASF::Tag::setArtist(const String &value)
+{
+  d->artist = value;
+}
+
+void
+ASF::Tag::setCopyright(const String &value)
+{
+  d->copyright = value;
+}
+
+void
+ASF::Tag::setComment(const String &value)
+{
+  d->comment = value;
+}
+
+void
+ASF::Tag::setRating(const String &value)
+{
+  d->rating = value;
+}
+
+void
+ASF::Tag::setAlbum(const String &value)
+{
+  setAttribute("WM/AlbumTitle", value);
+}
+
+void
+ASF::Tag::setGenre(const String &value)
+{
+  setAttribute("WM/Genre", value);
+}
+
+void
+ASF::Tag::setYear(uint value)
+{
+  setAttribute("WM/Year", String::number(value));
+}
+
+void
+ASF::Tag::setTrack(uint value)
+{
+  setAttribute("WM/TrackNumber", String::number(value));
+}
+
+ASF::AttributeListMap&
+ASF::Tag::attributeListMap()
+{
+  return d->attributeListMap;
+}
+
+void ASF::Tag::removeItem(const String &key)
+{
+  AttributeListMap::Iterator it = d->attributeListMap.find(key);
+  if(it != d->attributeListMap.end())
+    d->attributeListMap.erase(it);
+}
+
+void ASF::Tag::setAttribute(const String &name, const Attribute &attribute)
+{
+  AttributeList value;
+  value.append(attribute);
+  d->attributeListMap.insert(name, value);
+}
+
+void ASF::Tag::addAttribute(const String &name, const Attribute &attribute)
+{
+  if(d->attributeListMap.contains(name)) {
+    d->attributeListMap[name].append(attribute);
+  }
+  else {
+    setAttribute(name, attribute);
+  }
+}
+
+bool ASF::Tag::isEmpty() const {
+  return TagLib::Tag::isEmpty() &&
+         copyright().isEmpty() &&
+         rating().isEmpty() &&
+         d->attributeListMap.isEmpty();
+}
+
+#endif
diff --git a/taglib/asf/asftag.h b/taglib/asf/asftag.h
new file mode 100644 (file)
index 0000000..78c90fc
--- /dev/null
@@ -0,0 +1,186 @@
+/**************************************************************************
+    copyright            : (C) 2005-2007 by Lukáš Lalinský
+    email                : lalinsky@gmail.com
+ **************************************************************************/
+
+/***************************************************************************
+ *   This library is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License version   *
+ *   2.1 as published by the Free Software Foundation.                     *
+ *                                                                         *
+ *   This library is distributed in the hope that it will be useful, but   *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   Lesser General Public License for more details.                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU Lesser General Public      *
+ *   License along with this library; if not, write to the Free Software   *
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
+ *   USA                                                                   *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+#ifndef TAGLIB_ASFTAG_H
+#define TAGLIB_ASFTAG_H
+
+#include <tag.h>
+#include <tlist.h>
+#include <tmap.h>
+#include "taglib_export.h"
+#include "asfattribute.h"
+
+namespace TagLib {
+
+  namespace ASF {
+
+    typedef List<Attribute> AttributeList;
+    typedef Map<String, AttributeList> AttributeListMap;
+
+    class TAGLIB_EXPORT Tag : public TagLib::Tag {
+
+      friend class File;
+
+    public:
+
+      Tag();
+
+      virtual ~Tag();
+
+      /*!
+       * Returns the track name.
+       */
+      virtual String title() const;
+
+      /*!
+       * Returns the artist name.
+       */
+      virtual String artist() const;
+
+      /*!
+       * Returns the album name; if no album name is present in the tag
+       * String::null will be returned.
+       */
+      virtual String album() const;
+
+      /*!
+       * Returns the track comment.
+       */
+      virtual String comment() const;
+
+      /*!
+       * Returns the genre name; if no genre is present in the tag String::null
+       * will be returned.
+       */
+      virtual String genre() const;
+
+      /*!
+       * Returns the rating.
+       */
+      virtual String rating() const;
+
+      /*!
+       * Returns the genre name; if no genre is present in the tag String::null
+       * will be returned.
+       */
+      virtual String copyright() const;
+
+      /*!
+       * Returns the year; if there is no year set, this will return 0.
+       */
+      virtual uint year() const;
+
+      /*!
+       * Returns the track number; if there is no track number set, this will
+       * return 0.
+       */
+      virtual uint track() const;
+
+      /*!
+       * Sets the title to \a s.
+       */
+      virtual void setTitle(const String &s);
+
+      /*!
+       * Sets the artist to \a s.
+       */
+      virtual void setArtist(const String &s);
+
+      /*!
+       * Sets the album to \a s.  If \a s is String::null then this value will be
+       * cleared.
+       */
+      virtual void setAlbum(const String &s);
+
+      /*!
+       * Sets the comment to \a s.
+       */
+      virtual void setComment(const String &s);
+
+      /*!
+       * Sets the rating to \a s. 
+       */
+      virtual void setRating(const String &s);
+
+      /*!
+       * Sets the copyright to \a s. 
+       */
+      virtual void setCopyright(const String &s);
+
+      /*!
+       * Sets the genre to \a s. 
+       */
+      virtual void setGenre(const String &s);
+
+      /*!
+       * Sets the year to \a i.  If \a s is 0 then this value will be cleared.
+       */
+      virtual void setYear(uint i);
+
+      /*!
+       * Sets the track to \a i.  If \a s is 0 then this value will be cleared.
+       */
+      virtual void setTrack(uint i);
+
+      /*!
+       * 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;
+
+      /*!
+       * Returns a reference to the item list map.  This is an AttributeListMap of
+       * all of the items in the tag.
+       *
+       * This is the most powerfull structure for accessing the items of the tag.
+       */
+      AttributeListMap &attributeListMap();
+
+      /*!
+       * Removes the \a key attribute from the tag
+       */
+      void removeItem(const String &name);
+
+      /*!
+       * Sets the \a key attribute to the value of \a attribute. If an attribute
+       * with the \a key is already present, it will be replaced.
+       */
+      void setAttribute(const String &name, const Attribute &attribute);
+
+      /*!
+       * Sets the \a key attribute to the value of \a attribute. If an attribute
+       * with the \a key is already present, it will be added to the list.
+       */
+      void addAttribute(const String &name, const Attribute &attribute);
+
+    private:
+
+      class TagPrivate;
+      TagPrivate *d;
+    };
+  }
+}
+#endif
index ce6f649b299feb4bd64b573eae07d75da3fa863d..b326bd22950f3832dfc569df1c2ba531e68cb4c7 100644 (file)
  *   http://www.mozilla.org/MPL/                                           *
  ***************************************************************************/
 
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
 #include <tfile.h>
 #include <tstring.h>
 
 #include "fileref.h"
+#include "asffile.h"
 #include "mpegfile.h"
 #include "vorbisfile.h"
 #include "flacfile.h"
 #include "oggflacfile.h"
 #include "mpcfile.h"
+#include "mp4file.h"
 #include "wavpackfile.h"
 #include "speexfile.h"
 #include "trueaudiofile.h"
@@ -123,6 +129,15 @@ StringList FileRef::defaultFileExtensions()
   l.append("wv");
   l.append("spx");
   l.append("tta");
+#ifdef WITH_MP4
+  l.append("m4a");
+  l.append("m4b");
+  l.append("m4p");
+  l.append("3g2");
+#endif
+#ifdef WITH_ASF
+  l.append("wma");
+#endif
   l.append("aif");
   l.append("aiff");
   l.append("wav");
@@ -202,6 +217,17 @@ File *FileRef::create(FileName fileName, bool readAudioProperties,
       return new Ogg::Speex::File(fileName, readAudioProperties, audioPropertiesStyle);
     if(s.substr(s.size() - 4, 4).upper() == ".TTA")
       return new TrueAudio::File(fileName, readAudioProperties, audioPropertiesStyle);
+#ifdef WITH_MP4
+    if(s.substr(s.size() - 4, 4).upper() == ".M4A" ||
+       s.substr(s.size() - 4, 4).upper() == ".M4B" ||
+       s.substr(s.size() - 4, 4).upper() == ".M4P" ||
+       s.substr(s.size() - 4, 4).upper() == ".3G2")
+      return new MP4::File(fileName, readAudioProperties, audioPropertiesStyle);
+#endif
+#ifdef WITH_ASF
+    if(s.substr(s.size() - 4, 4).upper() == ".WMA")
+      return new ASF::File(fileName, readAudioProperties, audioPropertiesStyle);
+#endif
     if(s.substr(s.size() - 4, 4).upper() == ".AIF")
       return new RIFF::AIFF::File(fileName, readAudioProperties, audioPropertiesStyle);
     if(s.substr(s.size() - 4, 4).upper() == ".WAV")
diff --git a/taglib/mp4/CMakeLists.txt b/taglib/mp4/CMakeLists.txt
new file mode 100644 (file)
index 0000000..803a9d5
--- /dev/null
@@ -0,0 +1 @@
+INSTALL( FILES  mp4file.h mp4atom.h mp4tag.h mp4item.h mp4properties.h DESTINATION ${INCLUDE_INSTALL_DIR}/taglib)
diff --git a/taglib/mp4/Makefile.am b/taglib/mp4/Makefile.am
new file mode 100644 (file)
index 0000000..4ff5a30
--- /dev/null
@@ -0,0 +1,11 @@
+INCLUDES = \
+       -I$(top_srcdir)/taglib \
+       -I$(top_srcdir)/taglib/toolkit \
+       $(all_includes)
+
+noinst_LTLIBRARIES = libmp4.la
+
+libmp4_la_SOURCES = mp4atom.cpp mp4file.cpp mp4item.cpp mp4properties.cpp mp4tag.cpp
+
+taglib_include_HEADERS = mp4atom.h mp4file.h mp4item.h mp4properties.h mp4tag.h
+taglib_includedir = $(includedir)/taglib
diff --git a/taglib/mp4/mp4atom.cpp b/taglib/mp4/mp4atom.cpp
new file mode 100644 (file)
index 0000000..e1a3682
--- /dev/null
@@ -0,0 +1,175 @@
+/**************************************************************************
+    copyright            : (C) 2007 by Lukáš Lalinský
+    email                : lalinsky@gmail.com
+ **************************************************************************/
+
+/***************************************************************************
+ *   This library is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License version   *
+ *   2.1 as published by the Free Software Foundation.                     *
+ *                                                                         *
+ *   This library is distributed in the hope that it will be useful, but   *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   Lesser General Public License for more details.                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU Lesser General Public      *
+ *   License along with this library; if not, write to the Free Software   *
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
+ *   USA                                                                   *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifdef WITH_MP4
+
+#include <tdebug.h>
+#include <tstring.h>
+#include "mp4atom.h"
+
+using namespace TagLib;
+
+const char *MP4::Atom::containers[10] = {
+    "moov", "udta", "mdia", "meta", "ilst",
+    "stbl", "minf", "moof", "traf", "trak",
+};
+
+MP4::Atom::Atom(File *file)
+{
+  offset = file->tell();
+  ByteVector header = file->readBlock(8);
+  length = header.mid(0, 4).toUInt();
+
+  if (length == 1) {
+    debug("MP4: 64-bit atoms are not supported");
+    length = 0;
+    file->seek(0, File::End);
+    return;
+  }
+  if (length < 8) {
+    debug("MP4: Invalid atom size");
+    length = 0;
+    file->seek(0, File::End);
+    return;
+  }
+
+  name = header.mid(4, 4);
+
+  for(int i = 0; i < numContainers; i++) {
+    if(name == containers[i]) {
+      if(name == "meta") {
+        file->seek(4, File::Current);
+      }
+      while(file->tell() < offset + length) {
+        children.append(new MP4::Atom(file));
+      }
+      return;
+    }
+  }
+
+  file->seek(offset + length);
+}
+
+MP4::Atom::~Atom()
+{
+  for(unsigned int i = 0; i < children.size(); i++) {
+    delete children[i];
+  }
+  children.clear();
+}
+
+MP4::Atom *
+MP4::Atom::find(const char *name1, const char *name2, const char *name3, const char *name4)
+{
+  if(name1 == 0) {
+    return this;
+  }
+  for(unsigned int i = 0; i < children.size(); i++) {
+    if(children[i]->name == name1) {
+      return children[i]->find(name2, name3, name4);
+    }
+  }
+  return 0;
+}
+
+MP4::AtomList
+MP4::Atom::findall(const char *name, bool recursive)
+{
+  MP4::AtomList result;
+  for(unsigned int i = 0; i < children.size(); i++) {
+    if(children[i]->name == name) {
+      result.append(children[i]);
+    }
+    if(recursive) {
+      result.append(children[i]->findall(name, recursive));
+    }
+  }
+  return result;
+}
+
+bool
+MP4::Atom::path(MP4::AtomList &path, const char *name1, const char *name2, const char *name3)
+{
+  path.append(this);
+  if(name1 == 0) {
+    return true;
+  }
+  for(unsigned int i = 0; i < children.size(); i++) {
+    if(children[i]->name == name1) {
+      return children[i]->path(path, name2, name3);
+    }
+  }
+  return false;
+}
+
+MP4::Atoms::Atoms(File *file)
+{
+  file->seek(0, File::End);
+  long end = file->tell();
+  file->seek(0);
+  while(file->tell() + 8 <= end) {
+    atoms.append(new MP4::Atom(file));
+  }
+}
+
+MP4::Atoms::~Atoms()
+{
+  for(unsigned int i = 0; i < atoms.size(); i++) {
+    delete atoms[i];
+  }
+  atoms.clear();
+}
+
+MP4::Atom *
+MP4::Atoms::find(const char *name1, const char *name2, const char *name3, const char *name4)
+{
+  for(unsigned int i = 0; i < atoms.size(); i++) {
+    if(atoms[i]->name == name1) {
+      return atoms[i]->find(name2, name3, name4);
+    }
+  }
+  return 0;
+}
+
+MP4::AtomList
+MP4::Atoms::path(const char *name1, const char *name2, const char *name3, const char *name4)
+{
+  MP4::AtomList path;
+  for(unsigned int i = 0; i < atoms.size(); i++) {
+    if(atoms[i]->name == name1) {
+      if(!atoms[i]->path(path, name2, name3, name4)) {
+        path.clear();
+      }
+      return path;
+    }
+  }
+  return path;
+}
+
+#endif
diff --git a/taglib/mp4/mp4atom.h b/taglib/mp4/mp4atom.h
new file mode 100644 (file)
index 0000000..702566b
--- /dev/null
@@ -0,0 +1,77 @@
+/**************************************************************************
+    copyright            : (C) 2007 by Lukáš Lalinský
+    email                : lalinsky@gmail.com
+ **************************************************************************/
+
+/***************************************************************************
+ *   This library is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License version   *
+ *   2.1 as published by the Free Software Foundation.                     *
+ *                                                                         *
+ *   This library is distributed in the hope that it will be useful, but   *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   Lesser General Public License for more details.                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU Lesser General Public      *
+ *   License along with this library; if not, write to the Free Software   *
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
+ *   USA                                                                   *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+// This file is not part of the public API!
+
+#ifndef DO_NOT_DOCUMENT
+
+#ifndef TAGLIB_MP4ATOM_H
+#define TAGLIB_MP4ATOM_H
+
+#include <tfile.h>
+#include <tlist.h>
+
+namespace TagLib {
+
+  namespace MP4 {
+
+    class Atom;
+    typedef TagLib::List<Atom *> AtomList;
+
+    class Atom
+    {
+    public:
+        Atom(File *file);
+        ~Atom();
+        Atom *find(const char *name1, const char *name2 = 0, const char *name3 = 0, const char *name4 = 0);
+        bool path(AtomList &path, const char *name1, const char *name2 = 0, const char *name3 = 0);
+        AtomList findall(const char *name, bool recursive = false);
+        long offset;
+        long length;
+        TagLib::ByteVector name;
+        AtomList children;
+    private:
+        static const int numContainers = 10;
+        static const char *containers[10];
+    };
+
+    //! Root-level atoms
+    class Atoms
+    {
+    public:
+        Atoms(File *file);
+        ~Atoms();
+        Atom *find(const char *name1, const char *name2 = 0, const char *name3 = 0, const char *name4 = 0);
+        AtomList path(const char *name1, const char *name2 = 0, const char *name3 = 0, const char *name4 = 0);
+        AtomList atoms;
+    };
+
+  }
+
+}
+
+#endif
+
+#endif
diff --git a/taglib/mp4/mp4file.cpp b/taglib/mp4/mp4file.cpp
new file mode 100644 (file)
index 0000000..bcb88af
--- /dev/null
@@ -0,0 +1,111 @@
+/**************************************************************************
+    copyright            : (C) 2007 by Lukáš Lalinský
+    email                : lalinsky@gmail.com
+ **************************************************************************/
+
+/***************************************************************************
+ *   This library is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License version   *
+ *   2.1 as published by the Free Software Foundation.                     *
+ *                                                                         *
+ *   This library is distributed in the hope that it will be useful, but   *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   Lesser General Public License for more details.                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU Lesser General Public      *
+ *   License along with this library; if not, write to the Free Software   *
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
+ *   USA                                                                   *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifdef WITH_MP4
+
+#include <tdebug.h>
+#include <tstring.h>
+#include "mp4atom.h"
+#include "mp4tag.h"
+#include "mp4file.h"
+
+using namespace TagLib;
+
+class MP4::File::FilePrivate
+{
+public:
+  FilePrivate() : tag(0), atoms(0)
+  {
+  }
+
+  ~FilePrivate()
+  {
+    if(atoms) {
+        delete atoms;
+        atoms = 0;
+    }
+    if(tag) {
+        delete tag;
+        tag = 0;
+    }
+    if(properties) {
+        delete properties;
+        properties = 0;
+    }
+  }
+
+  MP4::Tag *tag;
+  MP4::Atoms *atoms;
+  MP4::Properties *properties;
+};
+
+MP4::File::File(FileName file, bool readProperties, AudioProperties::ReadStyle audioPropertiesStyle)
+    : TagLib::File(file)
+{
+  d = new FilePrivate;
+  read(readProperties, audioPropertiesStyle);
+}
+
+MP4::File::~File()
+{
+  delete d;
+}
+
+MP4::Tag *
+MP4::File::tag() const
+{
+  return d->tag;
+}
+
+MP4::Properties *
+MP4::File::audioProperties() const
+{
+  return d->properties;
+}
+
+void
+MP4::File::read(bool readProperties, Properties::ReadStyle audioPropertiesStyle)
+{
+  if(!isValid())
+    return;
+
+  d->atoms = new Atoms(this);
+  d->tag = new Tag(this, d->atoms);
+  if(readProperties) {
+    d->properties = new Properties(this, d->atoms, audioPropertiesStyle);
+  }
+}
+
+bool
+MP4::File::save()
+{
+  return d->tag->save();
+}
+
+#endif
diff --git a/taglib/mp4/mp4file.h b/taglib/mp4/mp4file.h
new file mode 100644 (file)
index 0000000..3bd15e3
--- /dev/null
@@ -0,0 +1,102 @@
+/**************************************************************************
+    copyright            : (C) 2007 by Lukáš Lalinský
+    email                : lalinsky@gmail.com
+ **************************************************************************/
+
+/***************************************************************************
+ *   This library is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License version   *
+ *   2.1 as published by the Free Software Foundation.                     *
+ *                                                                         *
+ *   This library is distributed in the hope that it will be useful, but   *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   Lesser General Public License for more details.                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU Lesser General Public      *
+ *   License along with this library; if not, write to the Free Software   *
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
+ *   USA                                                                   *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+#ifndef TAGLIB_MP4FILE_H
+#define TAGLIB_MP4FILE_H
+
+#include <tag.h>
+#include <tfile.h>
+#include "taglib_export.h"
+#include "mp4properties.h"
+#include "mp4tag.h"
+
+namespace TagLib {
+
+  //! An implementation of MP4 (AAC, ALAC, ...) metadata
+  namespace MP4 {
+
+    class Atoms;
+
+    /*!
+     * This implements and provides an interface for MP4 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 MP4 files.
+     */
+    class TAGLIB_EXPORT File : public TagLib::File
+    {
+    public:
+      /*!
+       * Contructs an ASF 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 In the current implementation, both \a readProperties and
+       * \a propertiesStyle are ignored.
+       */
+      File(FileName file, bool readProperties = true, Properties::ReadStyle audioPropertiesStyle = Properties::Average);
+
+      /*!
+       * Destroys this instance of the File.
+       */
+      virtual ~File();
+
+      /*!
+       * Returns a pointer to the MP4 tag of the file.
+       *
+       * MP4::Tag implements the tag interface, so this serves as the
+       * reimplementation of TagLib::File::tag().
+       *
+       * \note The Tag <b>is still</b> owned by the ASF::File and should not be
+       * deleted by the user.  It will be deleted when the file (object) is
+       * destroyed.
+       */
+      Tag *tag() const;
+
+      /*!
+       * Returns the MP4 audio properties for this file.
+       */
+      Properties *audioProperties() const;
+
+      /*!
+       * Save the file.
+       *
+       * This returns true if the save was successful.
+       */
+      bool save();
+
+    private:
+
+      void read(bool readProperties, Properties::ReadStyle audioPropertiesStyle);
+
+      class FilePrivate;
+      FilePrivate *d;
+    };
+
+  }
+
+}
+
+#endif
diff --git a/taglib/mp4/mp4item.cpp b/taglib/mp4/mp4item.cpp
new file mode 100644 (file)
index 0000000..2b5613a
--- /dev/null
@@ -0,0 +1,136 @@
+/**************************************************************************
+    copyright            : (C) 2007 by Lukáš Lalinský
+    email                : lalinsky@gmail.com
+ **************************************************************************/
+
+/***************************************************************************
+ *   This library is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License version   *
+ *   2.1 as published by the Free Software Foundation.                     *
+ *                                                                         *
+ *   This library is distributed in the hope that it will be useful, but   *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   Lesser General Public License for more details.                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU Lesser General Public      *
+ *   License along with this library; if not, write to the Free Software   *
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
+ *   USA                                                                   *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifdef WITH_MP4
+
+#include <taglib.h>
+#include <tdebug.h>
+#include "mp4item.h"
+
+using namespace TagLib;
+
+class MP4::Item::ItemPrivate : public RefCounter
+{
+public:
+  ItemPrivate() : RefCounter(), valid(true) {}
+
+  bool valid;
+  union {
+    bool m_bool;
+    int m_int;
+    IntPair m_intPair;
+  };
+  StringList m_stringList;
+};
+
+MP4::Item::Item()
+{
+  d = new ItemPrivate;
+  d->valid = false;
+}
+
+MP4::Item::Item(const Item &item) : d(item.d)
+{
+  d->ref();
+}
+
+MP4::Item &
+MP4::Item::operator=(const Item &item)
+{
+  if(d->deref()) {
+    delete d;
+  }
+  d = item.d;
+  d->ref();
+  return *this;
+}
+
+MP4::Item::~Item()
+{
+  if(d->deref()) {
+    delete d;
+  }
+}
+
+MP4::Item::Item(bool value)
+{
+  d = new ItemPrivate;
+  d->m_bool = value;
+}
+
+MP4::Item::Item(int value)
+{
+  d = new ItemPrivate;
+  d->m_int = value;
+}
+
+MP4::Item::Item(int value1, int value2)
+{
+  d = new ItemPrivate;
+  d->m_intPair.first = value1;
+  d->m_intPair.second = value2;
+}
+
+MP4::Item::Item(const StringList &value)
+{
+  d = new ItemPrivate;
+  d->m_stringList = value;
+}
+
+bool
+MP4::Item::toBool() const
+{
+  return d->m_bool;
+}
+
+int
+MP4::Item::toInt() const
+{
+  return d->m_int;
+}
+
+MP4::Item::IntPair
+MP4::Item::toIntPair() const
+{
+  return d->m_intPair;
+}
+
+StringList
+MP4::Item::toStringList() const
+{
+  return d->m_stringList;
+}
+
+bool
+MP4::Item::isValid() const
+{
+  return d->valid;
+}
+
+#endif
diff --git a/taglib/mp4/mp4item.h b/taglib/mp4/mp4item.h
new file mode 100644 (file)
index 0000000..d53c41c
--- /dev/null
@@ -0,0 +1,69 @@
+/**************************************************************************
+    copyright            : (C) 2007 by Lukáš Lalinský
+    email                : lalinsky@gmail.com
+ **************************************************************************/
+
+/***************************************************************************
+ *   This library is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License version   *
+ *   2.1 as published by the Free Software Foundation.                     *
+ *                                                                         *
+ *   This library is distributed in the hope that it will be useful, but   *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   Lesser General Public License for more details.                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU Lesser General Public      *
+ *   License along with this library; if not, write to the Free Software   *
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
+ *   USA                                                                   *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+#ifndef TAGLIB_MP4ITEM_H
+#define TAGLIB_MP4ITEM_H
+
+#include <tstringlist.h>
+#include "taglib_export.h"
+
+namespace TagLib {
+
+  namespace MP4 {
+
+    class TAGLIB_EXPORT Item
+    {
+    public:
+      struct IntPair {
+        int first, second;
+      };
+
+      Item();
+      Item(const Item &item);
+      Item &operator=(const Item &item);
+      ~Item();
+
+      Item(int value);
+      Item(bool value);
+      Item(int first, int second);
+      Item(const StringList &value);
+
+      int toInt() const;
+      bool toBool() const;
+      IntPair toIntPair() const;
+      StringList toStringList() const;
+
+      bool isValid() const;
+
+    private:
+      class ItemPrivate;
+      ItemPrivate *d;
+    };
+
+  }
+
+}
+
+#endif
diff --git a/taglib/mp4/mp4properties.cpp b/taglib/mp4/mp4properties.cpp
new file mode 100644 (file)
index 0000000..c973d3a
--- /dev/null
@@ -0,0 +1,169 @@
+/**************************************************************************
+    copyright            : (C) 2007 by Lukáš Lalinský
+    email                : lalinsky@gmail.com
+ **************************************************************************/
+
+/***************************************************************************
+ *   This library is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License version   *
+ *   2.1 as published by the Free Software Foundation.                     *
+ *                                                                         *
+ *   This library is distributed in the hope that it will be useful, but   *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   Lesser General Public License for more details.                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU Lesser General Public      *
+ *   License along with this library; if not, write to the Free Software   *
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
+ *   USA                                                                   *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifdef WITH_MP4
+
+#include <tdebug.h>
+#include <tstring.h>
+#include "mp4file.h"
+#include "mp4atom.h"
+#include "mp4properties.h"
+
+using namespace TagLib;
+
+class MP4::Properties::PropertiesPrivate
+{
+public:
+  PropertiesPrivate() : length(0), bitrate(0), sampleRate(0), channels(0), bitsPerSample(0) {}
+
+  int length;
+  int bitrate;
+  int sampleRate;
+  int channels;
+  int bitsPerSample;
+};
+
+MP4::Properties::Properties(File *file, MP4::Atoms *atoms, ReadStyle style)
+  : AudioProperties(style)
+{
+  d = new PropertiesPrivate;
+
+  MP4::Atom *moov = atoms->find("moov");
+  if(!moov) {
+    debug("MP4: Atom 'moov' not found");
+    return;
+  }
+
+  MP4::Atom *trak = 0;
+  ByteVector data;
+
+  MP4::AtomList trakList = moov->findall("trak");
+  for (unsigned int i = 0; i < trakList.size(); i++) {
+    trak = trakList[i];
+    MP4::Atom *hdlr = trak->find("mdia", "hdlr");
+    if(!hdlr) {
+      debug("MP4: Atom 'trak.mdia.hdlr' not found");
+      return;
+    }
+    file->seek(hdlr->offset);
+    data = file->readBlock(hdlr->length);
+    if(data.mid(16, 4) == "soun") {
+      break;
+    }
+    trak = 0;
+  }
+  if (!trak) {
+    debug("MP4: No audio tracks");
+    return;
+  }
+
+  MP4::Atom *mdhd = trak->find("mdia", "mdhd");
+  if(!mdhd) {
+    debug("MP4: Atom 'trak.mdia.mdhd' not found");
+    return;
+  }
+
+  file->seek(mdhd->offset);
+  data = file->readBlock(mdhd->length);
+  if(data[8] == 0) {
+    unsigned int unit = data.mid(20, 4).toUInt();
+    unsigned int length = data.mid(24, 4).toUInt();
+    d->length = length / unit;
+  }
+  else {
+    long long unit = data.mid(28, 8).toLongLong();
+    long long length = data.mid(36, 8).toLongLong();
+    d->length = int(length / unit);
+  }
+
+  MP4::Atom *atom = trak->find("mdia", "minf", "stbl", "stsd");
+  if(!atom) {
+    return;
+  }
+
+  file->seek(atom->offset);
+  data = file->readBlock(atom->length);
+  if(data.mid(20, 4) == "mp4a") {
+    d->channels = data.mid(40, 2).toShort();
+    d->bitsPerSample = data.mid(42, 2).toShort();
+    d->sampleRate = data.mid(46, 4).toUInt();
+    if(data.mid(56, 4) == "esds" && data[64] == 0x03) {
+      long pos = 65;
+      if(data.mid(pos, 3) == "\x80\x80\x80") {
+        pos += 3;
+      }
+      pos += 4;
+      if(data[pos] == 0x04) {
+        pos += 1;
+        if(data.mid(pos, 3) == "\x80\x80\x80") {
+          pos += 3;
+        }
+        pos += 10;
+        d->bitrate = (data.mid(pos, 4).toUInt() + 500) / 1000;
+      }
+    }
+  }
+}
+
+MP4::Properties::~Properties()
+{
+  delete d;
+}
+
+int
+MP4::Properties::channels() const
+{
+  return d->channels;
+}
+
+int
+MP4::Properties::sampleRate() const
+{
+  return d->sampleRate;
+}
+
+int
+MP4::Properties::length() const
+{
+  return d->length;
+}
+
+int
+MP4::Properties::bitrate() const
+{
+  return d->bitrate;
+}
+
+int
+MP4::Properties::bitsPerSample() const
+{
+  return d->bitsPerSample;
+}
+
+#endif
diff --git a/taglib/mp4/mp4properties.h b/taglib/mp4/mp4properties.h
new file mode 100644 (file)
index 0000000..fb76c8a
--- /dev/null
@@ -0,0 +1,61 @@
+/**************************************************************************
+    copyright            : (C) 2007 by Lukáš Lalinský
+    email                : lalinsky@gmail.com
+ **************************************************************************/
+
+/***************************************************************************
+ *   This library is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License version   *
+ *   2.1 as published by the Free Software Foundation.                     *
+ *                                                                         *
+ *   This library is distributed in the hope that it will be useful, but   *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   Lesser General Public License for more details.                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU Lesser General Public      *
+ *   License along with this library; if not, write to the Free Software   *
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
+ *   USA                                                                   *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+#ifndef TAGLIB_MP4PROPERTIES_H
+#define TAGLIB_MP4PROPERTIES_H
+
+#include "taglib_export.h"
+#include "audioproperties.h"
+
+namespace TagLib {
+
+  namespace MP4 {
+
+    class Atoms;
+    class File;
+
+    //! An implementation of MP4 audio properties
+    class TAGLIB_EXPORT Properties : public AudioProperties
+    {
+    public:
+      Properties(File *file, Atoms *atoms, ReadStyle style = Average);
+      virtual ~Properties();
+
+      virtual int length() const;
+      virtual int bitrate() const;
+      virtual int sampleRate() const;
+      virtual int channels() const;
+      virtual int bitsPerSample() const;
+
+    private:
+      class PropertiesPrivate;
+      PropertiesPrivate *d;
+    };
+
+  }
+
+}
+
+#endif
diff --git a/taglib/mp4/mp4tag.cpp b/taglib/mp4/mp4tag.cpp
new file mode 100644 (file)
index 0000000..229c475
--- /dev/null
@@ -0,0 +1,558 @@
+/**************************************************************************
+    copyright            : (C) 2007 by Lukáš Lalinský
+    email                : lalinsky@gmail.com
+ **************************************************************************/
+
+/***************************************************************************
+ *   This library is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License version   *
+ *   2.1 as published by the Free Software Foundation.                     *
+ *                                                                         *
+ *   This library is distributed in the hope that it will be useful, but   *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   Lesser General Public License for more details.                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU Lesser General Public      *
+ *   License along with this library; if not, write to the Free Software   *
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
+ *   USA                                                                   *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifdef WITH_MP4
+
+#include <tdebug.h>
+#include <tstring.h>
+#include "mp4atom.h"
+#include "mp4tag.h"
+
+using namespace TagLib;
+
+class MP4::Tag::TagPrivate
+{
+public:
+  TagPrivate() : file(0), atoms(0) {}
+  ~TagPrivate() {}
+  File *file;
+  Atoms *atoms;
+  ItemListMap items;
+};
+
+MP4::Tag::Tag(File *file, MP4::Atoms *atoms)
+{
+  d = new TagPrivate;
+  d->file = file;
+  d->atoms = atoms;
+
+  MP4::Atom *ilst = atoms->find("moov", "udta", "meta", "ilst");
+  if(!ilst) {
+    //debug("Atom moov.udta.meta.ilst not found.");
+    return;
+  }
+
+  for(unsigned int i = 0; i < ilst->children.size(); i++) {
+    MP4::Atom *atom = ilst->children[i];
+    file->seek(atom->offset + 8);
+    if(atom->name == "----") {
+      parseFreeForm(atom, file);
+    }
+    else if(atom->name == "trkn" || atom->name == "disk") {
+      parseIntPair(atom, file);
+    }
+    else if(atom->name == "cpil" || atom->name == "pgap" || atom->name == "pcst") {
+      parseBool(atom, file);
+    }
+    else if(atom->name == "tmpo") {
+      parseInt(atom, file);
+    }
+    else {
+      parseText(atom, file);
+    }
+  }
+}
+
+ByteVectorList
+MP4::Tag::parseData(MP4::Atom *atom, TagLib::File *file, int expectedFlags, bool freeForm)
+{
+  ByteVectorList result;
+  ByteVector data = file->readBlock(atom->length - 8);
+  int i = 0;
+  unsigned int pos = 0;
+  while(pos < data.size()) {
+    int length = data.mid(pos, 4).toUInt();
+    ByteVector name = data.mid(pos + 4, 4);
+    int flags = data.mid(pos + 8, 4).toUInt();
+    if(freeForm && i < 2) {
+      if(i == 0 && name != "mean") {
+        debug("MP4: Unexpected atom \"" + name + "\", expecting \"mean\"");
+        return result;
+      }
+      else if(i == 1 && name != "name") {
+        debug("MP4: Unexpected atom \"" + name + "\", expecting \"name\"");
+        return result;
+      }
+      result.append(data.mid(pos + 12, length - 12));
+    }
+    else {
+      if(name != "data") {
+        debug("MP4: Unexpected atom \"" + name + "\", expecting \"data\"");
+        return result;
+      }
+      if(expectedFlags == -1 || flags == expectedFlags) {
+        result.append(data.mid(pos + 16, length - 16));
+      }
+    }
+    pos += length;
+    i++;
+  }
+  return result;
+}
+
+void
+MP4::Tag::parseInt(MP4::Atom *atom, TagLib::File *file)
+{
+  ByteVectorList data = parseData(atom, file);
+  if(data.size()) {
+    d->items.insert(atom->name, (int)data[0].toShort());
+  }
+}
+
+void
+MP4::Tag::parseIntPair(MP4::Atom *atom, TagLib::File *file)
+{
+  ByteVectorList data = parseData(atom, file);
+  if(data.size()) {
+    int a = data[0].mid(2, 2).toShort();
+    int b = data[0].mid(4, 2).toShort();
+    d->items.insert(atom->name, MP4::Item(a, b));
+  }
+}
+
+void
+MP4::Tag::parseBool(MP4::Atom *atom, TagLib::File *file)
+{
+  ByteVectorList data = parseData(atom, file);
+  if(data.size()) {
+    d->items.insert(atom->name, data[0][0] != '\0');
+  }
+}
+
+void
+MP4::Tag::parseText(MP4::Atom *atom, TagLib::File *file, int expectedFlags)
+{
+  ByteVectorList data = parseData(atom, file, expectedFlags);
+  if(data.size()) {
+    StringList value;
+    for(unsigned int i = 0; i < data.size(); i++) {
+      value.append(String(data[i], String::UTF8));
+    }
+    d->items.insert(atom->name, value);
+  }
+}
+
+void
+MP4::Tag::parseFreeForm(MP4::Atom *atom, TagLib::File *file)
+{
+  ByteVectorList data = parseData(atom, file, 1, true);
+  if(data.size() > 2) {
+    StringList value;
+    for(unsigned int i = 2; i < data.size(); i++) {
+      value.append(String(data[i], String::UTF8));
+    }
+    String name = "----:" + data[0] + ":" + data[1];
+    d->items.insert(name, value);
+  }
+}
+
+ByteVector
+MP4::Tag::padIlst(const ByteVector &data, int length)
+{
+  if (length == -1) {
+    length = ((data.size() + 1023) & ~1023) - data.size();
+  }
+  return renderAtom("free", ByteVector(length, '\1'));
+}
+
+ByteVector
+MP4::Tag::renderAtom(const ByteVector &name, const ByteVector &data)
+{
+  return ByteVector::fromUInt(data.size() + 8) + name + data;
+}
+
+ByteVector
+MP4::Tag::renderData(const ByteVector &name, int flags, const ByteVectorList &data)
+{
+  ByteVector result;
+  for(unsigned int i = 0; i < data.size(); i++) {
+    result.append(renderAtom("data", ByteVector::fromUInt(flags) + ByteVector(4, '\0') + data[i]));
+  }
+  return renderAtom(name, result);
+}
+
+ByteVector
+MP4::Tag::renderBool(const ByteVector &name, MP4::Item &item)
+{
+  ByteVectorList data;
+  data.append(ByteVector(1, item.toBool() ? '\1' : '\0'));
+  return renderData(name, 0x15, data);
+}
+
+ByteVector
+MP4::Tag::renderInt(const ByteVector &name, MP4::Item &item)
+{
+  ByteVectorList data;
+  data.append(ByteVector::fromShort(item.toInt()));
+  return renderData(name, 0x15, data);
+}
+
+ByteVector
+MP4::Tag::renderIntPair(const ByteVector &name, MP4::Item &item)
+{
+  ByteVectorList data;
+  data.append(ByteVector(2, '\0') +
+              ByteVector::fromShort(item.toIntPair().first) +
+              ByteVector::fromShort(item.toIntPair().second) +
+              ByteVector(2, '\0'));
+  return renderData(name, 0x15, data);
+}
+
+ByteVector
+MP4::Tag::renderIntPairNoTrailing(const ByteVector &name, MP4::Item &item)
+{
+  ByteVectorList data;
+  data.append(ByteVector(2, '\0') +
+              ByteVector::fromShort(item.toIntPair().first) +
+              ByteVector::fromShort(item.toIntPair().second));
+  return renderData(name, 0x15, data);
+}
+
+ByteVector
+MP4::Tag::renderText(const ByteVector &name, MP4::Item &item, int flags)
+{
+  ByteVectorList data;
+  StringList value = item.toStringList();
+  for(unsigned int i = 0; i < value.size(); i++) {
+    data.append(value[i].data(String::UTF8));
+  }
+  return renderData(name, flags, data);
+}
+
+ByteVector
+MP4::Tag::renderFreeForm(const String &name, MP4::Item &item)
+{
+  StringList header = StringList::split(name, ":");
+  if (header.size() != 3) {
+    debug("MP4: Invalid free-form item name \"" + name + "\"");
+    return ByteVector::null;
+  }
+  ByteVector data;
+  data.append(renderAtom("mean", ByteVector::fromUInt(0) + header[1].data(String::UTF8)));
+  data.append(renderAtom("name", ByteVector::fromUInt(0) + header[2].data(String::UTF8)));
+  StringList value = item.toStringList();
+  for(unsigned int i = 0; i < value.size(); i++) {
+    data.append(renderAtom("data", ByteVector::fromUInt(1) + ByteVector(4, '\0') + value[i].data(String::UTF8)));
+  }
+  return renderAtom("----", data);
+}
+
+bool
+MP4::Tag::save()
+{
+  ByteVector data;
+  for(MP4::ItemListMap::Iterator i = d->items.begin(); i != d->items.end(); i++) {
+    const String name = i->first;
+    if(name.startsWith("----")) {
+      data.append(renderFreeForm(name, i->second));
+    }
+    else if(name == "trkn") {
+      data.append(renderIntPair(name.data(String::Latin1), i->second));
+    }
+    else if(name == "disk") {
+      data.append(renderIntPairNoTrailing(name.data(String::Latin1), i->second));
+    }
+    else if(name == "cpil" || name == "pgap" || name == "pcst") {
+      data.append(renderBool(name.data(String::Latin1), i->second));
+    }
+    else if(name == "tmpo") {
+      data.append(renderInt(name.data(String::Latin1), i->second));
+    }
+    else if(name.size() == 4){
+      data.append(renderText(name.data(String::Latin1), i->second));
+    }
+    else {
+      debug("MP4: Unknown item name \"" + name + "\"");
+    }
+  }
+  data = renderAtom("ilst", data);
+
+  AtomList path = d->atoms->path("moov", "udta", "meta", "ilst");
+  if(path.size() == 4) {
+    saveExisting(data, path);
+  }
+  else {
+    saveNew(data);
+  }
+
+  return true;
+}
+
+void
+MP4::Tag::updateParents(AtomList &path, long delta, int ignore)
+{
+  for(unsigned int i = 0; i < path.size() - ignore; i++) {
+    d->file->seek(path[i]->offset);
+    long size = d->file->readBlock(4).toUInt() + delta;
+    d->file->seek(path[i]->offset);
+    d->file->writeBlock(ByteVector::fromUInt(size));
+  }
+}
+
+void
+MP4::Tag::updateOffsets(long delta, long offset)
+{
+  MP4::Atom *moov = d->atoms->find("moov");
+  if(moov) {
+    MP4::AtomList stco = moov->findall("stco", true);
+    for(unsigned int i = 0; i < stco.size(); i++) {
+      MP4::Atom *atom = stco[i];
+      if(atom->offset > offset) {
+        atom->offset += delta;
+      }
+      d->file->seek(atom->offset + 12);
+      ByteVector data = d->file->readBlock(atom->length - 12);
+      unsigned int count = data.mid(0, 4).toUInt();
+      d->file->seek(atom->offset + 16);
+      int pos = 4;
+      while(count--) {
+        long o = data.mid(pos, 4).toUInt();
+        if(o > offset) {
+          o += delta;
+        }
+        d->file->writeBlock(ByteVector::fromUInt(o));
+        pos += 4;
+      }
+    }
+
+    MP4::AtomList co64 = moov->findall("co64", true);
+    for(unsigned int i = 0; i < co64.size(); i++) {
+      MP4::Atom *atom = co64[i];
+      if(atom->offset > offset) {
+        atom->offset += delta;
+      }
+      d->file->seek(atom->offset + 12);
+      ByteVector data = d->file->readBlock(atom->length - 12);
+      unsigned int count = data.mid(0, 4).toUInt();
+      d->file->seek(atom->offset + 16);
+      int pos = 4;
+      while(count--) {
+        long long o = data.mid(pos, 8).toLongLong();
+        if(o > offset) {
+          o += delta;
+        }
+        d->file->writeBlock(ByteVector::fromLongLong(o));
+        pos += 8;
+      }
+    }
+  }
+
+  MP4::Atom *moof = d->atoms->find("moof");
+  if(moof) {
+    MP4::AtomList tfhd = moof->findall("tfhd", true);
+    for(unsigned int i = 0; i < tfhd.size(); i++) {
+      MP4::Atom *atom = tfhd[i];
+      if(atom->offset > offset) {
+        atom->offset += delta;
+      }
+      d->file->seek(atom->offset + 9);
+      ByteVector data = d->file->readBlock(atom->offset - 9);
+      unsigned int flags = (ByteVector(1, '\0') + data.mid(0, 3)).toUInt();
+      if(flags & 1) {
+        long long o = data.mid(7, 8).toLongLong();
+        if(o > offset) {
+          o += delta;
+        }
+        d->file->seek(atom->offset + 16);
+        d->file->writeBlock(ByteVector::fromLongLong(o));
+      }
+    }
+  }
+}
+
+void
+MP4::Tag::saveNew(ByteVector &data)
+{
+  data = renderAtom("meta", TagLib::ByteVector(4, '\0') +
+                    renderAtom("hdlr", TagLib::ByteVector(8, '\0') + TagLib::ByteVector("mdirappl") + TagLib::ByteVector(9, '\0')) +
+                    data + padIlst(data));
+
+  AtomList path = d->atoms->path("moov", "udta");
+  if(path.size() != 2) {
+    path = d->atoms->path("moov");
+    data = renderAtom("udta", data);
+  }
+
+  long offset = path[path.size() - 1]->offset + 8;
+  d->file->insert(data, offset, 0);
+
+  updateParents(path, data.size());
+  updateOffsets(data.size(), offset);
+}
+
+void
+MP4::Tag::saveExisting(ByteVector &data, AtomList &path)
+{
+  MP4::Atom *ilst = path[path.size() - 1];
+  long offset = ilst->offset;
+  long length = ilst->length;
+
+  MP4::Atom *meta = path[path.size() - 2];
+  AtomList::Iterator index = meta->children.find(ilst);
+  if(index != meta->children.begin()) {
+    AtomList::Iterator prevIndex = index;
+    prevIndex--;
+    MP4::Atom *prev = *prevIndex;
+    if(prev->name == "free") {
+      offset = prev->offset;
+      length += prev->length;
+    }
+  }
+  if(index != meta->children.end()) {
+    AtomList::Iterator nextIndex = index;
+    nextIndex++;
+    MP4::Atom *next = *nextIndex;
+    if(next->name == "free") {
+      length += next->length;
+    }
+  }
+
+  long delta = data.size() - length;
+  if(delta > 0 || (delta < 0 && delta > -8)) {
+    data.append(padIlst(data));
+    delta = data.size() - length;
+  }
+  else if(delta < 0) {
+    data.append(padIlst(data, -delta - 8));
+    delta = 0;
+  }
+
+  d->file->insert(data, offset, length);
+
+  if(delta) {
+    updateParents(path, delta, 1);
+    updateOffsets(delta, offset);
+  }
+}
+
+String
+MP4::Tag::title() const
+{
+  if(d->items.contains("\251nam"))
+    return d->items["\251nam"].toStringList().toString(", ");
+  return String::null;
+}
+
+String
+MP4::Tag::artist() const
+{
+  if(d->items.contains("\251ART"))
+    return d->items["\251ART"].toStringList().toString(", ");
+  return String::null;
+}
+
+String
+MP4::Tag::album() const
+{
+  if(d->items.contains("\251alb"))
+    return d->items["\251alb"].toStringList().toString(", ");
+  return String::null;
+}
+
+String
+MP4::Tag::comment() const
+{
+  if(d->items.contains("\251cmt"))
+    return d->items["\251cmt"].toStringList().toString(", ");
+  return String::null;
+}
+
+String
+MP4::Tag::genre() const
+{
+  if(d->items.contains("\251gen"))
+    return d->items["\251gen"].toStringList().toString(", ");
+  return String::null;
+}
+
+unsigned int
+MP4::Tag::year() const
+{
+  if(d->items.contains("\251day"))
+    return d->items["\251day"].toStringList().toString().toInt();
+  return 0;
+}
+
+unsigned int
+MP4::Tag::track() const
+{
+  if(d->items.contains("trkn"))
+    return d->items["trkn"].toIntPair().first;
+  return 0;
+}
+
+void
+MP4::Tag::setTitle(const String &value)
+{
+  d->items["\251nam"] = StringList(value);
+}
+
+void
+MP4::Tag::setArtist(const String &value)
+{
+  d->items["\251ART"] = StringList(value);
+}
+
+void
+MP4::Tag::setAlbum(const String &value)
+{
+  d->items["\251alb"] = StringList(value);
+}
+
+void
+MP4::Tag::setComment(const String &value)
+{
+  d->items["\251cmt"] = StringList(value);
+}
+
+void
+MP4::Tag::setGenre(const String &value)
+{
+  d->items["\251gen"] = StringList(value);
+}
+
+void
+MP4::Tag::setYear(uint value)
+{
+  d->items["\251day"] = StringList(String::number(value));
+}
+
+void
+MP4::Tag::setTrack(uint value)
+{
+  d->items["trkn"] = MP4::Item(value, 0);
+}
+
+MP4::ItemListMap &
+MP4::Tag::itemListMap()
+{
+  return d->items;
+}
+
+#endif
diff --git a/taglib/mp4/mp4tag.h b/taglib/mp4/mp4tag.h
new file mode 100644 (file)
index 0000000..4671d16
--- /dev/null
@@ -0,0 +1,100 @@
+/**************************************************************************
+    copyright            : (C) 2007 by Lukáš Lalinský
+    email                : lalinsky@gmail.com
+ **************************************************************************/
+
+/***************************************************************************
+ *   This library is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU Lesser General Public License version   *
+ *   2.1 as published by the Free Software Foundation.                     *
+ *                                                                         *
+ *   This library is distributed in the hope that it will be useful, but   *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   Lesser General Public License for more details.                       *
+ *                                                                         *
+ *   You should have received a copy of the GNU Lesser General Public      *
+ *   License along with this library; if not, write to the Free Software   *
+ *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
+ *   USA                                                                   *
+ *                                                                         *
+ *   Alternatively, this file is available under the Mozilla Public        *
+ *   License Version 1.1.  You may obtain a copy of the License at         *
+ *   http://www.mozilla.org/MPL/                                           *
+ ***************************************************************************/
+
+#ifndef TAGLIB_MP4TAG_H
+#define TAGLIB_MP4TAG_H
+
+#include <tag.h>
+#include <tbytevectorlist.h>
+#include <tfile.h>
+#include <tmap.h>
+#include <tstringlist.h>
+#include "taglib_export.h"
+#include "mp4atom.h"
+#include "mp4item.h"
+
+namespace TagLib {
+
+  namespace MP4 {
+
+    typedef TagLib::Map<String, Item> ItemListMap;
+
+    class TAGLIB_EXPORT Tag: public TagLib::Tag
+    {
+    public:
+        Tag(TagLib::File *file, Atoms *atoms);
+        bool save();
+
+        String title() const;
+        String artist() const;
+        String album() const;
+        String comment() const;
+        String genre() const;
+        uint year() const;
+        uint track() const;
+
+        void setTitle(const String &value);
+        void setArtist(const String &value);
+        void setAlbum(const String &value);
+        void setComment(const String &value);
+        void setGenre(const String &value);
+        void setYear(uint value);
+        void setTrack(uint value);
+
+        ItemListMap &itemListMap();
+
+    private:
+        TagLib::ByteVectorList parseData(Atom *atom, TagLib::File *file, int expectedFlags = -1, bool freeForm = false);
+        void parseText(Atom *atom, TagLib::File *file, int expectedFlags = 1);
+        void parseFreeForm(Atom *atom, TagLib::File *file);
+        void parseInt(Atom *atom, TagLib::File *file);
+        void parseIntPair(Atom *atom, TagLib::File *file);
+        void parseBool(Atom *atom, TagLib::File *file);
+
+        TagLib::ByteVector padIlst(const ByteVector &data, int length = -1);
+        TagLib::ByteVector renderAtom(const ByteVector &name, const TagLib::ByteVector &data);
+        TagLib::ByteVector renderData(const ByteVector &name, int flags, const TagLib::ByteVectorList &data);
+        TagLib::ByteVector renderText(const ByteVector &name, Item &item, int flags = 1);
+        TagLib::ByteVector renderFreeForm(const String &name, Item &item);
+        TagLib::ByteVector renderBool(const ByteVector &name, Item &item);
+        TagLib::ByteVector renderInt(const ByteVector &name, Item &item);
+        TagLib::ByteVector renderIntPair(const ByteVector &name, Item &item);
+        TagLib::ByteVector renderIntPairNoTrailing(const ByteVector &name, Item &item);
+
+        void updateParents(AtomList &path, long delta, int ignore = 0);
+        void updateOffsets(long delta, long offset);
+
+        void saveNew(TagLib::ByteVector &data);
+        void saveExisting(TagLib::ByteVector &data, AtomList &path);
+
+        class TagPrivate;
+        TagPrivate *d;
+    };
+
+  }
+
+}
+
+#endif
index 76be4c45cfda1bc020f7d79136d237701474e50d..188179a46a11bbb72b9228e459668fc5364c88d7 100644 (file)
@@ -14,6 +14,7 @@ DEPENDPATH += . \
               ape \
               flac \
               mpc \
+              mp4 \
               mpeg \
               ogg \
               ogg/speex \
@@ -33,6 +34,7 @@ INCLUDEPATH += . \
                flac \
                ogg/flac \
                mpc \
+               mp4 \
                wavpack \
                ogg/speex \
                trueaudio \
@@ -54,6 +56,10 @@ HEADERS += audioproperties.h \
            flac/flacproperties.h \
            mpc/mpcfile.h \
            mpc/mpcproperties.h \
+           mp4/mp4atom.h \
+           mp4/mp4item.h \
+           mp4/mp4file.h \
+           mp4/mp4properties.h \
            mpeg/mpegfile.h \
            mpeg/mpegheader.h \
            mpeg/mpegproperties.h \
@@ -111,6 +117,10 @@ SOURCES += audioproperties.cpp \
            ape/apetag.cpp \
            flac/flacfile.cpp \
            flac/flacproperties.cpp \
+           mp4/mp4atom.cpp \
+           mp4/mp4item.cpp \
+           mp4/mp4file.cpp \
+           mp4/mp4properties.cpp \
            mpc/mpcfile.cpp \
            mpc/mpcproperties.cpp \
            mpeg/mpegfile.cpp \
@@ -168,6 +178,10 @@ SOURCES += audioproperties.cpp \
            ape/apetag.h \
            flac/flacfile.h \
            flac/flacproperties.h \
+           mp4/mp4atom.h \
+           mp4/mp4item.h \
+           mp4/mp4file.h \
+           mp4/mp4properties.h \
            mpc/mpcfile.h \
            mpc/mpcproperties.h \
            mpeg/mpegfile.h \
index 88c08098e1043573bf17cf0bdfa5a63cf9da496b..99d648d05fa77ec3826cbedbcd531667e34ae2a8 100644 (file)
@@ -3,10 +3,12 @@ if(BUILD_TESTS)
 INCLUDE_DIRECTORIES(
   ${CMAKE_CURRENT_SOURCE_DIR}/../taglib
   ${CMAKE_CURRENT_SOURCE_DIR}/../taglib/toolkit
+  ${CMAKE_CURRENT_SOURCE_DIR}/../taglib/asf
   ${CMAKE_CURRENT_SOURCE_DIR}/../taglib/mpeg/id3v1
   ${CMAKE_CURRENT_SOURCE_DIR}/../taglib/mpeg/id3v2
   ${CMAKE_CURRENT_SOURCE_DIR}/../taglib/mpeg/id3v2/frames
   ${CMAKE_CURRENT_SOURCE_DIR}/../taglib/mpeg
+  ${CMAKE_CURRENT_SOURCE_DIR}/../taglib/mp4
   ${CMAKE_CURRENT_SOURCE_DIR}/../taglib/trueaudio
 )
 
@@ -23,6 +25,12 @@ SET(test_runner_SRCS
   test_id3v1.cpp
   test_id3v2.cpp
 )
+IF(WITH_MP4)
+SET(test_runner_SRCS ${test_runner_SRCS} test_mp4.cpp)
+ENDIF(WITH_MP4)
+IF(WITH_ASF)
+SET(test_runner_SRCS ${test_runner_SRCS} test_asf.cpp)
+ENDIF(WITH_ASF)
 
 ADD_EXECUTABLE(test_runner ${test_runner_SRCS})
 TARGET_LINK_LIBRARIES(test_runner tag ${CPPUNIT_LIBRARIES})
diff --git a/tests/data/click.wv b/tests/data/click.wv
new file mode 100644 (file)
index 0000000..f8bd1a8
Binary files /dev/null and b/tests/data/click.wv differ
diff --git a/tests/data/has-tags.m4a b/tests/data/has-tags.m4a
new file mode 100644 (file)
index 0000000..f48a28b
Binary files /dev/null and b/tests/data/has-tags.m4a differ
diff --git a/tests/data/no-tags.3g2 b/tests/data/no-tags.3g2
new file mode 100644 (file)
index 0000000..d31a6ce
Binary files /dev/null and b/tests/data/no-tags.3g2 differ
diff --git a/tests/data/no-tags.m4a b/tests/data/no-tags.m4a
new file mode 100644 (file)
index 0000000..ba4e92b
Binary files /dev/null and b/tests/data/no-tags.m4a differ
diff --git a/tests/data/silence-1.wma b/tests/data/silence-1.wma
new file mode 100644 (file)
index 0000000..e06f917
Binary files /dev/null and b/tests/data/silence-1.wma differ
diff --git a/tests/test_asf.cpp b/tests/test_asf.cpp
new file mode 100644 (file)
index 0000000..2b1e7aa
--- /dev/null
@@ -0,0 +1,103 @@
+#include <cppunit/extensions/HelperMacros.h>
+#include <string>
+#include <stdio.h>
+#include <tag.h>
+#include <tstringlist.h>
+#include <tbytevectorlist.h>
+#include <asffile.h>
+#include "utils.h"
+
+using namespace std;
+using namespace TagLib;
+
+class TestASF : public CppUnit::TestFixture
+{
+  CPPUNIT_TEST_SUITE(TestASF);
+  CPPUNIT_TEST(testProperties);
+  CPPUNIT_TEST(testRead);
+  CPPUNIT_TEST(testSaveMultipleValues);
+  CPPUNIT_TEST(testSaveStream);
+  CPPUNIT_TEST(testSaveLanguage);
+  CPPUNIT_TEST_SUITE_END();
+
+public:
+
+  void testProperties()
+  {
+    ASF::File f("data/silence-1.wma");
+    CPPUNIT_ASSERT_EQUAL(4, f.audioProperties()->length());
+    CPPUNIT_ASSERT_EQUAL(64, f.audioProperties()->bitrate());
+    CPPUNIT_ASSERT_EQUAL(2, f.audioProperties()->channels());
+    CPPUNIT_ASSERT_EQUAL(48000, f.audioProperties()->sampleRate());
+  }
+
+  void testRead()
+  {
+    ASF::File f("data/silence-1.wma");
+    CPPUNIT_ASSERT_EQUAL(String("test"), f.tag()->title());
+  }
+
+  void testSaveMultipleValues()
+  {
+    string newname = copyFile("silence-1", ".wma");
+
+    ASF::File *f = new ASF::File(newname.c_str());
+    ASF::AttributeList values;
+    values.append("Foo");
+    values.append("Bar");
+    f->tag()->attributeListMap()["WM/AlbumTitle"] = values;
+    f->save();
+    delete f;
+
+    f = new ASF::File(newname.c_str());
+    CPPUNIT_ASSERT_EQUAL(2, (int)f->tag()->attributeListMap()["WM/AlbumTitle"].size());
+    delete f;
+
+    deleteFile(newname);
+  }
+
+  void testSaveStream()
+  {
+    string newname = copyFile("silence-1", ".wma");
+
+    ASF::File *f = new ASF::File(newname.c_str());
+    ASF::AttributeList values;
+    ASF::Attribute attr("Foo");
+    attr.setStream(43);
+    values.append(attr);
+    f->tag()->attributeListMap()["WM/AlbumTitle"] = values;
+    f->save();
+    delete f;
+
+    f = new ASF::File(newname.c_str());
+    CPPUNIT_ASSERT_EQUAL(43, f->tag()->attributeListMap()["WM/AlbumTitle"][0].stream());
+    delete f;
+
+    deleteFile(newname);
+  }
+
+  void testSaveLanguage()
+  {
+    string newname = copyFile("silence-1", ".wma");
+
+    ASF::File *f = new ASF::File(newname.c_str());
+    ASF::AttributeList values;
+    ASF::Attribute attr("Foo");
+    attr.setStream(32);
+    attr.setLanguage(56);
+    values.append(attr);
+    f->tag()->attributeListMap()["WM/AlbumTitle"] = values;
+    f->save();
+    delete f;
+
+    f = new ASF::File(newname.c_str());
+    CPPUNIT_ASSERT_EQUAL(32, f->tag()->attributeListMap()["WM/AlbumTitle"][0].stream());
+    CPPUNIT_ASSERT_EQUAL(56, f->tag()->attributeListMap()["WM/AlbumTitle"][0].language());
+    delete f;
+
+    deleteFile(newname);
+  }
+
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(TestASF);
index 0752c08a4383732e343fb08b44f1e4676d835907..c2cba179f1f03cf0cf2ccf9be8596786711fa2b7 100644 (file)
@@ -11,11 +11,15 @@ using namespace TagLib;
 class TestFileRef : public CppUnit::TestFixture
 {
   CPPUNIT_TEST_SUITE(TestFileRef);
+  CPPUNIT_TEST(testASF);
   CPPUNIT_TEST(testMusepack);
   CPPUNIT_TEST(testVorbis);
   CPPUNIT_TEST(testSpeex);
   CPPUNIT_TEST(testFLAC);
   CPPUNIT_TEST(testMP3);
+  CPPUNIT_TEST(testMP4_1);
+  CPPUNIT_TEST(testMP4_2);
+  CPPUNIT_TEST(testMP4_3);
   CPPUNIT_TEST(testTrueAudio);
   CPPUNIT_TEST_SUITE_END();
 
@@ -71,6 +75,11 @@ public:
     fileRefSave("click", ".mpc");
   }
 
+  void testASF()
+  {
+    fileRefSave("silence-1", ".wma");
+  }
+
   void testVorbis()
   {
     fileRefSave("empty", ".ogg");
@@ -96,6 +105,21 @@ public:
     fileRefSave("empty", ".tta");
   }
 
+  void testMP4_1()
+  {
+    fileRefSave("has-tags", ".m4a");
+  }
+
+  void testMP4_2()
+  {
+    fileRefSave("no-tags", ".m4a");
+  }
+
+  void testMP4_3()
+  {
+    fileRefSave("no-tags", ".3g2");
+  }
+
 };
 
 CPPUNIT_TEST_SUITE_REGISTRATION(TestFileRef);
diff --git a/tests/test_mp4.cpp b/tests/test_mp4.cpp
new file mode 100644 (file)
index 0000000..2bca203
--- /dev/null
@@ -0,0 +1,103 @@
+#include <cppunit/extensions/HelperMacros.h>
+#include <string>
+#include <stdio.h>
+#include <tag.h>
+#include <mp4tag.h>
+#include <tbytevectorlist.h>
+#include <mp4atom.h>
+#include <mp4file.h>
+#include "utils.h"
+
+using namespace std;
+using namespace TagLib;
+
+class TestMP4 : public CppUnit::TestFixture
+{
+  CPPUNIT_TEST_SUITE(TestMP4);
+  CPPUNIT_TEST(testProperties);
+  CPPUNIT_TEST(testFreeForm);
+  CPPUNIT_TEST(testUpdateStco);
+  CPPUNIT_TEST_SUITE_END();
+
+public:
+
+  void testProperties()
+  {
+    MP4::File f("data/has-tags.m4a");
+    CPPUNIT_ASSERT_EQUAL(3, f.audioProperties()->length());
+    CPPUNIT_ASSERT_EQUAL(3, f.audioProperties()->bitrate());
+    CPPUNIT_ASSERT_EQUAL(2, f.audioProperties()->channels());
+    CPPUNIT_ASSERT_EQUAL(44100, f.audioProperties()->sampleRate());
+    CPPUNIT_ASSERT_EQUAL(16, ((MP4::Properties *)f.audioProperties())->bitsPerSample());
+  }
+
+  void testUpdateStco()
+  {
+    string filename = copyFile("no-tags", ".3g2");
+
+    MP4::File *f = new MP4::File(filename.c_str());
+    f->tag()->setArtist(ByteVector(3000, 'x'));
+
+    ByteVectorList data1;
+    {
+      MP4::Atoms a(f);
+      MP4::Atom *stco = a.find("moov")->findall("stco", true)[0];
+      f->seek(stco->offset + 12);
+      ByteVector data = f->readBlock(stco->length - 12);
+      unsigned int count = data.mid(0, 4).toUInt();
+      int pos = 4;
+      while (count--) {
+        unsigned int offset = data.mid(pos, 4).toUInt();
+        f->seek(offset);
+        data1.append(f->readBlock(20));
+        pos += 4;
+      }
+    }
+
+    f->save();
+    delete f;
+    f = new MP4::File(filename.c_str());
+
+    {
+      MP4::Atoms a(f);
+      MP4::Atom *stco = a.find("moov")->findall("stco", true)[0];
+      f->seek(stco->offset + 12);
+      ByteVector data = f->readBlock(stco->length - 12);
+      unsigned int count = data.mid(0, 4).toUInt();
+      int pos = 4, i = 0;
+      while (count--) {
+        unsigned int offset = data.mid(pos, 4).toUInt();
+        f->seek(offset);
+        CPPUNIT_ASSERT_EQUAL(data1[i], f->readBlock(20));
+        pos += 4;
+        i++;
+      }
+    }
+
+    delete f;
+
+    deleteFile(filename);
+  }
+
+  void testFreeForm()
+  {
+    string filename = copyFile("has-tags", ".m4a");
+
+    MP4::File *f = new MP4::File(filename.c_str());
+    CPPUNIT_ASSERT(f->tag()->itemListMap().contains("----:com.apple.iTunes:iTunNORM"));
+    f->tag()->itemListMap()["----:org.kde.TagLib:Foo"] = StringList("Bar");
+    f->save();
+    delete f;
+
+    f = new MP4::File(filename.c_str());
+    CPPUNIT_ASSERT(f->tag()->itemListMap().contains("----:org.kde.TagLib:Foo"));
+    CPPUNIT_ASSERT_EQUAL(String("Bar"), f->tag()->itemListMap()["----:org.kde.TagLib:Foo"].toStringList()[0]);
+    f->save();
+    delete f;
+
+    deleteFile(filename);
+  }
+
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(TestMP4);