mpeg/id3v2/frames/popularimeterframe.h
mpeg/id3v2/frames/privateframe.h
mpeg/id3v2/frames/relativevolumeframe.h
+ mpeg/id3v2/frames/synchronizedlyricsframe.h
mpeg/id3v2/frames/textidentificationframe.h
mpeg/id3v2/frames/uniquefileidentifierframe.h
mpeg/id3v2/frames/unknownframe.h
mpeg/id3v2/frames/popularimeterframe.cpp
mpeg/id3v2/frames/privateframe.cpp
mpeg/id3v2/frames/relativevolumeframe.cpp
+ mpeg/id3v2/frames/synchronizedlyricsframe.cpp
mpeg/id3v2/frames/textidentificationframe.cpp
mpeg/id3v2/frames/uniquefileidentifierframe.cpp
mpeg/id3v2/frames/unknownframe.cpp
--- /dev/null
+/***************************************************************************
+ 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 "synchronizedlyricsframe.h"
+#include <tbytevectorlist.h>
+#include <id3v2tag.h>
+#include <tdebug.h>
+#include <tpropertymap.h>
+
+using namespace TagLib;
+using namespace ID3v2;
+
+class SynchronizedLyricsFrame::SynchronizedLyricsFramePrivate
+{
+public:
+ SynchronizedLyricsFramePrivate() :
+ textEncoding(String::Latin1),
+ timestampFormat(SynchronizedLyricsFrame::AbsoluteMilliseconds),
+ type(SynchronizedLyricsFrame::Lyrics) {}
+ String::Type textEncoding;
+ ByteVector language;
+ SynchronizedLyricsFrame::TimestampFormat timestampFormat;
+ SynchronizedLyricsFrame::Type type;
+ String description;
+ SynchronizedLyricsFrame::SynchedTextList synchedText;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// public members
+////////////////////////////////////////////////////////////////////////////////
+
+SynchronizedLyricsFrame::SynchronizedLyricsFrame(String::Type encoding) :
+ Frame("SYLT")
+{
+ d = new SynchronizedLyricsFramePrivate;
+ d->textEncoding = encoding;
+}
+
+SynchronizedLyricsFrame::SynchronizedLyricsFrame(const ByteVector &data) :
+ Frame(data)
+{
+ d = new SynchronizedLyricsFramePrivate;
+ setData(data);
+}
+
+SynchronizedLyricsFrame::~SynchronizedLyricsFrame()
+{
+ delete d;
+}
+
+String SynchronizedLyricsFrame::toString() const
+{
+ return d->description;
+}
+
+String::Type SynchronizedLyricsFrame::textEncoding() const
+{
+ return d->textEncoding;
+}
+
+ByteVector SynchronizedLyricsFrame::language() const
+{
+ return d->language;
+}
+
+SynchronizedLyricsFrame::TimestampFormat
+SynchronizedLyricsFrame::timestampFormat() const
+{
+ return d->timestampFormat;
+}
+
+SynchronizedLyricsFrame::Type SynchronizedLyricsFrame::type() const
+{
+ return d->type;
+}
+
+String SynchronizedLyricsFrame::description() const
+{
+ return d->description;
+}
+
+SynchronizedLyricsFrame::SynchedTextList
+SynchronizedLyricsFrame::synchedText() const
+{
+ return d->synchedText;
+}
+
+void SynchronizedLyricsFrame::setTextEncoding(String::Type encoding)
+{
+ d->textEncoding = encoding;
+}
+
+void SynchronizedLyricsFrame::setLanguage(const ByteVector &languageEncoding)
+{
+ d->language = languageEncoding.mid(0, 3);
+}
+
+void SynchronizedLyricsFrame::setTimestampFormat(
+ SynchronizedLyricsFrame::TimestampFormat f)
+{
+ d->timestampFormat = f;
+}
+
+void SynchronizedLyricsFrame::setType(SynchronizedLyricsFrame::Type t)
+{
+ d->type = t;
+}
+
+void SynchronizedLyricsFrame::setDescription(const String &s)
+{
+ d->description = s;
+}
+
+void SynchronizedLyricsFrame::setSynchedText(
+ const SynchronizedLyricsFrame::SynchedTextList &t)
+{
+ d->synchedText = t;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// protected members
+////////////////////////////////////////////////////////////////////////////////
+
+void SynchronizedLyricsFrame::parseFields(const ByteVector &data)
+{
+ const int end = data.size();
+ if(end < 7) {
+ debug("A synchronized lyrics frame must contain at least 7 bytes.");
+ return;
+ }
+
+ d->textEncoding = String::Type(data[0]);
+ d->language = data.mid(1, 3);
+ d->timestampFormat = TimestampFormat(data[4]);
+ d->type = Type(data[5]);
+
+ int pos = 6;
+
+ d->description = readStringField(data, d->textEncoding, &pos);
+ if(d->description.isNull())
+ return;
+
+ /*
+ * If UTF16 strings are found in SYLT frames, a BOM may only be
+ * present in the first string (content descriptor), and the strings of
+ * the synchronized text have no BOM. Here the BOM is read from
+ * the first string to have a specific encoding with endianness for the
+ * case of strings without BOM so that readStringField() will work.
+ */
+ String::Type encWithEndianness = d->textEncoding;
+ if(d->textEncoding == String::UTF16) {
+ ushort bom = data.toUShort(6, true);
+ if(bom == 0xfffe) {
+ encWithEndianness = String::UTF16LE;
+ } else if(bom == 0xfeff) {
+ encWithEndianness = String::UTF16BE;
+ }
+ }
+
+ d->synchedText.clear();
+ while(pos < end) {
+ String::Type enc = d->textEncoding;
+ // If a UTF16 string has no BOM, use the encoding found above.
+ if(enc == String::UTF16 && pos + 1 < end) {
+ ushort bom = data.toUShort(pos, true);
+ if(bom != 0xfffe && bom != 0xfeff) {
+ enc = encWithEndianness;
+ }
+ }
+ String text = readStringField(data, enc, &pos);
+ if(text.isNull() || pos + 4 > end)
+ return;
+
+ uint time = data.mid(pos, 4).toUInt(true);
+ pos += 4;
+
+ d->synchedText.append(SynchedText(time, text));
+ }
+}
+
+ByteVector SynchronizedLyricsFrame::renderFields() const
+{
+ ByteVector v;
+
+ String::Type encoding = d->textEncoding;
+
+ encoding = checkTextEncoding(d->description, encoding);
+ for(SynchedTextList::ConstIterator it = d->synchedText.begin();
+ it != d->synchedText.end();
+ ++it) {
+ encoding = checkTextEncoding(it->text, encoding);
+ }
+
+ v.append(char(encoding));
+ v.append(d->language.size() == 3 ? d->language : "XXX");
+ v.append(char(d->timestampFormat));
+ v.append(char(d->type));
+ v.append(d->description.data(encoding));
+ v.append(textDelimiter(encoding));
+ for(SynchedTextList::ConstIterator it = d->synchedText.begin();
+ it != d->synchedText.end();
+ ++it) {
+ const SynchedText &entry = *it;
+ v.append(entry.text.data(encoding));
+ v.append(textDelimiter(encoding));
+ v.append(ByteVector::fromUInt(entry.time));
+ }
+
+ return v;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// private members
+////////////////////////////////////////////////////////////////////////////////
+
+SynchronizedLyricsFrame::SynchronizedLyricsFrame(const ByteVector &data, Header *h)
+ : Frame(h)
+{
+ d = new SynchronizedLyricsFramePrivate();
+ parseFields(fieldData(data));
+}
--- /dev/null
+/***************************************************************************
+ 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_SYNCHRONIZEDLYRICSFRAME_H
+#define TAGLIB_SYNCHRONIZEDLYRICSFRAME_H
+
+#include "id3v2frame.h"
+#include "tlist.h"
+
+namespace TagLib {
+
+ namespace ID3v2 {
+
+ //! ID3v2 synchronized lyrics frame
+ /*!
+ * An implementation of ID3v2 synchronized lyrics.
+ */
+ class TAGLIB_EXPORT SynchronizedLyricsFrame : 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
+ };
+
+ /*!
+ * Specifies the type of text contained.
+ */
+ enum Type {
+ //! The text is some other type of text.
+ Other = 0x00,
+ //! The text contains lyrical data.
+ Lyrics = 0x01,
+ //! The text contains a transcription.
+ TextTranscription = 0x02,
+ //! The text lists the movements in the piece.
+ Movement = 0x03,
+ //! The text describes events that occur.
+ Events = 0x04,
+ //! The text contains chord changes that occur in the music.
+ Chord = 0x05,
+ //! The text contains trivia or "pop up" information about the media.
+ Trivia = 0x06,
+ //! The text contains URLs for relevant webpages.
+ WebpageUrls = 0x07,
+ //! The text contains URLs for relevant images.
+ ImageUrls = 0x08
+ };
+
+ /*!
+ * Single entry of time stamp and lyrics text.
+ */
+ struct SynchedText {
+ SynchedText(uint ms, String str) : time(ms), text(str) {}
+ uint time;
+ String text;
+ };
+
+ /*!
+ * List of synchronized lyrics.
+ */
+ typedef TagLib::List<SynchedText> SynchedTextList;
+
+ /*!
+ * Construct an empty synchronized lyrics frame that will use the text
+ * encoding \a encoding.
+ */
+ explicit SynchronizedLyricsFrame(String::Type encoding = String::Latin1);
+
+ /*!
+ * Construct a synchronized lyrics frame based on the data in \a data.
+ */
+ explicit SynchronizedLyricsFrame(const ByteVector &data);
+
+ /*!
+ * Destroys this SynchronizedLyricsFrame instance.
+ */
+ virtual ~SynchronizedLyricsFrame();
+
+ /*!
+ * Returns the description of this synchronized lyrics frame.
+ *
+ * \see description()
+ */
+ virtual String toString() const;
+
+ /*!
+ * Returns the text encoding that will be used in rendering this frame.
+ * This defaults to the type that was either specified in the constructor
+ * or read from the frame when parsed.
+ *
+ * \see setTextEncoding()
+ * \see render()
+ */
+ String::Type textEncoding() const;
+
+ /*!
+ * Returns the language encoding as a 3 byte encoding as specified by
+ * <a href="http://en.wikipedia.org/wiki/ISO_639">ISO-639-2</a>.
+ *
+ * \note Most taggers simply ignore this value.
+ *
+ * \see setLanguage()
+ */
+ ByteVector language() const;
+
+ /*!
+ * Returns the timestamp format.
+ */
+ TimestampFormat timestampFormat() const;
+
+ /*!
+ * Returns the type of text contained.
+ */
+ Type type() const;
+
+ /*!
+ * Returns the description of this synchronized lyrics frame.
+ *
+ * \note Most taggers simply ignore this value.
+ *
+ * \see setDescription()
+ */
+ String description() const;
+
+ /*!
+ * Returns the text with the time stamps.
+ */
+ SynchedTextList synchedText() const;
+
+ /*!
+ * Sets the text encoding to be used when rendering this frame to
+ * \a encoding.
+ *
+ * \see textEncoding()
+ * \see render()
+ */
+ void setTextEncoding(String::Type encoding);
+
+ /*!
+ * Set the language using the 3 byte language code from
+ * <a href="http://en.wikipedia.org/wiki/ISO_639">ISO-639-2</a> to
+ * \a languageCode.
+ *
+ * \see language()
+ */
+ void setLanguage(const ByteVector &languageCode);
+
+ /*!
+ * Set the timestamp format.
+ *
+ * \see timestampFormat()
+ */
+ void setTimestampFormat(TimestampFormat f);
+
+ /*!
+ * Set the type of text contained.
+ *
+ * \see type()
+ */
+ void setType(Type t);
+
+ /*!
+ * Sets the description of the synchronized lyrics frame to \a s.
+ *
+ * \see decription()
+ */
+ void setDescription(const String &s);
+
+ /*!
+ * Sets the text with the time stamps.
+ *
+ * \see text()
+ */
+ void setSynchedText(const SynchedTextList &t);
+
+ protected:
+ // Reimplementations.
+
+ virtual void parseFields(const ByteVector &data);
+ virtual ByteVector renderFields() const;
+
+ private:
+ /*!
+ * The constructor used by the FrameFactory.
+ */
+ SynchronizedLyricsFrame(const ByteVector &data, Header *h);
+ SynchronizedLyricsFrame(const SynchronizedLyricsFrame &);
+ SynchronizedLyricsFrame &operator=(const SynchronizedLyricsFrame &);
+
+ class SynchronizedLyricsFramePrivate;
+ SynchronizedLyricsFramePrivate *d;
+ };
+
+ }
+}
+#endif
#include "frames/popularimeterframe.h"
#include "frames/privateframe.h"
#include "frames/ownershipframe.h"
+#include "frames/synchronizedlyricsframe.h"
using namespace TagLib;
using namespace ID3v2;
return f;
}
+ // Synchronised lyrics/text (frames 4.9)
+
+ if(frameID == "SYLT") {
+ SynchronizedLyricsFrame *f = new SynchronizedLyricsFrame(data, header);
+ if(d->useDefaultEncoding)
+ f->setTextEncoding(d->defaultEncoding);
+ return f;
+ }
+
// Popularimeter (frames 4.17)
if(frameID == "POPM")
#include <textidentificationframe.h>
#include <attachedpictureframe.h>
#include <unsynchronizedlyricsframe.h>
+#include <synchronizedlyricsframe.h>
#include <generalencapsulatedobjectframe.h>
#include <relativevolumeframe.h>
#include <popularimeterframe.h>
CPPUNIT_TEST(testRenderUserUrlLinkFrame);
CPPUNIT_TEST(testParseOwnershipFrame);
CPPUNIT_TEST(testRenderOwnershipFrame);
+ CPPUNIT_TEST(testParseSynchronizedLyricsFrame);
+ CPPUNIT_TEST(testRenderSynchronizedLyricsFrame);
CPPUNIT_TEST(testSaveUTF16Comment);
CPPUNIT_TEST(testUpdateGenre23_1);
CPPUNIT_TEST(testUpdateGenre23_2);
f.render());
}
+ void testParseSynchronizedLyricsFrame()
+ {
+ ID3v2::SynchronizedLyricsFrame f(
+ ByteVector("SYLT" // Frame ID
+ "\x00\x00\x00\x21" // Frame size
+ "\x00\x00" // Frame flags
+ "\x00" // Text encoding
+ "eng" // Language
+ "\x02" // Time stamp format
+ "\x01" // Content type
+ "foo\x00" // Content descriptor
+ "Example\x00" // 1st text
+ "\x00\x00\x04\xd2" // 1st time stamp
+ "Lyrics\x00" // 2nd text
+ "\x00\x00\x11\xd7", 43)); // 2nd time stamp
+ CPPUNIT_ASSERT_EQUAL(String::Latin1, f.textEncoding());
+ CPPUNIT_ASSERT_EQUAL(ByteVector("eng", 3), f.language());
+ CPPUNIT_ASSERT_EQUAL(ID3v2::SynchronizedLyricsFrame::AbsoluteMilliseconds,
+ f.timestampFormat());
+ CPPUNIT_ASSERT_EQUAL(ID3v2::SynchronizedLyricsFrame::Lyrics, f.type());
+ CPPUNIT_ASSERT_EQUAL(String("foo"), f.description());
+ ID3v2::SynchronizedLyricsFrame::SynchedTextList stl = f.synchedText();
+ CPPUNIT_ASSERT_EQUAL(TagLib::uint(2), stl.size());
+ CPPUNIT_ASSERT_EQUAL(String("Example"), stl[0].text);
+ CPPUNIT_ASSERT_EQUAL(TagLib::uint(1234), stl[0].time);
+ CPPUNIT_ASSERT_EQUAL(String("Lyrics"), stl[1].text);
+ CPPUNIT_ASSERT_EQUAL(TagLib::uint(4567), stl[1].time);
+ }
+
+ void testRenderSynchronizedLyricsFrame()
+ {
+ ID3v2::SynchronizedLyricsFrame f;
+ f.setTextEncoding(String::Latin1);
+ f.setLanguage(ByteVector("eng", 3));
+ f.setTimestampFormat(ID3v2::SynchronizedLyricsFrame::AbsoluteMilliseconds);
+ f.setType(ID3v2::SynchronizedLyricsFrame::Lyrics);
+ f.setDescription("foo");
+ ID3v2::SynchronizedLyricsFrame::SynchedTextList stl;
+ stl.append(ID3v2::SynchronizedLyricsFrame::SynchedText(1234, "Example"));
+ stl.append(ID3v2::SynchronizedLyricsFrame::SynchedText(4567, "Lyrics"));
+ f.setSynchedText(stl);
+ CPPUNIT_ASSERT_EQUAL(
+ ByteVector("SYLT" // Frame ID
+ "\x00\x00\x00\x21" // Frame size
+ "\x00\x00" // Frame flags
+ "\x00" // Text encoding
+ "eng" // Language
+ "\x02" // Time stamp format
+ "\x01" // Content type
+ "foo\x00" // Content descriptor
+ "Example\x00" // 1st text
+ "\x00\x00\x04\xd2" // 1st time stamp
+ "Lyrics\x00" // 2nd text
+ "\x00\x00\x11\xd7", 43), // 2nd time stamp
+ f.render());
+ }
+
void testItunes24FrameSize()
{
MPEG::File f(TEST_FILE_PATH_C("005411.id3"), false);