File indexing completed on 2025-01-05 04:32:39

0001 /***************************************************************************
0002     copyright            : (C) 2014 by Urs Fleisch
0003     email                : ufleisch@users.sourceforge.net
0004  ***************************************************************************/
0005 
0006 /***************************************************************************
0007  *   This library is free software; you can redistribute it and/or modify  *
0008  *   it  under the terms of the GNU Lesser General Public License version  *
0009  *   2.1 as published by the Free Software Foundation.                     *
0010  *                                                                         *
0011  *   This library is distributed in the hope that it will be useful, but   *
0012  *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
0013  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
0014  *   Lesser General Public License for more details.                       *
0015  *                                                                         *
0016  *   You should have received a copy of the GNU Lesser General Public      *
0017  *   License along with this library; if not, write to the Free Software   *
0018  *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA         *
0019  *   02110-1301  USA                                                       *
0020  *                                                                         *
0021  *   Alternatively, this file is available under the Mozilla Public        *
0022  *   License Version 1.1.  You may obtain a copy of the License at         *
0023  *   http://www.mozilla.org/MPL/                                           *
0024  ***************************************************************************/
0025 
0026 #define BUILD_WITH_KID3
0027 #include "synchronizedlyricsframe.h"
0028 #include <tbytevectorlist.h>
0029 #include <id3v2tag.h>
0030 #ifdef BUILD_WITH_KID3
0031 #define debug(s)
0032 #else
0033 #include <tdebug.h>
0034 #endif
0035 #include <tstringlist.h>
0036 
0037 using namespace TagLib;
0038 using namespace ID3v2;
0039 
0040 class SynchronizedLyricsFrame::SynchronizedLyricsFramePrivate
0041 {
0042 public:
0043   SynchronizedLyricsFramePrivate() :
0044     textEncoding(String::Latin1),
0045     timestampFormat(SynchronizedLyricsFrame::AbsoluteMilliseconds),
0046     type(SynchronizedLyricsFrame::Lyrics) {}
0047   String::Type textEncoding;
0048   ByteVector language;
0049   SynchronizedLyricsFrame::TimestampFormat timestampFormat;
0050   SynchronizedLyricsFrame::Type type;
0051   String description;
0052   SynchronizedLyricsFrame::SynchedTextList synchedText;
0053 };
0054 
0055 ////////////////////////////////////////////////////////////////////////////////
0056 // public members
0057 ////////////////////////////////////////////////////////////////////////////////
0058 
0059 SynchronizedLyricsFrame::SynchronizedLyricsFrame(String::Type encoding) :
0060   Frame("SYLT")
0061 {
0062   d = new SynchronizedLyricsFramePrivate;
0063   d->textEncoding = encoding;
0064 }
0065 
0066 SynchronizedLyricsFrame::SynchronizedLyricsFrame(const ByteVector &data) :
0067   Frame(data)
0068 {
0069   d = new SynchronizedLyricsFramePrivate;
0070   setData(data);
0071 }
0072 
0073 SynchronizedLyricsFrame::~SynchronizedLyricsFrame()
0074 {
0075   delete d;
0076 }
0077 
0078 String SynchronizedLyricsFrame::toString() const
0079 {
0080   return d->description;
0081 }
0082 
0083 String::Type SynchronizedLyricsFrame::textEncoding() const
0084 {
0085   return d->textEncoding;
0086 }
0087 
0088 ByteVector SynchronizedLyricsFrame::language() const
0089 {
0090   return d->language;
0091 }
0092 
0093 SynchronizedLyricsFrame::TimestampFormat
0094 SynchronizedLyricsFrame::timestampFormat() const
0095 {
0096   return d->timestampFormat;
0097 }
0098 
0099 SynchronizedLyricsFrame::Type SynchronizedLyricsFrame::type() const
0100 {
0101   return d->type;
0102 }
0103 
0104 String SynchronizedLyricsFrame::description() const
0105 {
0106   return d->description;
0107 }
0108 
0109 SynchronizedLyricsFrame::SynchedTextList
0110 SynchronizedLyricsFrame::synchedText() const
0111 {
0112   return d->synchedText;
0113 }
0114 
0115 void SynchronizedLyricsFrame::setTextEncoding(String::Type encoding)
0116 {
0117   d->textEncoding = encoding;
0118 }
0119 
0120 void SynchronizedLyricsFrame::setLanguage(const ByteVector &languageEncoding)
0121 {
0122   d->language = languageEncoding.mid(0, 3);
0123 }
0124 
0125 void SynchronizedLyricsFrame::setTimestampFormat(
0126     SynchronizedLyricsFrame::TimestampFormat f)
0127 {
0128   d->timestampFormat = f;
0129 }
0130 
0131 void SynchronizedLyricsFrame::setType(SynchronizedLyricsFrame::Type t)
0132 {
0133   d->type = t;
0134 }
0135 
0136 void SynchronizedLyricsFrame::setDescription(const String &s)
0137 {
0138   d->description = s;
0139 }
0140 
0141 void SynchronizedLyricsFrame::setSynchedText(
0142     const SynchronizedLyricsFrame::SynchedTextList &t)
0143 {
0144   d->synchedText = t;
0145 }
0146 
0147 ////////////////////////////////////////////////////////////////////////////////
0148 // protected members
0149 ////////////////////////////////////////////////////////////////////////////////
0150 
0151 void SynchronizedLyricsFrame::parseFields(const ByteVector &data)
0152 {
0153   const int end = data.size();
0154   if(end < 7) {
0155     debug("A synchronized lyrics frame must contain at least 7 bytes.");
0156     return;
0157   }
0158 
0159   d->textEncoding = String::Type(data[0]);
0160   d->language = data.mid(1, 3);
0161   d->timestampFormat = TimestampFormat(data[4]);
0162   d->type = Type(data[5]);
0163 
0164   int pos = 6;
0165 
0166   d->description = readStringField(data, d->textEncoding, &pos);
0167   if(d->description.isNull())
0168     return;
0169 
0170   /*
0171    * If UTF16 strings are found in SYLT frames, a BOM may only be
0172    * present in the first string (content descriptor), and the strings of
0173    * the synchronized text have no BOM. Here the BOM is read from
0174    * the first string to have a specific encoding with endianness for the
0175    * case of strings without BOM so that readStringField() will work.
0176    */
0177   String::Type encWithEndianness = d->textEncoding;
0178   if(d->textEncoding == String::UTF16) {
0179 #if (((TAGLIB_MAJOR_VERSION) << 16) + ((TAGLIB_MINOR_VERSION) << 8) + (TAGLIB_PATCH_VERSION)) >= 0x010700
0180     ushort bom = data.mid(6, 2).toUShort(true);
0181 #else
0182     ushort bom = static_cast<ushort>(data.mid(6, 2).toShort(true));
0183 #endif
0184     if(bom == 0xfffe) {
0185       encWithEndianness = String::UTF16LE;
0186     } else if(bom == 0xfeff) {
0187       encWithEndianness = String::UTF16BE;
0188     }
0189   }
0190 
0191   d->synchedText.clear();
0192   while(pos < end) {
0193     String::Type enc = d->textEncoding;
0194     // If a UTF16 string has no BOM, use the encoding found above.
0195     if(enc == String::UTF16 && pos + 1 < end) {
0196 #if (((TAGLIB_MAJOR_VERSION) << 16) + ((TAGLIB_MINOR_VERSION) << 8) + (TAGLIB_PATCH_VERSION)) >= 0x010700
0197       ushort bom = data.mid(pos, 2).toUShort(true);
0198 #else
0199       ushort bom = static_cast<ushort>(data.mid(pos, 2).toShort(true));
0200 #endif
0201       if(bom != 0xfffe && bom != 0xfeff) {
0202         enc = encWithEndianness;
0203       }
0204     }
0205     String text = readStringField(data, enc, &pos);
0206     if(text.isNull() || pos + 4 > end)
0207       return;
0208 
0209     uint time = data.mid(pos, 4).toUInt(true);
0210     pos += 4;
0211 
0212     d->synchedText.append(SynchedText(time, text));
0213   }
0214 }
0215 
0216 ByteVector SynchronizedLyricsFrame::renderFields() const
0217 {
0218   ByteVector v;
0219 
0220   String::Type encoding = d->textEncoding;
0221 
0222 #if (((TAGLIB_MAJOR_VERSION) << 16) + ((TAGLIB_MINOR_VERSION) << 8) + (TAGLIB_PATCH_VERSION)) >= 0x010701
0223   encoding = checkTextEncoding(d->description, encoding);
0224   for(SynchedTextList::ConstIterator it = d->synchedText.begin();
0225       it != d->synchedText.end();
0226       ++it) {
0227     encoding = checkTextEncoding(it->text, encoding);
0228   }
0229 #endif
0230 
0231   v.append(char(encoding));
0232   v.append(d->language.size() == 3 ? d->language : "XXX");
0233   v.append(char(d->timestampFormat));
0234   v.append(char(d->type));
0235   v.append(d->description.data(encoding));
0236   v.append(textDelimiter(encoding));
0237   for(SynchedTextList::ConstIterator it = d->synchedText.begin();
0238       it != d->synchedText.end();
0239       ++it) {
0240     const SynchedText &entry = *it;
0241     v.append(entry.text.data(encoding));
0242     v.append(textDelimiter(encoding));
0243     v.append(ByteVector::fromUInt(entry.time));
0244   }
0245 
0246   return v;
0247 }
0248 
0249 ////////////////////////////////////////////////////////////////////////////////
0250 // private members
0251 ////////////////////////////////////////////////////////////////////////////////
0252 
0253 SynchronizedLyricsFrame::SynchronizedLyricsFrame(const ByteVector &data, Header *h)
0254   : Frame(h)
0255 {
0256   d = new SynchronizedLyricsFramePrivate();
0257   parseFields(fieldData(data));
0258 }