mirror of
https://github.com/taglib/taglib.git
synced 2025-05-27 21:20:26 -04:00
Add support for ID3v2 ETCO frames (event timing codes).
This commit is contained in:
parent
eba99c3a70
commit
3b60af2c0b
@ -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
|
||||
|
144
taglib/mpeg/id3v2/frames/eventtimingcodesframe.cpp
Normal file
144
taglib/mpeg/id3v2/frames/eventtimingcodesframe.cpp
Normal file
@ -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));
|
||||
}
|
185
taglib/mpeg/id3v2/frames/eventtimingcodesframe.h
Normal file
185
taglib/mpeg/id3v2/frames/eventtimingcodesframe.h
Normal file
@ -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
|
@ -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")
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user