src/tag_parse_lyrics3.cpp

Go to the documentation of this file.
00001 // $Id: tag_parse_lyrics3.cpp,v 1.35 2002/10/04 08:52:23 t1mpy Exp $
00002 
00003 // id3lib: a C++ library for creating and manipulating id3v1/v2 tags
00004 // Copyright 1999, 2000  Scott Thomas Haug
00005 // Copyright 2002 Thijmen Klok (thijmen@id3lib.org)
00006 
00007 // This library is free software; you can redistribute it and/or modify it
00008 // under the terms of the GNU Library General Public License as published by
00009 // the Free Software Foundation; either version 2 of the License, or (at your
00010 // option) any later version.
00011 //
00012 // This library is distributed in the hope that it will be useful, but WITHOUT
00013 // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
00014 // FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Library General Public
00015 // License for more details.
00016 //
00017 // You should have received a copy of the GNU Library General Public License
00018 // along with this library; if not, write to the Free Software Foundation,
00019 // Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
00020 
00021 // The id3lib authors encourage improvements and optimisations to be sent to
00022 // the id3lib coordinator.  Please see the README file for details on where to
00023 // send such submissions.  See the AUTHORS file for a list of people who have
00024 // contributed to id3lib.  See the ChangeLog file for a list of changes to
00025 // id3lib.  These files are distributed with id3lib at
00026 // http://download.sourceforge.net/id3lib/
00027 
00028 #include <ctype.h>
00029 #include <memory.h>
00030 #include "tag_impl.h" //has <stdio.h> "tag.h" "header_tag.h" "frame.h" "field.h" "spec.h" "id3lib_strings.h" "utils.h"
00031 #include "helpers.h"
00032 #include "id3/io_decorators.h" //has "readers.h" "io_helpers.h" "utils.h"
00033 #include "io_strings.h"
00034 
00035 using namespace dami;
00036 
00037 namespace
00038 {
00039   uint32 readIntegerString(ID3_Reader& reader, size_t numBytes)
00040   {
00041     uint32 val = 0;
00042     for (size_t i = 0; i < numBytes && isdigit(reader.peekChar()); ++i)
00043     {
00044       val = (val * 10) + (reader.readChar() - '0');
00045     }
00046     ID3D_NOTICE( "readIntegerString: val = " << val );
00047     return val;
00048   }
00049 
00050   uint32 readIntegerString(ID3_Reader& reader)
00051   {
00052     return readIntegerString(reader, reader.remainingBytes());
00053   }
00054 
00055   bool isTimeStamp(ID3_Reader& reader)
00056   {
00057     ID3_Reader::pos_type cur = reader.getCur();
00058     if (reader.getEnd() < cur + 7)
00059     {
00060       return false;
00061     }
00062     bool its = ('[' == reader.readChar() &&
00063                 isdigit(reader.readChar()) && isdigit(reader.readChar()) &&
00064                 ':' == reader.readChar() &&
00065                 isdigit(reader.readChar()) && isdigit(reader.readChar()) &&
00066                 ']' == reader.readChar());
00067     reader.setCur(cur);
00068     if (its)
00069     {
00070       ID3D_NOTICE( "isTimeStamp(): found timestamp, cur = " << reader.getCur() );
00071     }
00072     return its;
00073   }
00074 
00075   uint32 readTimeStamp(ID3_Reader& reader)
00076   {
00077     reader.skipChars(1);
00078     size_t sec = readIntegerString(reader, 2) * 60;
00079     reader.skipChars(1);
00080     sec += readIntegerString(reader, 2);
00081     reader.skipChars(1);
00082     ID3D_NOTICE( "readTimeStamp(): timestamp = " << sec );
00083     return sec * 1000;
00084   }
00085 
00086   bool findText(ID3_Reader& reader, String text)
00087   {
00088     if (text.empty())
00089     {
00090       return true;
00091     }
00092 
00093     size_t index = 0;
00094     while (!reader.atEnd())
00095     {
00096       ID3_Reader::char_type ch = reader.readChar();
00097       if (ch == text[index])
00098       {
00099         index++;
00100       }
00101       else if (ch == text[0])
00102       {
00103         index = 1;
00104       }
00105       else
00106       {
00107         index = 0;
00108       }
00109       if (index == text.size())
00110       {
00111         reader.setCur(reader.getCur() - index);
00112         ID3D_NOTICE( "findText: found \"" << text << "\" at " <<
00113                      reader.getCur() );
00114         break;
00115       }
00116     }
00117     return !reader.atEnd();
00118   };
00119 
00120   void lyrics3ToSylt(ID3_Reader& reader, ID3_Writer& writer)
00121   {
00122     while (!reader.atEnd())
00123     {
00124       bool lf = false;
00125       size_t ms = 0;
00126       size_t count = 0;
00127       while (isTimeStamp(reader))
00128       {
00129         // For now, just skip over multiple time stamps
00130         if (count++ > 0)
00131         {
00132           readTimeStamp(reader);
00133         }
00134         else
00135         {
00136           ms = readTimeStamp(reader);
00137         }
00138       }
00139       while (!reader.atEnd() && !isTimeStamp(reader))
00140       {
00141         ID3_Reader::char_type ch = reader.readChar();
00142         if (0x0A == ch && (reader.atEnd() || isTimeStamp(reader)))
00143         {
00144           lf = true;
00145           break;
00146         }
00147         else
00148         {
00149           writer.writeChar(ch);
00150         }
00151       }
00152 
00153       // put synch identifier
00154       writer.writeChar('\0');
00155 
00156       // put timestamp
00157       ID3D_NOTICE( "lyrics3toSylt: ms = " << ms );
00158 
00159       io::writeBENumber(writer, ms, sizeof(uint32));
00160       if (lf)
00161       {
00162         ID3D_NOTICE( "lyrics3toSylt: adding lf" );
00163 
00164         // put the LF
00165         writer.writeChar(0x0A);
00166       }
00167     }
00168   }
00169 };
00170 
00171 bool lyr3::v1::parse(ID3_TagImpl& tag, ID3_Reader& reader)
00172 {
00173   io::ExitTrigger et(reader);
00174   ID3_Reader::pos_type end = reader.getCur();
00175   if (end < reader.getBeg() + 9 + 128)
00176   {
00177     ID3D_NOTICE( "id3::v1::parse: bailing, not enough bytes to parse, pos = " << end );
00178     return false;
00179   }
00180   reader.setCur(end - (9 + 128));
00181 
00182   {
00183     if (io::readText(reader, 9) != "LYRICSEND" ||
00184         io::readText(reader, 3) != "TAG")
00185     {
00186       return false;
00187     }
00188   }
00189 
00190   // we have a Lyrics3 v1.00 tag
00191   if (end < reader.getBeg() + 11 + 9 + 128)
00192   {
00193     // the file size isn't large enough to actually hold lyrics
00194     ID3D_WARNING( "id3::v1::parse: not enough data to parse lyrics3" );
00195     return false;
00196   }
00197 
00198   // reserve enough space for lyrics3 + id3v1 tag
00199   size_t window = end - reader.getBeg();
00200   size_t lyrDataSize = min<size_t>(window, 11 + 5100 + 9 + 128);
00201   reader.setCur(end - lyrDataSize);
00202   io::WindowedReader wr(reader, lyrDataSize - (9 + 128));
00203 
00204   if (!findText(wr, "LYRICSBEGIN"))
00205   {
00206     ID3D_WARNING( "id3::v1::parse: couldn't find LYRICSBEGIN, bailing" );
00207     return false;
00208   }
00209 
00210   et.setExitPos(wr.getCur());
00211   wr.skipChars(11);
00212   wr.setBeg(wr.getCur());
00213 
00214   io::LineFeedReader lfr(wr);
00215   String lyrics = io::readText(lfr, wr.remainingBytes());
00216   id3::v2::setLyrics(tag, lyrics, "Converted from Lyrics3 v1.00", "XXX");
00217 
00218   return true;
00219 }
00220 
00221 //bool parse(TagImpl& tag, ID3_Reader& reader)
00222 bool lyr3::v2::parse(ID3_TagImpl& tag, ID3_Reader& reader)
00223 {
00224   io::ExitTrigger et(reader);
00225   ID3_Reader::pos_type end = reader.getCur();
00226   if (end < reader.getBeg() + 6 + 9 + 128)
00227   {
00228     ID3D_NOTICE( "lyr3::v2::parse: bailing, not enough bytes to parse, pos = " << reader.getCur() );
00229     return false;
00230   }
00231 
00232   reader.setCur(end - (6 + 9 + 128));
00233   uint32 lyrSize = 0;
00234 
00235   ID3_Reader::pos_type beg = reader.getCur();
00236   lyrSize = readIntegerString(reader, 6);
00237   if (reader.getCur() < beg + 6)
00238   {
00239     ID3D_NOTICE( "lyr3::v2::parse: couldn't find numeric string, lyrSize = " <<
00240                  lyrSize );
00241     return false;
00242   }
00243 
00244   if (io::readText(reader, 9) != "LYRICS200" ||
00245       io::readText(reader, 3) != "TAG")
00246   {
00247     return false;
00248   }
00249 
00250   if (end < reader.getBeg() + lyrSize + 6 + 9 + 128)
00251   {
00252     ID3D_WARNING( "lyr3::v2::parse: not enough data to parse tag, lyrSize = " << lyrSize );
00253     return false;
00254   }
00255   reader.setCur(end - (lyrSize + 6 + 9 + 128));
00256 
00257   io::WindowedReader wr(reader);
00258   wr.setWindow(wr.getCur(), lyrSize);
00259 
00260   beg = wr.getCur();
00261 
00262   if (io::readText(wr, 11) != "LYRICSBEGIN")
00263   {
00264     // not a lyrics v2.00 tag
00265     ID3D_WARNING( "lyr3::v2::parse: couldn't find LYRICSBEGIN, bailing" );
00266     return false;
00267   }
00268 
00269   bool has_time_stamps = false;
00270 
00271   ID3_Frame* lyr_frame = NULL;
00272 
00273   while (!wr.atEnd())
00274   {
00275     uint32 fldSize;
00276 
00277     String fldName = io::readText(wr, 3);
00278     ID3D_NOTICE( "lyr3::v2::parse: fldName = " << fldName );
00279     fldSize = readIntegerString(wr, 5);
00280     ID3D_NOTICE( "lyr3::v2::parse: fldSize = " << fldSize );
00281 
00282     String fldData;
00283 
00284     io::WindowedReader wr2(wr, fldSize);
00285     io::LineFeedReader lfr(wr2);
00286 
00287     fldData = io::readText(lfr, fldSize);
00288     ID3D_NOTICE( "lyr3::v2::parse: fldData = \"" << fldData << "\"" );
00289 
00290     // the IND field
00291     if (fldName == "IND")
00292     {
00293       has_time_stamps = (fldData.size() > 1 && fldData[1] == '1');
00294     }
00295 
00296     // the TITLE field
00297     else if (fldName == "ETT" && !id3::v2::hasTitle(tag))
00298     {
00299       //tag.setTitle(fldData);
00300       id3::v2::setTitle(tag, fldData);
00301     }
00302 
00303     // the ARTIST field
00304     else if (fldName == "EAR" && !id3::v2::hasArtist(tag))
00305     {
00306       //tag.setArtist(fldData);
00307       id3::v2::setArtist(tag, fldData);
00308     }
00309 
00310     // the ALBUM field
00311     else if (fldName == "EAL" && !id3::v2::hasAlbum(tag))
00312     {
00313       //tag.setAlbum(fldData);
00314       id3::v2::setAlbum(tag, fldData);
00315     }
00316 
00317     // the Lyrics/Music AUTHOR field
00318     else if (fldName == "AUT")
00319     {
00320       //tag.setAuthor(fldData);
00321       id3::v2::setLyricist(tag, fldData);
00322     }
00323 
00324     // the INFORMATION field
00325     else if (fldName == "INF")
00326     {
00327       //tag.setInfo(fldData);
00328       id3::v2::setComment(tag, fldData, "Lyrics3 v2.00 INF", "XXX");
00329     }
00330 
00331     // the LYRICS field
00332     else if (fldName == "LYR")
00333     {
00334       // if already found an INF field, use it as description
00335       String desc =  "Converted from Lyrics3 v2.00";
00336       //tag.setLyrics(fldData);
00337       if (!has_time_stamps)
00338       {
00339         lyr_frame = id3::v2::setLyrics(tag, fldData, desc, "XXX");
00340       }
00341       else
00342       {
00343         // converts from lyrics3 to SYLT in-place
00344         io::StringReader sr(fldData);
00345         ID3D_NOTICE( "lyr3::v2::parse: determining synced lyrics" );
00346         BString sylt;
00347         io::BStringWriter sw(sylt);
00348         lyrics3ToSylt(sr, sw);
00349 
00350         lyr_frame = id3::v2::setSyncLyrics(tag, sylt, ID3TSF_MS, desc,
00351                                            "XXX", ID3CT_LYRICS);
00352         ID3D_NOTICE( "lyr3::v2::parse: determined synced lyrics" );
00353       }
00354     }
00355     else if (fldName == "IMG")
00356     {
00357       // currently unsupported
00358       ID3D_WARNING( "lyr3::v2::parse: IMG field unsupported" );
00359     }
00360     else
00361     {
00362       ID3D_WARNING( "lyr3::v2::parse: undefined field id: " <<
00363                     fldName );
00364     }
00365   }
00366 
00367   et.setExitPos(beg);
00368   return true;
00369 }
00370 

Generated on Mon Aug 20 17:48:46 2007 for id3lib by  doxygen 1.5.2