]> granicus.if.org Git - taglib/commitdiff
Add support for ID3v2 ETCO frames (event timing codes).
authorUrs Fleisch <ufleisch@users.sourceforge.net>
Sun, 30 Mar 2014 07:27:23 +0000 (09:27 +0200)
committerUrs Fleisch <ufleisch@users.sourceforge.net>
Sun, 30 Mar 2014 07:28:14 +0000 (09:28 +0200)
taglib/CMakeLists.txt
taglib/mpeg/id3v2/frames/eventtimingcodesframe.cpp [new file with mode: 0644]
taglib/mpeg/id3v2/frames/eventtimingcodesframe.h [new file with mode: 0644]
taglib/mpeg/id3v2/id3v2framefactory.cpp
tests/test_id3v2.cpp

index e61bba0156cb283e386b22729600561ae59d2791..89cb8e128ad399d908bd0a6cc517e4b2942d0ed5 100644 (file)
@@ -67,6 +67,7 @@ set(tag_HDRS
   mpeg/id3v2/id3v2tag.h
   mpeg/id3v2/frames/attachedpictureframe.h
   mpeg/id3v2/frames/commentsframe.h
+  mpeg/id3v2/frames/eventtimingcodesframe.h
   mpeg/id3v2/frames/generalencapsulatedobjectframe.h
   mpeg/id3v2/frames/ownershipframe.h
   mpeg/id3v2/frames/popularimeterframe.h
@@ -158,6 +159,7 @@ set(id3v2_SRCS
 set(frames_SRCS
   mpeg/id3v2/frames/attachedpictureframe.cpp
   mpeg/id3v2/frames/commentsframe.cpp
+  mpeg/id3v2/frames/eventtimingcodesframe.cpp
   mpeg/id3v2/frames/generalencapsulatedobjectframe.cpp
   mpeg/id3v2/frames/ownershipframe.cpp
   mpeg/id3v2/frames/popularimeterframe.cpp
diff --git a/taglib/mpeg/id3v2/frames/eventtimingcodesframe.cpp b/taglib/mpeg/id3v2/frames/eventtimingcodesframe.cpp
new file mode 100644 (file)
index 0000000..70214ba
--- /dev/null
@@ -0,0 +1,144 @@
+/***************************************************************************
+    copyright            : (C) 2014 by Urs Fleisch
+    email                : ufleisch@users.sourceforge.net
+ ***************************************************************************/
+
+/***************************************************************************
+ *   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., 51 Franklin Street, Fifth Floor, Boston, MA         *
+ *   02110-1301  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/                                           *
+ ***************************************************************************/
+
+#include "eventtimingcodesframe.h"
+#include <tbytevectorlist.h>
+#include <id3v2tag.h>
+#include <tdebug.h>
+#include <tpropertymap.h>
+
+using namespace TagLib;
+using namespace ID3v2;
+
+class EventTimingCodesFrame::EventTimingCodesFramePrivate
+{
+public:
+  EventTimingCodesFramePrivate() :
+    timestampFormat(EventTimingCodesFrame::AbsoluteMilliseconds) {}
+  EventTimingCodesFrame::TimestampFormat timestampFormat;
+  EventTimingCodesFrame::SynchedEventList synchedEvents;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// public members
+////////////////////////////////////////////////////////////////////////////////
+
+EventTimingCodesFrame::EventTimingCodesFrame() :
+  Frame("ETCO")
+{
+  d = new EventTimingCodesFramePrivate;
+}
+
+EventTimingCodesFrame::EventTimingCodesFrame(const ByteVector &data) :
+  Frame(data)
+{
+  d = new EventTimingCodesFramePrivate;
+  setData(data);
+}
+
+EventTimingCodesFrame::~EventTimingCodesFrame()
+{
+  delete d;
+}
+
+String EventTimingCodesFrame::toString() const
+{
+  return String();
+}
+
+EventTimingCodesFrame::TimestampFormat
+EventTimingCodesFrame::timestampFormat() const
+{
+  return d->timestampFormat;
+}
+
+EventTimingCodesFrame::SynchedEventList
+EventTimingCodesFrame::synchedEvents() const
+{
+  return d->synchedEvents;
+}
+
+void EventTimingCodesFrame::setTimestampFormat(
+    EventTimingCodesFrame::TimestampFormat f)
+{
+  d->timestampFormat = f;
+}
+
+void EventTimingCodesFrame::setSynchedEvents(
+    const EventTimingCodesFrame::SynchedEventList &e)
+{
+  d->synchedEvents = e;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// protected members
+////////////////////////////////////////////////////////////////////////////////
+
+void EventTimingCodesFrame::parseFields(const ByteVector &data)
+{
+  const int end = data.size();
+  if(end < 1) {
+    debug("An event timing codes frame must contain at least 1 byte.");
+    return;
+  }
+
+  d->timestampFormat = TimestampFormat(data[0]);
+
+  int pos = 1;
+  d->synchedEvents.clear();
+  while(pos + 4 < end) {
+    EventType type = EventType(uchar(data[pos++]));
+    uint time = data.toUInt(pos, true);
+    pos += 4;
+    d->synchedEvents.append(SynchedEvent(time, type));
+  }
+}
+
+ByteVector EventTimingCodesFrame::renderFields() const
+{
+  ByteVector v;
+
+  v.append(char(d->timestampFormat));
+  for(SynchedEventList::ConstIterator it = d->synchedEvents.begin();
+      it != d->synchedEvents.end();
+      ++it) {
+    const SynchedEvent &entry = *it;
+    v.append(char(entry.type));
+    v.append(ByteVector::fromUInt(entry.time));
+  }
+
+  return v;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// private members
+////////////////////////////////////////////////////////////////////////////////
+
+EventTimingCodesFrame::EventTimingCodesFrame(const ByteVector &data, Header *h)
+  : Frame(h)
+{
+  d = new EventTimingCodesFramePrivate();
+  parseFields(fieldData(data));
+}
diff --git a/taglib/mpeg/id3v2/frames/eventtimingcodesframe.h b/taglib/mpeg/id3v2/frames/eventtimingcodesframe.h
new file mode 100644 (file)
index 0000000..0719f51
--- /dev/null
@@ -0,0 +1,185 @@
+/***************************************************************************
+    copyright            : (C) 2014 by Urs Fleisch
+    email                : ufleisch@users.sourceforge.net
+ ***************************************************************************/
+
+/***************************************************************************
+ *   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., 51 Franklin Street, Fifth Floor, Boston, MA         *
+ *   02110-1301  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_EVENTTIMINGCODESFRAME_H
+#define TAGLIB_EVENTTIMINGCODESFRAME_H
+
+#include "id3v2frame.h"
+#include "tlist.h"
+
+namespace TagLib {
+
+  namespace ID3v2 {
+
+    //! ID3v2 event timing codes frame
+    /*!
+     * An implementation of ID3v2 event timing codes.
+     */
+    class TAGLIB_EXPORT EventTimingCodesFrame : public Frame
+    {
+      friend class FrameFactory;
+
+    public:
+
+      /*!
+       * Specifies the timestamp format used.
+       */
+      enum TimestampFormat {
+        //! The timestamp is of unknown format.
+        Unknown              = 0x00,
+        //! The timestamp represents the number of MPEG frames since
+        //! the beginning of the audio stream.
+        AbsoluteMpegFrames   = 0x01,
+        //! The timestamp represents the number of milliseconds since
+        //! the beginning of the audio stream.
+        AbsoluteMilliseconds = 0x02
+      };
+
+      /*!
+       * Event types defined in id3v2.4.0-frames.txt 4.5. Event timing codes.
+       */
+      enum EventType {
+        Padding                = 0x00,
+        EndOfInitialSilence    = 0x01,
+        IntroStart             = 0x02,
+        MainPartStart          = 0x03,
+        OutroStart             = 0x04,
+        OutroEnd               = 0x05,
+        VerseStart             = 0x06,
+        RefrainStart           = 0x07,
+        InterludeStart         = 0x08,
+        ThemeStart             = 0x09,
+        VariationStart         = 0x0a,
+        KeyChange              = 0x0b,
+        TimeChange             = 0x0c,
+        MomentaryUnwantedNoise = 0x0d,
+        SustainedNoise         = 0x0e,
+        SustainedNoiseEnd      = 0x0f,
+        IntroEnd               = 0x10,
+        MainPartEnd            = 0x11,
+        VerseEnd               = 0x12,
+        RefrainEnd             = 0x13,
+        ThemeEnd               = 0x14,
+        Profanity              = 0x15,
+        ProfanityEnd           = 0x16,
+        NotPredefinedSynch0    = 0xe0,
+        NotPredefinedSynch1    = 0xe1,
+        NotPredefinedSynch2    = 0xe2,
+        NotPredefinedSynch3    = 0xe3,
+        NotPredefinedSynch4    = 0xe4,
+        NotPredefinedSynch5    = 0xe5,
+        NotPredefinedSynch6    = 0xe6,
+        NotPredefinedSynch7    = 0xe7,
+        NotPredefinedSynch8    = 0xe8,
+        NotPredefinedSynch9    = 0xe9,
+        NotPredefinedSynchA    = 0xea,
+        NotPredefinedSynchB    = 0xeb,
+        NotPredefinedSynchC    = 0xec,
+        NotPredefinedSynchD    = 0xed,
+        NotPredefinedSynchE    = 0xee,
+        NotPredefinedSynchF    = 0xef,
+        AudioEnd               = 0xfd,
+        AudioFileEnds          = 0xfe
+      };
+
+      /*!
+       * Single entry of time stamp and event.
+       */
+      struct SynchedEvent {
+        SynchedEvent(uint ms, EventType t) : time(ms), type(t) {}
+        uint time;
+        EventType type;
+      };
+
+      /*!
+       * List of synchronized events.
+       */
+      typedef TagLib::List<SynchedEvent> SynchedEventList;
+
+      /*!
+       * Construct an empty event timing codes frame.
+       */
+      explicit EventTimingCodesFrame();
+
+      /*!
+       * Construct a event timing codes frame based on the data in \a data.
+       */
+      explicit EventTimingCodesFrame(const ByteVector &data);
+
+      /*!
+       * Destroys this EventTimingCodesFrame instance.
+       */
+      virtual ~EventTimingCodesFrame();
+
+      /*!
+       * Returns a null string.
+       */
+      virtual String toString() const;
+
+      /*!
+       * Returns the timestamp format.
+       */
+      TimestampFormat timestampFormat() const;
+
+      /*!
+       * Returns the events with the time stamps.
+       */
+      SynchedEventList synchedEvents() const;
+
+      /*!
+       * Set the timestamp format.
+       *
+       * \see timestampFormat()
+       */
+      void setTimestampFormat(TimestampFormat f);
+
+      /*!
+       * Sets the text with the time stamps.
+       *
+       * \see text()
+       */
+      void setSynchedEvents(const SynchedEventList &e);
+
+    protected:
+      // Reimplementations.
+
+      virtual void parseFields(const ByteVector &data);
+      virtual ByteVector renderFields() const;
+
+    private:
+      /*!
+       * The constructor used by the FrameFactory.
+       */
+      EventTimingCodesFrame(const ByteVector &data, Header *h);
+      EventTimingCodesFrame(const EventTimingCodesFrame &);
+      EventTimingCodesFrame &operator=(const EventTimingCodesFrame &);
+
+      class EventTimingCodesFramePrivate;
+      EventTimingCodesFramePrivate *d;
+    };
+
+  }
+}
+#endif
index 74d4f382192a5a8d833934fae93a348a42af5b09..b2c32ce4611bd2cb56613b313a2bb0a879942f81 100644 (file)
@@ -46,6 +46,7 @@
 #include "frames/privateframe.h"
 #include "frames/ownershipframe.h"
 #include "frames/synchronizedlyricsframe.h"
+#include "frames/eventtimingcodesframe.h"
 
 using namespace TagLib;
 using namespace ID3v2;
@@ -251,6 +252,11 @@ Frame *FrameFactory::createFrame(const ByteVector &origData, Header *tagHeader)
     return f;
   }
 
+  // Event timing codes (frames 4.5)
+
+  if(frameID == "ETCO")
+    return new EventTimingCodesFrame(data, header);
+
   // Popularimeter (frames 4.17)
 
   if(frameID == "POPM")
index da74b96040ff82c5eabce9fbf0e9e59aa221f1f8..fadcae2fcefe57de3ac2a3f9fcb5a9fbe22de98f 100644 (file)
@@ -15,6 +15,7 @@
 #include <attachedpictureframe.h>
 #include <unsynchronizedlyricsframe.h>
 #include <synchronizedlyricsframe.h>
+#include <eventtimingcodesframe.h>
 #include <generalencapsulatedobjectframe.h>
 #include <relativevolumeframe.h>
 #include <popularimeterframe.h>
@@ -72,6 +73,8 @@ class TestID3v2 : public CppUnit::TestFixture
   CPPUNIT_TEST(testRenderOwnershipFrame);
   CPPUNIT_TEST(testParseSynchronizedLyricsFrame);
   CPPUNIT_TEST(testRenderSynchronizedLyricsFrame);
+  CPPUNIT_TEST(testParseEventTimingCodesFrame);
+  CPPUNIT_TEST(testRenderEventTimingCodesFrame);
   CPPUNIT_TEST(testSaveUTF16Comment);
   CPPUNIT_TEST(testUpdateGenre23_1);
   CPPUNIT_TEST(testUpdateGenre23_2);
@@ -487,6 +490,47 @@ public:
       f.render());
   }
 
+  void testParseEventTimingCodesFrame()
+  {
+    ID3v2::EventTimingCodesFrame f(
+      ByteVector("ETCO"                      // Frame ID
+                 "\x00\x00\x00\x0b"          // Frame size
+                 "\x00\x00"                  // Frame flags
+                 "\x02"                      // Time stamp format
+                 "\x02"                      // 1st event
+                 "\x00\x00\xf3\x5c"          // 1st time stamp
+                 "\xfe"                      // 2nd event
+                 "\x00\x36\xee\x80", 21));   // 2nd time stamp
+    CPPUNIT_ASSERT_EQUAL(ID3v2::EventTimingCodesFrame::AbsoluteMilliseconds,
+                         f.timestampFormat());
+    ID3v2::EventTimingCodesFrame::SynchedEventList sel = f.synchedEvents();
+    CPPUNIT_ASSERT_EQUAL(TagLib::uint(2), sel.size());
+    CPPUNIT_ASSERT_EQUAL(ID3v2::EventTimingCodesFrame::IntroStart, sel[0].type);
+    CPPUNIT_ASSERT_EQUAL(TagLib::uint(62300), sel[0].time);
+    CPPUNIT_ASSERT_EQUAL(ID3v2::EventTimingCodesFrame::AudioFileEnds, sel[1].type);
+    CPPUNIT_ASSERT_EQUAL(TagLib::uint(3600000), sel[1].time);
+  }
+
+  void testRenderEventTimingCodesFrame()
+  {
+    ID3v2::EventTimingCodesFrame f;
+    f.setTimestampFormat(ID3v2::EventTimingCodesFrame::AbsoluteMilliseconds);
+    ID3v2::EventTimingCodesFrame::SynchedEventList sel;
+    sel.append(ID3v2::EventTimingCodesFrame::SynchedEvent(62300, ID3v2::EventTimingCodesFrame::IntroStart));
+    sel.append(ID3v2::EventTimingCodesFrame::SynchedEvent(3600000, ID3v2::EventTimingCodesFrame::AudioFileEnds));
+    f.setSynchedEvents(sel);
+    CPPUNIT_ASSERT_EQUAL(
+      ByteVector("ETCO"                      // Frame ID
+                 "\x00\x00\x00\x0b"          // Frame size
+                 "\x00\x00"                  // Frame flags
+                 "\x02"                      // Time stamp format
+                 "\x02"                      // 1st event
+                 "\x00\x00\xf3\x5c"          // 1st time stamp
+                 "\xfe"                      // 2nd event
+                 "\x00\x36\xee\x80", 21),    // 2nd time stamp
+      f.render());
+  }
+
   void testItunes24FrameSize()
   {
     MPEG::File f(TEST_FILE_PATH_C("005411.id3"), false);