From 61fb0427c5641b30c0b000751e6f4885f526ec60 Mon Sep 17 00:00:00 2001 From: Chocobozzz Date: Tue, 24 Nov 2015 19:36:24 +0100 Subject: [PATCH] Update taglib to 1.10.0 --- 3rdparty/taglib/CMakeLists.txt | 38 +- 3rdparty/taglib/ape/apefile.cpp | 75 ++- 3rdparty/taglib/ape/apefile.h | 19 +- 3rdparty/taglib/ape/apefooter.h | 2 +- 3rdparty/taglib/ape/apeitem.cpp | 6 +- 3rdparty/taglib/ape/apeitem.h | 4 +- 3rdparty/taglib/ape/apeproperties.cpp | 174 +++--- 3rdparty/taglib/ape/apeproperties.h | 66 +- 3rdparty/taglib/ape/apetag.cpp | 38 +- 3rdparty/taglib/ape/apetag.h | 2 +- 3rdparty/taglib/asf/asfattribute.cpp | 52 +- 3rdparty/taglib/asf/asffile.cpp | 567 ++++++++++-------- 3rdparty/taglib/asf/asffile.h | 28 +- 3rdparty/taglib/asf/asfpicture.cpp | 8 +- 3rdparty/taglib/asf/asfpicture.h | 2 +- 3rdparty/taglib/asf/asfproperties.cpp | 113 +++- 3rdparty/taglib/asf/asfproperties.h | 114 +++- 3rdparty/taglib/asf/asftag.cpp | 28 +- 3rdparty/taglib/asf/asftag.h | 27 +- 3rdparty/taglib/asf/asfutils.h | 101 ++++ 3rdparty/taglib/audioproperties.cpp | 115 +++- 3rdparty/taglib/audioproperties.h | 19 +- 3rdparty/taglib/fileref.cpp | 2 +- 3rdparty/taglib/fileref.h | 8 +- 3rdparty/taglib/flac/flacfile.cpp | 465 +++++++------- 3rdparty/taglib/flac/flacfile.h | 30 +- 3rdparty/taglib/flac/flacproperties.cpp | 77 +-- 3rdparty/taglib/flac/flacproperties.h | 55 +- 3rdparty/taglib/it/itproperties.cpp | 10 + 3rdparty/taglib/it/itproperties.h | 10 +- 3rdparty/taglib/mod/modproperties.cpp | 10 + 3rdparty/taglib/mod/modproperties.h | 12 +- 3rdparty/taglib/mod/modtag.cpp | 2 +- 3rdparty/taglib/mod/modtag.h | 6 +- 3rdparty/taglib/mp4/mp4atom.cpp | 21 +- 3rdparty/taglib/mp4/mp4atom.h | 2 +- 3rdparty/taglib/mp4/mp4coverart.cpp | 9 +- 3rdparty/taglib/mp4/mp4file.cpp | 77 ++- 3rdparty/taglib/mp4/mp4file.h | 11 +- 3rdparty/taglib/mp4/mp4item.cpp | 10 +- 3rdparty/taglib/mp4/mp4properties.cpp | 245 ++++---- 3rdparty/taglib/mp4/mp4properties.h | 51 +- 3rdparty/taglib/mp4/mp4tag.cpp | 198 +++--- 3rdparty/taglib/mp4/mp4tag.h | 97 ++- 3rdparty/taglib/mpc/mpcfile.cpp | 49 +- 3rdparty/taglib/mpc/mpcfile.h | 18 +- 3rdparty/taglib/mpc/mpcproperties.cpp | 155 +++-- 3rdparty/taglib/mpc/mpcproperties.h | 44 +- 3rdparty/taglib/mpeg/id3v1/id3v1genres.cpp | 48 +- 3rdparty/taglib/mpeg/id3v1/id3v1genres.h | 2 +- 3rdparty/taglib/mpeg/id3v1/id3v1tag.h | 6 +- .../taglib/mpeg/id3v2/frames/chapterframe.cpp | 306 ++++++++++ .../taglib/mpeg/id3v2/frames/chapterframe.h | 249 ++++++++ .../taglib/mpeg/id3v2/frames/commentsframe.h | 6 +- .../id3v2/frames/eventtimingcodesframe.cpp | 144 +++++ .../mpeg/id3v2/frames/eventtimingcodesframe.h | 185 ++++++ .../taglib/mpeg/id3v2/frames/ownershipframe.h | 8 +- .../mpeg/id3v2/frames/popularimeterframe.h | 2 +- .../mpeg/id3v2/frames/relativevolumeframe.h | 6 +- .../id3v2/frames/synchronizedlyricsframe.cpp | 243 ++++++++ .../id3v2/frames/synchronizedlyricsframe.h | 231 +++++++ .../id3v2/frames/tableofcontentsframe.cpp | 338 +++++++++++ .../mpeg/id3v2/frames/tableofcontentsframe.h | 258 ++++++++ .../id3v2/frames/textidentificationframe.cpp | 6 +- .../id3v2/frames/textidentificationframe.h | 2 +- .../id3v2/frames/uniquefileidentifierframe.h | 2 +- .../id3v2/frames/unsynchronizedlyricsframe.h | 4 +- .../taglib/mpeg/id3v2/frames/urllinkframe.cpp | 2 +- .../taglib/mpeg/id3v2/id3v2extendedheader.h | 2 +- 3rdparty/taglib/mpeg/id3v2/id3v2frame.cpp | 49 +- 3rdparty/taglib/mpeg/id3v2/id3v2frame.h | 8 +- .../taglib/mpeg/id3v2/id3v2framefactory.cpp | 56 +- .../taglib/mpeg/id3v2/id3v2framefactory.h | 10 +- 3rdparty/taglib/mpeg/id3v2/id3v2header.cpp | 2 +- 3rdparty/taglib/mpeg/id3v2/id3v2header.h | 2 +- 3rdparty/taglib/mpeg/id3v2/id3v2tag.cpp | 54 +- 3rdparty/taglib/mpeg/id3v2/id3v2tag.h | 22 +- 3rdparty/taglib/mpeg/mpegfile.cpp | 75 +-- 3rdparty/taglib/mpeg/mpegfile.h | 31 +- 3rdparty/taglib/mpeg/mpegheader.cpp | 20 +- 3rdparty/taglib/mpeg/mpegheader.h | 2 +- 3rdparty/taglib/mpeg/mpegproperties.cpp | 154 ++--- 3rdparty/taglib/mpeg/mpegproperties.h | 46 +- 3rdparty/taglib/mpeg/xingheader.cpp | 97 +-- 3rdparty/taglib/mpeg/xingheader.h | 53 +- 3rdparty/taglib/ogg/flac/oggflacfile.cpp | 34 +- 3rdparty/taglib/ogg/flac/oggflacfile.h | 21 +- 3rdparty/taglib/ogg/oggfile.cpp | 2 +- 3rdparty/taglib/ogg/oggpage.h | 4 +- 3rdparty/taglib/ogg/opus/opusfile.cpp | 14 +- 3rdparty/taglib/ogg/opus/opusfile.h | 10 +- 3rdparty/taglib/ogg/opus/opusproperties.cpp | 50 +- 3rdparty/taglib/ogg/opus/opusproperties.h | 46 +- 3rdparty/taglib/ogg/speex/speexfile.cpp | 22 +- 3rdparty/taglib/ogg/speex/speexfile.h | 12 +- 3rdparty/taglib/ogg/speex/speexproperties.cpp | 69 ++- 3rdparty/taglib/ogg/speex/speexproperties.h | 46 +- 3rdparty/taglib/ogg/vorbis/vorbisfile.cpp | 20 +- 3rdparty/taglib/ogg/vorbis/vorbisfile.h | 10 +- .../taglib/ogg/vorbis/vorbisproperties.cpp | 65 +- 3rdparty/taglib/ogg/vorbis/vorbisproperties.h | 41 +- 3rdparty/taglib/ogg/xiphcomment.cpp | 12 +- .../01-too-many-flac-metadata-blocks.patch | 29 - .../taglib/patches/02-swap-big-endian.patch | 22 - 3rdparty/taglib/riff/aiff/aifffile.cpp | 54 +- 3rdparty/taglib/riff/aiff/aifffile.h | 8 +- 3rdparty/taglib/riff/aiff/aiffproperties.cpp | 163 +++-- 3rdparty/taglib/riff/aiff/aiffproperties.h | 89 ++- 3rdparty/taglib/riff/rifffile.cpp | 32 +- 3rdparty/taglib/riff/rifffile.h | 8 +- 3rdparty/taglib/riff/wav/infotag.cpp | 20 +- 3rdparty/taglib/riff/wav/infotag.h | 32 +- 3rdparty/taglib/riff/wav/wavfile.cpp | 107 ++-- 3rdparty/taglib/riff/wav/wavfile.h | 20 +- 3rdparty/taglib/riff/wav/wavproperties.cpp | 147 ++++- 3rdparty/taglib/riff/wav/wavproperties.h | 77 ++- 3rdparty/taglib/s3m/s3mproperties.cpp | 10 + 3rdparty/taglib/s3m/s3mproperties.h | 10 +- 3rdparty/taglib/tag.cpp | 4 +- 3rdparty/taglib/tag.h | 2 +- 3rdparty/taglib/taglib_config.h.cmake | 2 + 3rdparty/taglib/toolkit/taglib.h | 6 +- 3rdparty/taglib/toolkit/tbytevector.cpp | 292 ++++++--- 3rdparty/taglib/toolkit/tbytevector.h | 100 ++- 3rdparty/taglib/toolkit/tdebug.cpp | 40 +- 3rdparty/taglib/toolkit/tdebuglistener.h | 12 +- 3rdparty/taglib/toolkit/tfile.cpp | 47 +- 3rdparty/taglib/toolkit/tfile.h | 14 +- 3rdparty/taglib/toolkit/tfilestream.cpp | 54 +- 3rdparty/taglib/toolkit/tiostream.cpp | 4 + 3rdparty/taglib/toolkit/tiostream.h | 2 +- 3rdparty/taglib/toolkit/tlist.h | 2 +- 3rdparty/taglib/toolkit/tlist.tcc | 9 +- 3rdparty/taglib/toolkit/tmap.tcc | 4 +- 3rdparty/taglib/toolkit/tpropertymap.cpp | 12 +- 3rdparty/taglib/toolkit/tpropertymap.h | 16 +- 3rdparty/taglib/toolkit/trefcounter.cpp | 30 +- 3rdparty/taglib/toolkit/trefcounter.h | 4 +- 3rdparty/taglib/toolkit/tstring.cpp | 319 +++++----- 3rdparty/taglib/toolkit/tstring.h | 106 +++- 3rdparty/taglib/toolkit/tstringlist.h | 2 +- 3rdparty/taglib/toolkit/tutils.h | 103 +++- 3rdparty/taglib/toolkit/unicode.h | 5 - 3rdparty/taglib/trueaudio/trueaudiofile.cpp | 52 +- 3rdparty/taglib/trueaudio/trueaudiofile.h | 22 +- .../taglib/trueaudio/trueaudioproperties.cpp | 61 +- .../taglib/trueaudio/trueaudioproperties.h | 43 +- 3rdparty/taglib/wavpack/wavpackfile.cpp | 31 +- 3rdparty/taglib/wavpack/wavpackfile.h | 20 +- 3rdparty/taglib/wavpack/wavpackproperties.cpp | 165 +++-- 3rdparty/taglib/wavpack/wavpackproperties.h | 51 +- 3rdparty/taglib/xm/xmfile.cpp | 89 ++- 3rdparty/taglib/xm/xmproperties.cpp | 10 + 3rdparty/taglib/xm/xmproperties.h | 12 +- CMakeLists.txt | 2 +- 155 files changed, 6573 insertions(+), 2485 deletions(-) create mode 100644 3rdparty/taglib/asf/asfutils.h create mode 100644 3rdparty/taglib/mpeg/id3v2/frames/chapterframe.cpp create mode 100644 3rdparty/taglib/mpeg/id3v2/frames/chapterframe.h create mode 100644 3rdparty/taglib/mpeg/id3v2/frames/eventtimingcodesframe.cpp create mode 100644 3rdparty/taglib/mpeg/id3v2/frames/eventtimingcodesframe.h create mode 100644 3rdparty/taglib/mpeg/id3v2/frames/synchronizedlyricsframe.cpp create mode 100644 3rdparty/taglib/mpeg/id3v2/frames/synchronizedlyricsframe.h create mode 100644 3rdparty/taglib/mpeg/id3v2/frames/tableofcontentsframe.cpp create mode 100644 3rdparty/taglib/mpeg/id3v2/frames/tableofcontentsframe.h delete mode 100644 3rdparty/taglib/patches/01-too-many-flac-metadata-blocks.patch delete mode 100644 3rdparty/taglib/patches/02-swap-big-endian.patch diff --git a/3rdparty/taglib/CMakeLists.txt b/3rdparty/taglib/CMakeLists.txt index fe2cf4f8b..b3e9ec857 100644 --- a/3rdparty/taglib/CMakeLists.txt +++ b/3rdparty/taglib/CMakeLists.txt @@ -1,8 +1,8 @@ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-delete-non-virtual-dtor") -set(TAGLIB_SOVERSION_CURRENT 13) -set(TAGLIB_SOVERSION_REVISION 0) -set(TAGLIB_SOVERSION_AGE 12) +set(TAGLIB_SOVERSION_CURRENT 16) +set(TAGLIB_SOVERSION_REVISION 1) +set(TAGLIB_SOVERSION_AGE 15) math(EXPR TAGLIB_SOVERSION_MAJOR "${TAGLIB_SOVERSION_CURRENT} - ${TAGLIB_SOVERSION_AGE}") math(EXPR TAGLIB_SOVERSION_MINOR "${TAGLIB_SOVERSION_AGE}") math(EXPR TAGLIB_SOVERSION_PATCH "${TAGLIB_SOVERSION_REVISION}") @@ -48,6 +48,8 @@ include_directories( if(ZLIB_FOUND) include_directories(${ZLIB_INCLUDE_DIR}) +elseif(HAVE_ZLIB_SOURCE) + include_directories(${ZLIB_SOURCE}) endif() set(tag_HDRS @@ -87,16 +89,20 @@ 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 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/unsynchronizedlyricsframe.h mpeg/id3v2/frames/urllinkframe.h + mpeg/id3v2/frames/chapterframe.h + mpeg/id3v2/frames/tableofcontentsframe.h ogg/oggfile.h ogg/oggpage.h ogg/oggpageheader.h @@ -177,16 +183,20 @@ 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 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 mpeg/id3v2/frames/unsynchronizedlyricsframe.cpp mpeg/id3v2/frames/urllinkframe.cpp + mpeg/id3v2/frames/chapterframe.cpp + mpeg/id3v2/frames/tableofcontentsframe.cpp ) set(ogg_SRCS @@ -313,14 +323,31 @@ set(toolkit_SRCS toolkit/tpropertymap.cpp toolkit/trefcounter.cpp toolkit/tdebuglistener.cpp - toolkit/unicode.cpp ) +if(NOT WIN32) + set(unicode_SRCS + toolkit/unicode.cpp + ) +endif() + +if(HAVE_ZLIB_SOURCE) + set(zlib_SRCS + ${ZLIB_SOURCE}/adler32.c + ${ZLIB_SOURCE}/crc32.c + ${ZLIB_SOURCE}/inffast.c + ${ZLIB_SOURCE}/inflate.c + ${ZLIB_SOURCE}/inftrees.c + ${ZLIB_SOURCE}/zutil.c + ) +endif() + set(tag_LIB_SRCS ${mpeg_SRCS} ${id3v1_SRCS} ${id3v2_SRCS} ${frames_SRCS} ${ogg_SRCS} ${vorbis_SRCS} ${oggflacs_SRCS} ${mpc_SRCS} ${ape_SRCS} ${toolkit_SRCS} ${flacs_SRCS} ${wavpack_SRCS} ${speex_SRCS} ${trueaudio_SRCS} ${riff_SRCS} ${aiff_SRCS} ${wav_SRCS} ${asf_SRCS} ${mp4_SRCS} ${mod_SRCS} ${s3m_SRCS} ${it_SRCS} ${xm_SRCS} ${opus_SRCS} + ${unicode_SRCS} ${zlib_SRCS} tag.cpp tagunion.cpp fileref.cpp @@ -345,6 +372,7 @@ foreach(header ${tag_HDRS}) configure_file( "${header}" "${CMAKE_CURRENT_BINARY_DIR}/headers/taglib/${header_name}" - COPY_ONLY + COPYONLY ) endforeach() + diff --git a/3rdparty/taglib/ape/apefile.cpp b/3rdparty/taglib/ape/apefile.cpp index 3cb9d9a70..c774f5160 100644 --- a/3rdparty/taglib/ape/apefile.cpp +++ b/3rdparty/taglib/ape/apefile.cpp @@ -36,6 +36,7 @@ #include #include #include +#include #include #include "apefile.h" @@ -57,12 +58,17 @@ public: APELocation(-1), APESize(0), ID3v1Location(-1), + ID3v2Header(0), + ID3v2Location(-1), + ID3v2Size(0), properties(0), hasAPE(false), - hasID3v1(false) {} + hasID3v1(false), + hasID3v2(false) {} ~FilePrivate() { + delete ID3v2Header; delete properties; } @@ -71,6 +77,10 @@ public: long ID3v1Location; + ID3v2::Header *ID3v2Header; + long ID3v2Location; + uint ID3v2Size; + TagUnion tag; Properties *properties; @@ -80,26 +90,27 @@ public: bool hasAPE; bool hasID3v1; + bool hasID3v2; }; //////////////////////////////////////////////////////////////////////////////// // public members //////////////////////////////////////////////////////////////////////////////// -APE::File::File(FileName file, bool readProperties, - Properties::ReadStyle propertiesStyle) : TagLib::File(file) +APE::File::File(FileName file, bool readProperties, Properties::ReadStyle) : + TagLib::File(file), + d(new FilePrivate()) { - d = new FilePrivate; if(isOpen()) - read(readProperties, propertiesStyle); + read(readProperties); } -APE::File::File(IOStream *stream, bool readProperties, - Properties::ReadStyle propertiesStyle) : TagLib::File(stream) +APE::File::File(IOStream *stream, bool readProperties, Properties::ReadStyle) : + TagLib::File(stream), + d(new FilePrivate()) { - d = new FilePrivate; if(isOpen()) - read(readProperties, propertiesStyle); + read(readProperties); } APE::File::~File() @@ -249,8 +260,19 @@ bool APE::File::hasID3v1Tag() const // private members //////////////////////////////////////////////////////////////////////////////// -void APE::File::read(bool readProperties, Properties::ReadStyle /* propertiesStyle */) +void APE::File::read(bool readProperties) { + // Look for an ID3v2 tag + + d->ID3v2Location = findID3v2(); + + if(d->ID3v2Location >= 0) { + seek(d->ID3v2Location); + d->ID3v2Header = new ID3v2::Header(readBlock(ID3v2::Header::size())); + d->ID3v2Size = d->ID3v2Header->completeTagSize(); + d->hasID3v2 = true; + } + // Look for an ID3v1 tag d->ID3v1Location = findID3v1(); @@ -277,7 +299,25 @@ void APE::File::read(bool readProperties, Properties::ReadStyle /* propertiesSty // Look for APE audio properties if(readProperties) { - d->properties = new Properties(this); + + long streamLength; + + if(d->hasAPE) + streamLength = d->APELocation; + else if(d->hasID3v1) + streamLength = d->ID3v1Location; + else + streamLength = length(); + + if(d->hasID3v2) { + seek(d->ID3v2Location + d->ID3v2Size); + streamLength -= (d->ID3v2Location + d->ID3v2Size); + } + else { + seek(0); + } + + d->properties = new Properties(this, streamLength); } } @@ -312,3 +352,16 @@ long APE::File::findID3v1() return -1; } + +long APE::File::findID3v2() +{ + if(!isValid()) + return -1; + + seek(0); + + if(readBlock(3) == ID3v2::Header::fileIdentifier()) + return 0; + + return -1; +} diff --git a/3rdparty/taglib/ape/apefile.h b/3rdparty/taglib/ape/apefile.h index f7b509f40..1d2e5c675 100644 --- a/3rdparty/taglib/ape/apefile.h +++ b/3rdparty/taglib/ape/apefile.h @@ -130,7 +130,7 @@ namespace TagLib { /*! * Implements the unified property interface -- import function. - * Creates an APEv2 tag if necessary. A pontentially existing ID3v1 + * Creates an APEv2 tag if necessary. A potentially existing ID3v1 * tag will be updated as well. */ PropertyMap setProperties(const PropertyMap &); @@ -146,6 +146,9 @@ namespace TagLib { * * \note According to the official Monkey's Audio SDK, an APE file * can only have either ID3V1 or APE tags, so a parameter is used here. + * + * \warning In the current implementation, it's dangerous to call save() + * repeatedly. At worst it will corrupt the file. */ virtual bool save(); @@ -156,8 +159,8 @@ namespace TagLib { * if there is no valid ID3v1 tag. If \a create is true it will create * an ID3v1 tag if one does not exist and returns a valid pointer. * - * \note This may return a valid pointer regardless of whether or not the - * file on disk has an ID3v1 tag. Use hasID3v1Tag() to check if the file + * \note This may return a valid pointer regardless of whether or not the + * file on disk has an ID3v1 tag. Use hasID3v1Tag() to check if the file * on disk actually has an ID3v1 tag. * * \note The Tag is still owned by the MPEG::File and should not be @@ -175,8 +178,8 @@ namespace TagLib { * if there is no valid APE tag. If \a create is true it will create * an APE tag if one does not exist and returns a valid pointer. * - * \note This may return a valid pointer regardless of whether or not the - * file on disk has an APE tag. Use hasAPETag() to check if the file + * \note This may return a valid pointer regardless of whether or not the + * file on disk has an APE tag. Use hasAPETag() to check if the file * on disk actually has an APE tag. * * \note The Tag is still owned by the MPEG::File and should not be @@ -215,10 +218,10 @@ namespace TagLib { File(const File &); File &operator=(const File &); - void read(bool readProperties, Properties::ReadStyle propertiesStyle); - void scan(); - long findID3v1(); + void read(bool readProperties); long findAPE(); + long findID3v1(); + long findID3v2(); class FilePrivate; FilePrivate *d; diff --git a/3rdparty/taglib/ape/apefooter.h b/3rdparty/taglib/ape/apefooter.h index 080f93006..683af12f0 100644 --- a/3rdparty/taglib/ape/apefooter.h +++ b/3rdparty/taglib/ape/apefooter.h @@ -37,7 +37,7 @@ namespace TagLib { /*! * This class implements APE footers (and headers). It attempts to follow, both - * semantically and programatically, the structure specified in + * semantically and programmatically, the structure specified in * the APE v2.0 standard. The API is based on the properties of APE footer and * headers specified there. */ diff --git a/3rdparty/taglib/ape/apeitem.cpp b/3rdparty/taglib/ape/apeitem.cpp index 3490173a2..49c3a6653 100644 --- a/3rdparty/taglib/ape/apeitem.cpp +++ b/3rdparty/taglib/ape/apeitem.cpp @@ -86,8 +86,10 @@ APE::Item::~Item() Item &APE::Item::operator=(const Item &item) { - delete d; - d = new ItemPrivate(*item.d); + if(&item != this) { + delete d; + d = new ItemPrivate(*item.d); + } return *this; } diff --git a/3rdparty/taglib/ape/apeitem.h b/3rdparty/taglib/ape/apeitem.h index 0588d1850..4dd77d607 100644 --- a/3rdparty/taglib/ape/apeitem.h +++ b/3rdparty/taglib/ape/apeitem.h @@ -153,7 +153,7 @@ namespace TagLib { /*! * Returns the value as a single string. In case of multiple strings, - * the first is returned. If the data type is not \a Text, always returns + * the first is returned. If the data type is not \a Text, always returns * an empty String. */ String toString() const; @@ -164,7 +164,7 @@ namespace TagLib { #endif /*! - * Returns the list of text values. If the data type is not \a Text, always + * Returns the list of text values. If the data type is not \a Text, always * returns an empty StringList. */ StringList values() const; diff --git a/3rdparty/taglib/ape/apeproperties.cpp b/3rdparty/taglib/ape/apeproperties.cpp index 4940edead..4a339456e 100644 --- a/3rdparty/taglib/ape/apeproperties.cpp +++ b/3rdparty/taglib/ape/apeproperties.cpp @@ -33,22 +33,22 @@ #include "id3v2tag.h" #include "apeproperties.h" #include "apefile.h" +#include "apetag.h" +#include "apefooter.h" using namespace TagLib; class APE::Properties::PropertiesPrivate { public: - PropertiesPrivate(File *file, long streamLength) : + PropertiesPrivate() : length(0), bitrate(0), sampleRate(0), channels(0), version(0), bitsPerSample(0), - sampleFrames(0), - file(file), - streamLength(streamLength) {} + sampleFrames(0) {} int length; int bitrate; @@ -57,18 +57,24 @@ public: int version; int bitsPerSample; uint sampleFrames; - File *file; - long streamLength; }; //////////////////////////////////////////////////////////////////////////////// // public members //////////////////////////////////////////////////////////////////////////////// -APE::Properties::Properties(File *file, ReadStyle style) : AudioProperties(style) +APE::Properties::Properties(File *, ReadStyle style) : + AudioProperties(style), + d(new PropertiesPrivate()) { - d = new PropertiesPrivate(file, file->length()); - read(); + debug("APE::Properties::Properties() -- This constructor is no longer used."); +} + +APE::Properties::Properties(File *file, long streamLength, ReadStyle style) : + AudioProperties(style), + d(new PropertiesPrivate()) +{ + read(file, streamLength); } APE::Properties::~Properties() @@ -77,6 +83,16 @@ APE::Properties::~Properties() } int APE::Properties::length() const +{ + return lengthInSeconds(); +} + +int APE::Properties::lengthInSeconds() const +{ + return d->length / 1000; +} + +int APE::Properties::lengthInMilliseconds() const { return d->length; } @@ -115,98 +131,93 @@ TagLib::uint APE::Properties::sampleFrames() const // private members //////////////////////////////////////////////////////////////////////////////// - -void APE::Properties::read() +namespace { - // First we are searching the descriptor - long offset = findDescriptor(); - if(offset < 0) - return; + inline int headerVersion(const ByteVector &header) + { + if(header.size() < 6 || !header.startsWith("MAC ")) + return -1; - // Then we read the header common for all versions of APE - d->file->seek(offset); - ByteVector commonHeader = d->file->readBlock(6); - if(!commonHeader.startsWith("MAC ")) - return; - d->version = commonHeader.toUShort(4, false); - - if(d->version >= 3980) { - analyzeCurrent(); - } - else { - analyzeOld(); + return header.toUShort(4, false); } } -long APE::Properties::findDescriptor() +void APE::Properties::read(File *file, long streamLength) { - long ID3v2Location = findID3v2(); - long ID3v2OriginalSize = 0; - bool hasID3v2 = false; - if(ID3v2Location >= 0) { - ID3v2::Tag tag(d->file, ID3v2Location); - ID3v2OriginalSize = tag.header()->completeTagSize(); - if(tag.header()->tagSize() > 0) - hasID3v2 = true; + // First, we assume that the file pointer is set at the first descriptor. + long offset = file->tell(); + int version = headerVersion(file->readBlock(6)); + + // Next, we look for the descriptor. + if(version < 0) { + offset = file->find("MAC ", offset); + file->seek(offset); + version = headerVersion(file->readBlock(6)); } - long offset = 0; - if(hasID3v2) - offset = d->file->find("MAC ", ID3v2Location + ID3v2OriginalSize); + if(version < 0) { + debug("APE::Properties::read() -- APE descriptor not found"); + return; + } + + d->version = version; + + if(d->version >= 3980) + analyzeCurrent(file); else - offset = d->file->find("MAC "); + analyzeOld(file); - if(offset < 0) { - debug("APE::Properties::findDescriptor() -- APE descriptor not found"); - return -1; + if(d->sampleFrames > 0 && d->sampleRate > 0) { + const double length = d->sampleFrames * 1000.0 / d->sampleRate; + d->length = static_cast(length + 0.5); + d->bitrate = static_cast(streamLength * 8.0 / length + 0.5); } - - return offset; } -long APE::Properties::findID3v2() -{ - if(!d->file->isValid()) - return -1; - - d->file->seek(0); - - if(d->file->readBlock(3) == ID3v2::Header::fileIdentifier()) - return 0; - - return -1; -} - -void APE::Properties::analyzeCurrent() +void APE::Properties::analyzeCurrent(File *file) { // Read the descriptor - d->file->seek(2, File::Current); - ByteVector descriptor = d->file->readBlock(44); + file->seek(2, File::Current); + const ByteVector descriptor = file->readBlock(44); + if(descriptor.size() < 44) { + debug("APE::Properties::analyzeCurrent() -- descriptor is too short."); + return; + } + const uint descriptorBytes = descriptor.toUInt(0, false); - if ((descriptorBytes - 52) > 0) - d->file->seek(descriptorBytes - 52, File::Current); + if((descriptorBytes - 52) > 0) + file->seek(descriptorBytes - 52, File::Current); // Read the header - ByteVector header = d->file->readBlock(24); + const ByteVector header = file->readBlock(24); + if(header.size() < 24) { + debug("APE::Properties::analyzeCurrent() -- MAC header is too short."); + return; + } // Get the APE info d->channels = header.toShort(18, false); d->sampleRate = header.toUInt(20, false); d->bitsPerSample = header.toShort(16, false); - //d->compressionLevel = - const uint totalFrames = header.toUInt(12, false); + const uint totalFrames = header.toUInt(12, false); + if(totalFrames == 0) + return; + const uint blocksPerFrame = header.toUInt(4, false); const uint finalFrameBlocks = header.toUInt(8, false); - d->sampleFrames = totalFrames > 0 ? (totalFrames - 1) * blocksPerFrame + finalFrameBlocks : 0; - d->length = d->sampleRate > 0 ? d->sampleFrames / d->sampleRate : 0; - d->bitrate = d->length > 0 ? ((d->streamLength * 8L) / d->length) / 1000 : 0; + d->sampleFrames = (totalFrames - 1) * blocksPerFrame + finalFrameBlocks; } -void APE::Properties::analyzeOld() +void APE::Properties::analyzeOld(File *file) { - ByteVector header = d->file->readBlock(26); + const ByteVector header = file->readBlock(26); + if(header.size() < 26) { + debug("APE::Properties::analyzeOld() -- MAC header is too short."); + return; + } + const uint totalFrames = header.toUInt(18, false); // Fail on 0 length APE files (catches non-finalized APE files) @@ -221,12 +232,21 @@ void APE::Properties::analyzeOld() blocksPerFrame = 73728; else blocksPerFrame = 9216; + + // Get the APE info d->channels = header.toShort(4, false); d->sampleRate = header.toUInt(6, false); - const uint finalFrameBlocks = header.toUInt(22, false); - const uint totalBlocks - = totalFrames > 0 ? (totalFrames - 1) * blocksPerFrame + finalFrameBlocks : 0; - d->length = totalBlocks / d->sampleRate; - d->bitrate = d->length > 0 ? ((d->streamLength * 8L) / d->length) / 1000 : 0; -} + const uint finalFrameBlocks = header.toUInt(22, false); + d->sampleFrames = (totalFrames - 1) * blocksPerFrame + finalFrameBlocks; + + // Get the bit depth from the RIFF-fmt chunk. + file->seek(16, File::Current); + const ByteVector fmt = file->readBlock(28); + if(fmt.size() < 28 || !fmt.startsWith("WAVEfmt ")) { + debug("APE::Properties::analyzeOld() -- fmt header is too short."); + return; + } + + d->bitsPerSample = fmt.toShort(26, false); +} diff --git a/3rdparty/taglib/ape/apeproperties.h b/3rdparty/taglib/ape/apeproperties.h index f154ec342..fcf125ff5 100644 --- a/3rdparty/taglib/ape/apeproperties.h +++ b/3rdparty/taglib/ape/apeproperties.h @@ -51,26 +51,73 @@ namespace TagLib { public: /*! * Create an instance of APE::Properties with the data read from the - * ByteVector \a data. + * APE::File \a file. + * + * \deprecated */ - Properties(File *f, ReadStyle style = Average); + Properties(File *file, ReadStyle style = Average); + + /*! + * Create an instance of APE::Properties with the data read from the + * APE::File \a file. + */ + Properties(File *file, long streamLength, ReadStyle style = Average); /*! * Destroys this APE::Properties instance. */ virtual ~Properties(); - // Reimplementations. - + /*! + * Returns the length of the file in seconds. The length is rounded down to + * the nearest whole second. + * + * \note This method is just an alias of lengthInSeconds(). + * + * \deprecated + */ virtual int length() const; + + /*! + * Returns the length of the file in seconds. The length is rounded down to + * the nearest whole second. + * + * \see lengthInMilliseconds() + */ + // BIC: make virtual + int lengthInSeconds() const; + + /*! + * Returns the length of the file in milliseconds. + * + * \see lengthInSeconds() + */ + // BIC: make virtual + int lengthInMilliseconds() const; + + /*! + * Returns the average bit rate of the file in kb/s. + */ virtual int bitrate() const; + + /*! + * Returns the sample rate in Hz. + */ virtual int sampleRate() const; + + /*! + * Returns the number of audio channels. + */ virtual int channels() const; /*! - * Returns number of bits per sample. + * Returns the number of bits per audio sample. */ int bitsPerSample() const; + + /*! + * Returns the total number of audio samples in file. + */ uint sampleFrames() const; /*! @@ -82,13 +129,10 @@ namespace TagLib { Properties(const Properties &); Properties &operator=(const Properties &); - void read(); + void read(File *file, long streamLength); - long findDescriptor(); - long findID3v2(); - - void analyzeCurrent(); - void analyzeOld(); + void analyzeCurrent(File *file); + void analyzeOld(File *file); class PropertiesPrivate; PropertiesPrivate *d; diff --git a/3rdparty/taglib/ape/apetag.cpp b/3rdparty/taglib/ape/apetag.cpp index 664ee3a81..312533fee 100644 --- a/3rdparty/taglib/ape/apetag.cpp +++ b/3rdparty/taglib/ape/apetag.cpp @@ -46,11 +46,12 @@ using namespace APE; class APE::Tag::TagPrivate { public: - TagPrivate() : file(0), footerLocation(-1), tagLength(0) {} + TagPrivate() : + file(0), + footerLocation(-1) {} TagLib::File *file; long footerLocation; - long tagLength; Footer footer; @@ -61,14 +62,16 @@ public: // public methods //////////////////////////////////////////////////////////////////////////////// -APE::Tag::Tag() : TagLib::Tag() +APE::Tag::Tag() : + TagLib::Tag(), + d(new TagPrivate()) { - d = new TagPrivate; } -APE::Tag::Tag(TagLib::File *file, long footerLocation) : TagLib::Tag() +APE::Tag::Tag(TagLib::File *file, long footerLocation) : + TagLib::Tag(), + d(new TagPrivate()) { - d = new TagPrivate; d->file = file; d->footerLocation = footerLocation; @@ -89,35 +92,35 @@ String APE::Tag::title() const { if(d->itemListMap["TITLE"].isEmpty()) return String::null; - return d->itemListMap["TITLE"].toString(); + return d->itemListMap["TITLE"].values().toString(); } String APE::Tag::artist() const { if(d->itemListMap["ARTIST"].isEmpty()) return String::null; - return d->itemListMap["ARTIST"].toString(); + return d->itemListMap["ARTIST"].values().toString(); } String APE::Tag::album() const { if(d->itemListMap["ALBUM"].isEmpty()) return String::null; - return d->itemListMap["ALBUM"].toString(); + return d->itemListMap["ALBUM"].values().toString(); } String APE::Tag::comment() const { if(d->itemListMap["COMMENT"].isEmpty()) return String::null; - return d->itemListMap["COMMENT"].toString(); + return d->itemListMap["COMMENT"].values().toString(); } String APE::Tag::genre() const { if(d->itemListMap["GENRE"].isEmpty()) return String::null; - return d->itemListMap["GENRE"].toString(); + return d->itemListMap["GENRE"].values().toString(); } TagLib::uint APE::Tag::year() const @@ -233,7 +236,7 @@ PropertyMap APE::Tag::setProperties(const PropertyMap &origProps) toRemove.append(remIt->first); } - for (StringList::Iterator removeIt = toRemove.begin(); removeIt != toRemove.end(); removeIt++) + for(StringList::ConstIterator removeIt = toRemove.begin(); removeIt != toRemove.end(); removeIt++) removeItem(*removeIt); // now sync in the "forward direction" @@ -284,9 +287,7 @@ const APE::ItemListMap& APE::Tag::itemListMap() const void APE::Tag::removeItem(const String &key) { - Map::Iterator it = d->itemListMap.find(key.upper()); - if(it != d->itemListMap.end()) - d->itemListMap.erase(it); + d->itemListMap.erase(key.upper()); } void APE::Tag::addValue(const String &key, const String &value, bool replace) @@ -368,10 +369,13 @@ ByteVector APE::Tag::render() const void APE::Tag::parse(const ByteVector &data) { - uint pos = 0; - // 11 bytes is the minimum size for an APE item + if(data.size() < 11) + return; + + uint pos = 0; + for(uint i = 0; i < d->footer.itemCount() && pos <= data.size() - 11; i++) { APE::Item item; item.parse(data.mid(pos)); diff --git a/3rdparty/taglib/ape/apetag.h b/3rdparty/taglib/ape/apetag.h index c1f12e296..555726128 100644 --- a/3rdparty/taglib/ape/apetag.h +++ b/3rdparty/taglib/ape/apetag.h @@ -143,7 +143,7 @@ namespace TagLib { * Returns a reference to the item list map. This is an ItemListMap of * all of the items in the tag. * - * This is the most powerfull structure for accessing the items of the tag. + * This is the most powerful structure for accessing the items of the tag. * * APE tags are case-insensitive, all keys in this map have been converted * to upper case. diff --git a/3rdparty/taglib/asf/asfattribute.cpp b/3rdparty/taglib/asf/asfattribute.cpp index 4ee2d0a18..7a40bea3a 100644 --- a/3rdparty/taglib/asf/asfattribute.cpp +++ b/3rdparty/taglib/asf/asfattribute.cpp @@ -25,9 +25,11 @@ #include #include -#include "trefcounter.h" +#include + #include "asfattribute.h" #include "asffile.h" +#include "asfutils.h" using namespace TagLib; @@ -70,10 +72,12 @@ ASF::Attribute::Attribute(const ASF::Attribute &other) ASF::Attribute &ASF::Attribute::operator=(const ASF::Attribute &other) { - if(d->deref()) - delete d; - d = other.d; - d->ref(); + if(&other != this) { + if(d->deref()) + delete d; + d = other.d; + d->ref(); + } return *this; } @@ -181,23 +185,23 @@ String ASF::Attribute::parse(ASF::File &f, int kind) d->pictureValue = Picture::fromInvalid(); // extended content descriptor if(kind == 0) { - nameLength = f.readWORD(); - name = f.readString(nameLength); - d->type = ASF::Attribute::AttributeTypes(f.readWORD()); - size = f.readWORD(); + nameLength = readWORD(&f); + name = readString(&f, nameLength); + d->type = ASF::Attribute::AttributeTypes(readWORD(&f)); + size = readWORD(&f); } // metadata & metadata library else { - int temp = f.readWORD(); + int temp = readWORD(&f); // metadata library if(kind == 2) { d->language = temp; } - d->stream = f.readWORD(); - nameLength = f.readWORD(); - d->type = ASF::Attribute::AttributeTypes(f.readWORD()); - size = f.readDWORD(); - name = f.readString(nameLength); + d->stream = readWORD(&f); + nameLength = readWORD(&f); + d->type = ASF::Attribute::AttributeTypes(readWORD(&f)); + size = readDWORD(&f); + name = readString(&f, nameLength); } if(kind != 2 && size > 65535) { @@ -206,28 +210,28 @@ String ASF::Attribute::parse(ASF::File &f, int kind) switch(d->type) { case WordType: - d->shortValue = f.readWORD(); + d->shortValue = readWORD(&f); break; case BoolType: if(kind == 0) { - d->boolValue = f.readDWORD() == 1; + d->boolValue = (readDWORD(&f) == 1); } else { - d->boolValue = f.readWORD() == 1; + d->boolValue = (readWORD(&f) == 1); } break; case DWordType: - d->intValue = f.readDWORD(); + d->intValue = readDWORD(&f); break; case QWordType: - d->longLongValue = f.readQWORD(); + d->longLongValue = readQWORD(&f); break; case UnicodeType: - d->stringValue = f.readString(size); + d->stringValue = readString(&f, size); break; case BytesType: @@ -295,7 +299,7 @@ ByteVector ASF::Attribute::render(const String &name, int kind) const break; case UnicodeType: - data.append(File::renderString(d->stringValue)); + data.append(renderString(d->stringValue)); break; case BytesType: @@ -309,13 +313,13 @@ ByteVector ASF::Attribute::render(const String &name, int kind) const } if(kind == 0) { - data = File::renderString(name, true) + + data = renderString(name, true) + ByteVector::fromShort((int)d->type, false) + ByteVector::fromShort(data.size(), false) + data; } else { - ByteVector nameData = File::renderString(name); + ByteVector nameData = renderString(name); data = ByteVector::fromShort(kind == 2 ? d->language : 0, false) + ByteVector::fromShort(d->stream, false) + ByteVector::fromShort(nameData.size(), false) + diff --git a/3rdparty/taglib/asf/asffile.cpp b/3rdparty/taglib/asf/asffile.cpp index 241998ca8..e8a68d812 100644 --- a/3rdparty/taglib/asf/asffile.cpp +++ b/3rdparty/taglib/asf/asffile.cpp @@ -27,15 +27,28 @@ #include #include #include + #include "asffile.h" #include "asftag.h" #include "asfproperties.h" +#include "asfutils.h" using namespace TagLib; class ASF::File::FilePrivate { public: + class BaseObject; + class UnknownObject; + class FilePropertiesObject; + class StreamPropertiesObject; + class ContentDescriptionObject; + class ExtendedContentDescriptionObject; + class HeaderExtensionObject; + class CodecListObject; + class MetadataObject; + class MetadataLibraryObject; + FilePrivate(): size(0), tag(0), @@ -44,189 +57,227 @@ public: extendedContentDescriptionObject(0), headerExtensionObject(0), metadataObject(0), - metadataLibraryObject(0) {} + metadataLibraryObject(0) + { + objects.setAutoDelete(true); + } + + ~FilePrivate() + { + delete tag; + delete properties; + } + unsigned long long size; + ASF::Tag *tag; ASF::Properties *properties; - List objects; - ASF::File::ContentDescriptionObject *contentDescriptionObject; - ASF::File::ExtendedContentDescriptionObject *extendedContentDescriptionObject; - ASF::File::HeaderExtensionObject *headerExtensionObject; - ASF::File::MetadataObject *metadataObject; - ASF::File::MetadataLibraryObject *metadataLibraryObject; + + List objects; + + ContentDescriptionObject *contentDescriptionObject; + ExtendedContentDescriptionObject *extendedContentDescriptionObject; + HeaderExtensionObject *headerExtensionObject; + MetadataObject *metadataObject; + MetadataLibraryObject *metadataLibraryObject; }; -static ByteVector headerGuid("\x30\x26\xB2\x75\x8E\x66\xCF\x11\xA6\xD9\x00\xAA\x00\x62\xCE\x6C", 16); -static ByteVector filePropertiesGuid("\xA1\xDC\xAB\x8C\x47\xA9\xCF\x11\x8E\xE4\x00\xC0\x0C\x20\x53\x65", 16); -static ByteVector streamPropertiesGuid("\x91\x07\xDC\xB7\xB7\xA9\xCF\x11\x8E\xE6\x00\xC0\x0C\x20\x53\x65", 16); -static ByteVector contentDescriptionGuid("\x33\x26\xB2\x75\x8E\x66\xCF\x11\xA6\xD9\x00\xAA\x00\x62\xCE\x6C", 16); -static ByteVector extendedContentDescriptionGuid("\x40\xA4\xD0\xD2\x07\xE3\xD2\x11\x97\xF0\x00\xA0\xC9\x5E\xA8\x50", 16); -static ByteVector headerExtensionGuid("\xb5\x03\xbf_.\xa9\xcf\x11\x8e\xe3\x00\xc0\x0c Se", 16); -static ByteVector metadataGuid("\xEA\xCB\xF8\xC5\xAF[wH\204g\xAA\214D\xFAL\xCA", 16); -static ByteVector metadataLibraryGuid("\224\034#D\230\224\321I\241A\x1d\x13NEpT", 16); -static ByteVector contentEncryptionGuid("\xFB\xB3\x11\x22\x23\xBD\xD2\x11\xB4\xB7\x00\xA0\xC9\x55\xFC\x6E", 16); -static ByteVector extendedContentEncryptionGuid("\x14\xE6\x8A\x29\x22\x26 \x17\x4C\xB9\x35\xDA\xE0\x7E\xE9\x28\x9C", 16); -static ByteVector advancedContentEncryptionGuid("\xB6\x9B\x07\x7A\xA4\xDA\x12\x4E\xA5\xCA\x91\xD3\x8D\xC1\x1A\x8D", 16); +namespace +{ + const ByteVector headerGuid("\x30\x26\xB2\x75\x8E\x66\xCF\x11\xA6\xD9\x00\xAA\x00\x62\xCE\x6C", 16); + const ByteVector filePropertiesGuid("\xA1\xDC\xAB\x8C\x47\xA9\xCF\x11\x8E\xE4\x00\xC0\x0C\x20\x53\x65", 16); + const ByteVector streamPropertiesGuid("\x91\x07\xDC\xB7\xB7\xA9\xCF\x11\x8E\xE6\x00\xC0\x0C\x20\x53\x65", 16); + const ByteVector contentDescriptionGuid("\x33\x26\xB2\x75\x8E\x66\xCF\x11\xA6\xD9\x00\xAA\x00\x62\xCE\x6C", 16); + const ByteVector extendedContentDescriptionGuid("\x40\xA4\xD0\xD2\x07\xE3\xD2\x11\x97\xF0\x00\xA0\xC9\x5E\xA8\x50", 16); + const ByteVector headerExtensionGuid("\xb5\x03\xbf_.\xa9\xcf\x11\x8e\xe3\x00\xc0\x0c Se", 16); + const ByteVector metadataGuid("\xEA\xCB\xF8\xC5\xAF[wH\204g\xAA\214D\xFAL\xCA", 16); + const ByteVector metadataLibraryGuid("\224\034#D\230\224\321I\241A\x1d\x13NEpT", 16); + const ByteVector codecListGuid("\x40\x52\xd1\x86\x1d\x31\xd0\x11\xa3\xa4\x00\xa0\xc9\x03\x48\xf6", 16); + const ByteVector contentEncryptionGuid("\xFB\xB3\x11\x22\x23\xBD\xD2\x11\xB4\xB7\x00\xA0\xC9\x55\xFC\x6E", 16); + const ByteVector extendedContentEncryptionGuid("\x14\xE6\x8A\x29\x22\x26 \x17\x4C\xB9\x35\xDA\xE0\x7E\xE9\x28\x9C", 16); + const ByteVector advancedContentEncryptionGuid("\xB6\x9B\x07\x7A\xA4\xDA\x12\x4E\xA5\xCA\x91\xD3\x8D\xC1\x1A\x8D", 16); +} -class ASF::File::BaseObject +class ASF::File::FilePrivate::BaseObject { public: ByteVector data; virtual ~BaseObject() {} - virtual ByteVector guid() = 0; + virtual ByteVector guid() const = 0; virtual void parse(ASF::File *file, unsigned int size); virtual ByteVector render(ASF::File *file); }; -class ASF::File::UnknownObject : public ASF::File::BaseObject +class ASF::File::FilePrivate::UnknownObject : public ASF::File::FilePrivate::BaseObject { ByteVector myGuid; public: UnknownObject(const ByteVector &guid); - ByteVector guid(); + ByteVector guid() const; }; -class ASF::File::FilePropertiesObject : public ASF::File::BaseObject +class ASF::File::FilePrivate::FilePropertiesObject : public ASF::File::FilePrivate::BaseObject { public: - ByteVector guid(); + ByteVector guid() const; void parse(ASF::File *file, uint size); }; -class ASF::File::StreamPropertiesObject : public ASF::File::BaseObject +class ASF::File::FilePrivate::StreamPropertiesObject : public ASF::File::FilePrivate::BaseObject { public: - ByteVector guid(); + ByteVector guid() const; void parse(ASF::File *file, uint size); }; -class ASF::File::ContentDescriptionObject : public ASF::File::BaseObject +class ASF::File::FilePrivate::ContentDescriptionObject : public ASF::File::FilePrivate::BaseObject { public: - ByteVector guid(); + ByteVector guid() const; void parse(ASF::File *file, uint size); ByteVector render(ASF::File *file); }; -class ASF::File::ExtendedContentDescriptionObject : public ASF::File::BaseObject +class ASF::File::FilePrivate::ExtendedContentDescriptionObject : public ASF::File::FilePrivate::BaseObject { public: ByteVectorList attributeData; - ByteVector guid(); + ByteVector guid() const; void parse(ASF::File *file, uint size); ByteVector render(ASF::File *file); }; -class ASF::File::MetadataObject : public ASF::File::BaseObject +class ASF::File::FilePrivate::MetadataObject : public ASF::File::FilePrivate::BaseObject { public: ByteVectorList attributeData; - ByteVector guid(); + ByteVector guid() const; void parse(ASF::File *file, uint size); ByteVector render(ASF::File *file); }; -class ASF::File::MetadataLibraryObject : public ASF::File::BaseObject +class ASF::File::FilePrivate::MetadataLibraryObject : public ASF::File::FilePrivate::BaseObject { public: ByteVectorList attributeData; - ByteVector guid(); + ByteVector guid() const; void parse(ASF::File *file, uint size); ByteVector render(ASF::File *file); }; -class ASF::File::HeaderExtensionObject : public ASF::File::BaseObject +class ASF::File::FilePrivate::HeaderExtensionObject : public ASF::File::FilePrivate::BaseObject { public: - List objects; - ~HeaderExtensionObject(); - ByteVector guid(); + List objects; + HeaderExtensionObject(); + ByteVector guid() const; void parse(ASF::File *file, uint size); ByteVector render(ASF::File *file); }; -ASF::File::HeaderExtensionObject::~HeaderExtensionObject() +class ASF::File::FilePrivate::CodecListObject : public ASF::File::FilePrivate::BaseObject { - for(unsigned int i = 0; i < objects.size(); i++) { - delete objects[i]; - } -} +public: + ByteVector guid() const; + void parse(ASF::File *file, uint size); -void ASF::File::BaseObject::parse(ASF::File *file, unsigned int size) +private: + enum CodecType + { + Video = 0x0001, + Audio = 0x0002, + Unknown = 0xFFFF + }; +}; + +void ASF::File::FilePrivate::BaseObject::parse(ASF::File *file, unsigned int size) { data.clear(); - if (size > 24 && size <= (unsigned int)(file->length())) + if(size > 24 && size <= (unsigned int)(file->length())) data = file->readBlock(size - 24); else data = ByteVector::null; } -ByteVector ASF::File::BaseObject::render(ASF::File * /*file*/) +ByteVector ASF::File::FilePrivate::BaseObject::render(ASF::File * /*file*/) { return guid() + ByteVector::fromLongLong(data.size() + 24, false) + data; } -ASF::File::UnknownObject::UnknownObject(const ByteVector &guid) : myGuid(guid) +ASF::File::FilePrivate::UnknownObject::UnknownObject(const ByteVector &guid) : myGuid(guid) { } -ByteVector ASF::File::UnknownObject::guid() +ByteVector ASF::File::FilePrivate::UnknownObject::guid() const { return myGuid; } -ByteVector ASF::File::FilePropertiesObject::guid() +ByteVector ASF::File::FilePrivate::FilePropertiesObject::guid() const { return filePropertiesGuid; } -void ASF::File::FilePropertiesObject::parse(ASF::File *file, uint size) +void ASF::File::FilePrivate::FilePropertiesObject::parse(ASF::File *file, uint size) { BaseObject::parse(file, size); - file->d->properties->setLength( - (int)(data.toLongLong(40, false) / 10000000L - data.toLongLong(56, false) / 1000L)); + if(data.size() < 64) { + debug("ASF::File::FilePrivate::FilePropertiesObject::parse() -- data is too short."); + return; + } + + const long long duration = data.toLongLong(40, false); + const long long preroll = data.toLongLong(56, false); + file->d->properties->setLengthInMilliseconds(static_cast(duration / 10000.0 - preroll + 0.5)); } -ByteVector ASF::File::StreamPropertiesObject::guid() +ByteVector ASF::File::FilePrivate::StreamPropertiesObject::guid() const { return streamPropertiesGuid; } -void ASF::File::StreamPropertiesObject::parse(ASF::File *file, uint size) +void ASF::File::FilePrivate::StreamPropertiesObject::parse(ASF::File *file, uint size) { BaseObject::parse(file, size); - file->d->properties->setChannels(data.toShort(56, false)); + if(data.size() < 70) { + debug("ASF::File::FilePrivate::StreamPropertiesObject::parse() -- data is too short."); + return; + } + + file->d->properties->setCodec(data.toUShort(54, false)); + file->d->properties->setChannels(data.toUShort(56, false)); file->d->properties->setSampleRate(data.toUInt(58, false)); - file->d->properties->setBitrate(data.toUInt(62, false) * 8 / 1000); + file->d->properties->setBitrate(static_cast(data.toUInt(62, false) * 8.0 / 1000.0 + 0.5)); + file->d->properties->setBitsPerSample(data.toUShort(68, false)); } -ByteVector ASF::File::ContentDescriptionObject::guid() +ByteVector ASF::File::FilePrivate::ContentDescriptionObject::guid() const { return contentDescriptionGuid; } -void ASF::File::ContentDescriptionObject::parse(ASF::File *file, uint /*size*/) +void ASF::File::FilePrivate::ContentDescriptionObject::parse(ASF::File *file, uint /*size*/) { file->d->contentDescriptionObject = this; - int titleLength = file->readWORD(); - int artistLength = file->readWORD(); - int copyrightLength = file->readWORD(); - int commentLength = file->readWORD(); - int ratingLength = file->readWORD(); - file->d->tag->setTitle(file->readString(titleLength)); - file->d->tag->setArtist(file->readString(artistLength)); - file->d->tag->setCopyright(file->readString(copyrightLength)); - file->d->tag->setComment(file->readString(commentLength)); - file->d->tag->setRating(file->readString(ratingLength)); + const int titleLength = readWORD(file); + const int artistLength = readWORD(file); + const int copyrightLength = readWORD(file); + const int commentLength = readWORD(file); + const int ratingLength = readWORD(file); + file->d->tag->setTitle(readString(file,titleLength)); + file->d->tag->setArtist(readString(file,artistLength)); + file->d->tag->setCopyright(readString(file,copyrightLength)); + file->d->tag->setComment(readString(file,commentLength)); + file->d->tag->setRating(readString(file,ratingLength)); } -ByteVector ASF::File::ContentDescriptionObject::render(ASF::File *file) +ByteVector ASF::File::FilePrivate::ContentDescriptionObject::render(ASF::File *file) { - ByteVector v1 = file->renderString(file->d->tag->title()); - ByteVector v2 = file->renderString(file->d->tag->artist()); - ByteVector v3 = file->renderString(file->d->tag->copyright()); - ByteVector v4 = file->renderString(file->d->tag->comment()); - ByteVector v5 = file->renderString(file->d->tag->rating()); + const ByteVector v1 = renderString(file->d->tag->title()); + const ByteVector v2 = renderString(file->d->tag->artist()); + const ByteVector v3 = renderString(file->d->tag->copyright()); + const ByteVector v4 = renderString(file->d->tag->comment()); + const ByteVector v5 = renderString(file->d->tag->rating()); data.clear(); data.append(ByteVector::fromShort(v1.size(), false)); data.append(ByteVector::fromShort(v2.size(), false)); @@ -241,15 +292,15 @@ ByteVector ASF::File::ContentDescriptionObject::render(ASF::File *file) return BaseObject::render(file); } -ByteVector ASF::File::ExtendedContentDescriptionObject::guid() +ByteVector ASF::File::FilePrivate::ExtendedContentDescriptionObject::guid() const { return extendedContentDescriptionGuid; } -void ASF::File::ExtendedContentDescriptionObject::parse(ASF::File *file, uint /*size*/) +void ASF::File::FilePrivate::ExtendedContentDescriptionObject::parse(ASF::File *file, uint /*size*/) { file->d->extendedContentDescriptionObject = this; - int count = file->readWORD(); + int count = readWORD(file); while(count--) { ASF::Attribute attribute; String name = attribute.parse(*file); @@ -257,7 +308,7 @@ void ASF::File::ExtendedContentDescriptionObject::parse(ASF::File *file, uint /* } } -ByteVector ASF::File::ExtendedContentDescriptionObject::render(ASF::File *file) +ByteVector ASF::File::FilePrivate::ExtendedContentDescriptionObject::render(ASF::File *file) { data.clear(); data.append(ByteVector::fromShort(attributeData.size(), false)); @@ -265,15 +316,15 @@ ByteVector ASF::File::ExtendedContentDescriptionObject::render(ASF::File *file) return BaseObject::render(file); } -ByteVector ASF::File::MetadataObject::guid() +ByteVector ASF::File::FilePrivate::MetadataObject::guid() const { return metadataGuid; } -void ASF::File::MetadataObject::parse(ASF::File *file, uint /*size*/) +void ASF::File::FilePrivate::MetadataObject::parse(ASF::File *file, uint /*size*/) { file->d->metadataObject = this; - int count = file->readWORD(); + int count = readWORD(file); while(count--) { ASF::Attribute attribute; String name = attribute.parse(*file, 1); @@ -281,7 +332,7 @@ void ASF::File::MetadataObject::parse(ASF::File *file, uint /*size*/) } } -ByteVector ASF::File::MetadataObject::render(ASF::File *file) +ByteVector ASF::File::FilePrivate::MetadataObject::render(ASF::File *file) { data.clear(); data.append(ByteVector::fromShort(attributeData.size(), false)); @@ -289,15 +340,15 @@ ByteVector ASF::File::MetadataObject::render(ASF::File *file) return BaseObject::render(file); } -ByteVector ASF::File::MetadataLibraryObject::guid() +ByteVector ASF::File::FilePrivate::MetadataLibraryObject::guid() const { return metadataLibraryGuid; } -void ASF::File::MetadataLibraryObject::parse(ASF::File *file, uint /*size*/) +void ASF::File::FilePrivate::MetadataLibraryObject::parse(ASF::File *file, uint /*size*/) { file->d->metadataLibraryObject = this; - int count = file->readWORD(); + int count = readWORD(file); while(count--) { ASF::Attribute attribute; String name = attribute.parse(*file, 2); @@ -305,7 +356,7 @@ void ASF::File::MetadataLibraryObject::parse(ASF::File *file, uint /*size*/) } } -ByteVector ASF::File::MetadataLibraryObject::render(ASF::File *file) +ByteVector ASF::File::FilePrivate::MetadataLibraryObject::render(ASF::File *file) { data.clear(); data.append(ByteVector::fromShort(attributeData.size(), false)); @@ -313,16 +364,21 @@ ByteVector ASF::File::MetadataLibraryObject::render(ASF::File *file) return BaseObject::render(file); } -ByteVector ASF::File::HeaderExtensionObject::guid() +ASF::File::FilePrivate::HeaderExtensionObject::HeaderExtensionObject() +{ + objects.setAutoDelete(true); +} + +ByteVector ASF::File::FilePrivate::HeaderExtensionObject::guid() const { return headerExtensionGuid; } -void ASF::File::HeaderExtensionObject::parse(ASF::File *file, uint /*size*/) +void ASF::File::FilePrivate::HeaderExtensionObject::parse(ASF::File *file, uint /*size*/) { file->d->headerExtensionObject = this; file->seek(18, File::Current); - long long dataSize = file->readDWORD(); + long long dataSize = readDWORD(file); long long dataPos = 0; while(dataPos < dataSize) { ByteVector guid = file->readBlock(16); @@ -331,7 +387,7 @@ void ASF::File::HeaderExtensionObject::parse(ASF::File *file, uint /*size*/) break; } bool ok; - long long size = file->readQWORD(&ok); + long long size = readQWORD(file, &ok); if(!ok) { file->setValid(false); break; @@ -352,47 +408,93 @@ void ASF::File::HeaderExtensionObject::parse(ASF::File *file, uint /*size*/) } } -ByteVector ASF::File::HeaderExtensionObject::render(ASF::File *file) +ByteVector ASF::File::FilePrivate::HeaderExtensionObject::render(ASF::File *file) { data.clear(); - for(unsigned int i = 0; i < objects.size(); i++) { - data.append(objects[i]->render(file)); + for(List::ConstIterator it = objects.begin(); it != objects.end(); ++it) { + data.append((*it)->render(file)); } data = ByteVector("\x11\xD2\xD3\xAB\xBA\xA9\xcf\x11\x8E\xE6\x00\xC0\x0C\x20\x53\x65\x06\x00", 18) + ByteVector::fromUInt(data.size(), false) + data; return BaseObject::render(file); } +ByteVector ASF::File::FilePrivate::CodecListObject::guid() const +{ + return codecListGuid; +} + +void ASF::File::FilePrivate::CodecListObject::parse(ASF::File *file, uint size) +{ + BaseObject::parse(file, size); + if(data.size() <= 20) { + debug("ASF::File::FilePrivate::CodecListObject::parse() -- data is too short."); + return; + } + + uint pos = 16; + + const int count = data.toUInt(pos, false); + pos += 4; + + for(int i = 0; i < count; ++i) { + + if(pos >= data.size()) + break; + + const CodecType type = static_cast(data.toUShort(pos, false)); + pos += 2; + + int nameLength = data.toUShort(pos, false); + pos += 2; + + const uint namePos = pos; + pos += nameLength * 2; + + const int descLength = data.toUShort(pos, false); + pos += 2; + + const uint descPos = pos; + pos += descLength * 2; + + const int infoLength = data.toUShort(pos, false); + pos += 2 + infoLength * 2; + + if(type == CodecListObject::Audio) { + // First audio codec found. + + const String name(data.mid(namePos, nameLength * 2), String::UTF16LE); + file->d->properties->setCodecName(name.stripWhiteSpace()); + + const String desc(data.mid(descPos, descLength * 2), String::UTF16LE); + file->d->properties->setCodecDescription(desc.stripWhiteSpace()); + + break; + } + } +} + //////////////////////////////////////////////////////////////////////////////// // public members //////////////////////////////////////////////////////////////////////////////// -ASF::File::File(FileName file, bool readProperties, Properties::ReadStyle propertiesStyle) - : TagLib::File(file) +ASF::File::File(FileName file, bool, Properties::ReadStyle) : + TagLib::File(file), + d(new FilePrivate()) { - d = new FilePrivate; if(isOpen()) - read(readProperties, propertiesStyle); + read(); } -ASF::File::File(IOStream *stream, bool readProperties, Properties::ReadStyle propertiesStyle) - : TagLib::File(stream) +ASF::File::File(IOStream *stream, bool, Properties::ReadStyle) : + TagLib::File(stream), + d(new FilePrivate()) { - d = new FilePrivate; if(isOpen()) - read(readProperties, propertiesStyle); + read(); } ASF::File::~File() { - for(unsigned int i = 0; i < d->objects.size(); i++) { - delete d->objects[i]; - } - if(d->tag) { - delete d->tag; - } - if(d->properties) { - delete d->properties; - } delete d; } @@ -421,7 +523,85 @@ ASF::Properties *ASF::File::audioProperties() const return d->properties; } -void ASF::File::read(bool /*readProperties*/, Properties::ReadStyle /*propertiesStyle*/) +bool ASF::File::save() +{ + if(readOnly()) { + debug("ASF::File::save() -- File is read only."); + return false; + } + + if(!isValid()) { + debug("ASF::File::save() -- Trying to save invalid file."); + return false; + } + + if(!d->contentDescriptionObject) { + d->contentDescriptionObject = new FilePrivate::ContentDescriptionObject(); + d->objects.append(d->contentDescriptionObject); + } + if(!d->extendedContentDescriptionObject) { + d->extendedContentDescriptionObject = new FilePrivate::ExtendedContentDescriptionObject(); + d->objects.append(d->extendedContentDescriptionObject); + } + if(!d->headerExtensionObject) { + d->headerExtensionObject = new FilePrivate::HeaderExtensionObject(); + d->objects.append(d->headerExtensionObject); + } + if(!d->metadataObject) { + d->metadataObject = new FilePrivate::MetadataObject(); + d->headerExtensionObject->objects.append(d->metadataObject); + } + if(!d->metadataLibraryObject) { + d->metadataLibraryObject = new FilePrivate::MetadataLibraryObject(); + d->headerExtensionObject->objects.append(d->metadataLibraryObject); + } + + const AttributeListMap allAttributes = d->tag->attributeListMap(); + + for(AttributeListMap::ConstIterator it = allAttributes.begin(); it != allAttributes.end(); ++it) { + + const String &name = it->first; + const AttributeList &attributes = it->second; + + bool inExtendedContentDescriptionObject = false; + bool inMetadataObject = false; + + for(AttributeList::ConstIterator jt = attributes.begin(); jt != attributes.end(); ++jt) { + + const Attribute &attribute = *jt; + const bool largeValue = (attribute.dataSize() > 65535); + const bool guid = (attribute.type() == Attribute::GuidType); + + if(!inExtendedContentDescriptionObject && !guid && !largeValue && attribute.language() == 0 && attribute.stream() == 0) { + d->extendedContentDescriptionObject->attributeData.append(attribute.render(name)); + inExtendedContentDescriptionObject = true; + } + else if(!inMetadataObject && !guid && !largeValue && attribute.language() == 0 && attribute.stream() != 0) { + d->metadataObject->attributeData.append(attribute.render(name, 1)); + inMetadataObject = true; + } + else { + d->metadataLibraryObject->attributeData.append(attribute.render(name, 2)); + } + } + } + + ByteVector data; + for(List::ConstIterator it = d->objects.begin(); it != d->objects.end(); ++it) { + data.append((*it)->render(this)); + } + + data = headerGuid + ByteVector::fromLongLong(data.size() + 30, false) + ByteVector::fromUInt(d->objects.size(), false) + ByteVector("\x01\x02", 2) + data; + insert(data, 0, (TagLib::ulong)d->size); + + return true; +} + +//////////////////////////////////////////////////////////////////////////////// +// private members +//////////////////////////////////////////////////////////////////////////////// + +void ASF::File::read() { if(!isValid()) return; @@ -437,12 +617,12 @@ void ASF::File::read(bool /*readProperties*/, Properties::ReadStyle /*properties d->properties = new ASF::Properties(); bool ok; - d->size = readQWORD(&ok); + d->size = readQWORD(this, &ok); if(!ok) { setValid(false); return; } - int numObjects = readDWORD(&ok); + int numObjects = readDWORD(this, &ok); if(!ok) { setValid(false); return; @@ -450,31 +630,34 @@ void ASF::File::read(bool /*readProperties*/, Properties::ReadStyle /*properties seek(2, Current); for(int i = 0; i < numObjects; i++) { - ByteVector guid = readBlock(16); + guid = readBlock(16); if(guid.size() != 16) { setValid(false); break; } - long size = (long)readQWORD(&ok); + long size = (long)readQWORD(this, &ok); if(!ok) { setValid(false); break; } - BaseObject *obj; + FilePrivate::BaseObject *obj; if(guid == filePropertiesGuid) { - obj = new FilePropertiesObject(); + obj = new FilePrivate::FilePropertiesObject(); } else if(guid == streamPropertiesGuid) { - obj = new StreamPropertiesObject(); + obj = new FilePrivate::StreamPropertiesObject(); } else if(guid == contentDescriptionGuid) { - obj = new ContentDescriptionObject(); + obj = new FilePrivate::ContentDescriptionObject(); } else if(guid == extendedContentDescriptionGuid) { - obj = new ExtendedContentDescriptionObject(); + obj = new FilePrivate::ExtendedContentDescriptionObject(); } else if(guid == headerExtensionGuid) { - obj = new HeaderExtensionObject(); + obj = new FilePrivate::HeaderExtensionObject(); + } + else if(guid == codecListGuid) { + obj = new FilePrivate::CodecListObject(); } else { if(guid == contentEncryptionGuid || @@ -482,149 +665,9 @@ void ASF::File::read(bool /*readProperties*/, Properties::ReadStyle /*properties guid == advancedContentEncryptionGuid) { d->properties->setEncrypted(true); } - obj = new UnknownObject(guid); + obj = new FilePrivate::UnknownObject(guid); } obj->parse(this, size); d->objects.append(obj); } } - -bool ASF::File::save() -{ - if(readOnly()) { - debug("ASF::File::save() -- File is read only."); - return false; - } - - if(!isValid()) { - debug("ASF::File::save() -- Trying to save invalid file."); - return false; - } - - if(!d->contentDescriptionObject) { - d->contentDescriptionObject = new ContentDescriptionObject(); - d->objects.append(d->contentDescriptionObject); - } - if(!d->extendedContentDescriptionObject) { - d->extendedContentDescriptionObject = new ExtendedContentDescriptionObject(); - d->objects.append(d->extendedContentDescriptionObject); - } - if(!d->headerExtensionObject) { - d->headerExtensionObject = new HeaderExtensionObject(); - d->objects.append(d->headerExtensionObject); - } - if(!d->metadataObject) { - d->metadataObject = new MetadataObject(); - d->headerExtensionObject->objects.append(d->metadataObject); - } - if(!d->metadataLibraryObject) { - d->metadataLibraryObject = new MetadataLibraryObject(); - d->headerExtensionObject->objects.append(d->metadataLibraryObject); - } - - ASF::AttributeListMap::ConstIterator it = d->tag->attributeListMap().begin(); - for(; it != d->tag->attributeListMap().end(); it++) { - const String &name = it->first; - const AttributeList &attributes = it->second; - bool inExtendedContentDescriptionObject = false; - bool inMetadataObject = false; - for(unsigned int j = 0; j < attributes.size(); j++) { - const Attribute &attribute = attributes[j]; - bool largeValue = attribute.dataSize() > 65535; - if(!inExtendedContentDescriptionObject && !largeValue && attribute.language() == 0 && attribute.stream() == 0) { - d->extendedContentDescriptionObject->attributeData.append(attribute.render(name)); - inExtendedContentDescriptionObject = true; - } - else if(!inMetadataObject && !largeValue && attribute.language() == 0 && attribute.stream() != 0) { - d->metadataObject->attributeData.append(attribute.render(name, 1)); - inMetadataObject = true; - } - else { - d->metadataLibraryObject->attributeData.append(attribute.render(name, 2)); - } - } - } - - ByteVector data; - for(unsigned int i = 0; i < d->objects.size(); i++) { - data.append(d->objects[i]->render(this)); - } - data = headerGuid + ByteVector::fromLongLong(data.size() + 30, false) + ByteVector::fromUInt(d->objects.size(), false) + ByteVector("\x01\x02", 2) + data; - insert(data, 0, (TagLib::ulong)d->size); - - return true; -} - -//////////////////////////////////////////////////////////////////////////////// -// protected members -//////////////////////////////////////////////////////////////////////////////// - -int ASF::File::readBYTE(bool *ok) -{ - ByteVector v = readBlock(1); - if(v.size() != 1) { - if(ok) *ok = false; - return 0; - } - if(ok) *ok = true; - return v[0]; -} - -int ASF::File::readWORD(bool *ok) -{ - ByteVector v = readBlock(2); - if(v.size() != 2) { - if(ok) *ok = false; - return 0; - } - if(ok) *ok = true; - return v.toUShort(false); -} - -unsigned int ASF::File::readDWORD(bool *ok) -{ - ByteVector v = readBlock(4); - if(v.size() != 4) { - if(ok) *ok = false; - return 0; - } - if(ok) *ok = true; - return v.toUInt(false); -} - -long long ASF::File::readQWORD(bool *ok) -{ - ByteVector v = readBlock(8); - if(v.size() != 8) { - if(ok) *ok = false; - return 0; - } - if(ok) *ok = true; - return v.toLongLong(false); -} - -String ASF::File::readString(int length) -{ - ByteVector data = readBlock(length); - unsigned int size = data.size(); - while (size >= 2) { - if(data[size - 1] != '\0' || data[size - 2] != '\0') { - break; - } - size -= 2; - } - if(size != data.size()) { - data.resize(size); - } - return String(data, String::UTF16LE); -} - -ByteVector ASF::File::renderString(const String &str, bool includeLength) -{ - ByteVector data = str.data(String::UTF16LE) + ByteVector::fromShort(0, false); - if(includeLength) { - data = ByteVector::fromShort(data.size(), false) + data; - } - return data; -} - diff --git a/3rdparty/taglib/asf/asffile.h b/3rdparty/taglib/asf/asffile.h index 5ccf2fdeb..f1ae431fe 100644 --- a/3rdparty/taglib/asf/asffile.h +++ b/3rdparty/taglib/asf/asffile.h @@ -54,7 +54,7 @@ namespace TagLib { * \a propertiesStyle are ignored. The audio properties are always * read. */ - File(FileName file, bool readProperties = true, + File(FileName file, bool readProperties = true, Properties::ReadStyle propertiesStyle = Properties::Average); /*! @@ -67,7 +67,7 @@ namespace TagLib { * \note TagLib will *not* take ownership of the stream, the caller is * responsible for deleting it after the File object. */ - File(IOStream *stream, bool readProperties = true, + File(IOStream *stream, bool readProperties = true, Properties::ReadStyle propertiesStyle = Properties::Average); /*! @@ -112,30 +112,14 @@ namespace TagLib { * Save the file. * * This returns true if the save was successful. + * + * \warning In the current implementation, it's dangerous to call save() + * repeatedly. At worst it will corrupt the file. */ virtual bool save(); private: - int readBYTE(bool *ok = 0); - int readWORD(bool *ok = 0); - unsigned int readDWORD(bool *ok = 0); - long long readQWORD(bool *ok = 0); - static ByteVector renderString(const String &str, bool includeLength = false); - String readString(int len); - void read(bool readProperties, Properties::ReadStyle propertiesStyle); - - friend class Attribute; - friend class Picture; - - class BaseObject; - class UnknownObject; - class FilePropertiesObject; - class StreamPropertiesObject; - class ContentDescriptionObject; - class ExtendedContentDescriptionObject; - class HeaderExtensionObject; - class MetadataObject; - class MetadataLibraryObject; + void read(); class FilePrivate; FilePrivate *d; diff --git a/3rdparty/taglib/asf/asfpicture.cpp b/3rdparty/taglib/asf/asfpicture.cpp index 999f92043..cdf6e7585 100644 --- a/3rdparty/taglib/asf/asfpicture.cpp +++ b/3rdparty/taglib/asf/asfpicture.cpp @@ -25,10 +25,12 @@ #include #include -#include "trefcounter.h" +#include + #include "asfattribute.h" #include "asffile.h" #include "asfpicture.h" +#include "asfutils.h" using namespace TagLib; @@ -134,8 +136,8 @@ ByteVector ASF::Picture::render() const return ByteVector((char)d->type) + ByteVector::fromUInt(d->picture.size(), false) + - ASF::File::renderString(d->mimeType) + - ASF::File::renderString(d->description) + + renderString(d->mimeType) + + renderString(d->description) + d->picture; } diff --git a/3rdparty/taglib/asf/asfpicture.h b/3rdparty/taglib/asf/asfpicture.h index aa0a060c1..b510c35f1 100644 --- a/3rdparty/taglib/asf/asfpicture.h +++ b/3rdparty/taglib/asf/asfpicture.h @@ -205,8 +205,8 @@ namespace TagLib /* THIS IS PRIVATE, DON'T TOUCH IT! */ void parse(const ByteVector& ); static Picture fromInvalid(); - friend class Attribute; #endif + private: class PicturePrivate; PicturePrivate *d; diff --git a/3rdparty/taglib/asf/asfproperties.cpp b/3rdparty/taglib/asf/asfproperties.cpp index b82c131a7..a836da30b 100644 --- a/3rdparty/taglib/asf/asfproperties.cpp +++ b/3rdparty/taglib/asf/asfproperties.cpp @@ -32,11 +32,23 @@ using namespace TagLib; class ASF::Properties::PropertiesPrivate { public: - PropertiesPrivate(): length(0), bitrate(0), sampleRate(0), channels(0), encrypted(false) {} + PropertiesPrivate() : + length(0), + bitrate(0), + sampleRate(0), + channels(0), + bitsPerSample(0), + codec(ASF::Properties::Unknown), + encrypted(false) {} + int length; int bitrate; int sampleRate; int channels; + int bitsPerSample; + ASF::Properties::Codec codec; + String codecName; + String codecDescription; bool encrypted; }; @@ -44,18 +56,28 @@ public: // public members //////////////////////////////////////////////////////////////////////////////// -ASF::Properties::Properties() : AudioProperties(AudioProperties::Average) +ASF::Properties::Properties() : + AudioProperties(AudioProperties::Average), + d(new PropertiesPrivate()) { - d = new PropertiesPrivate; } ASF::Properties::~Properties() { - if(d) - delete d; + delete d; } int ASF::Properties::length() const +{ + return lengthInSeconds(); +} + +int ASF::Properties::lengthInSeconds() const +{ + return d->length / 1000; +} + +int ASF::Properties::lengthInMilliseconds() const { return d->length; } @@ -75,6 +97,26 @@ int ASF::Properties::channels() const return d->channels; } +int ASF::Properties::bitsPerSample() const +{ + return d->bitsPerSample; +} + +ASF::Properties::Codec ASF::Properties::codec() const +{ + return d->codec; +} + +String ASF::Properties::codecName() const +{ + return d->codecName; +} + +String ASF::Properties::codecDescription() const +{ + return d->codecDescription; +} + bool ASF::Properties::isEncrypted() const { return d->encrypted; @@ -84,28 +126,69 @@ bool ASF::Properties::isEncrypted() const // private members //////////////////////////////////////////////////////////////////////////////// -void ASF::Properties::setLength(int length) +void ASF::Properties::setLength(int /*length*/) { - d->length = length; + debug("ASF::Properties::setLength() -- This method is deprecated. Do not use."); } -void ASF::Properties::setBitrate(int length) +void ASF::Properties::setLengthInMilliseconds(int value) { - d->bitrate = length; + d->length = value; } -void ASF::Properties::setSampleRate(int length) +void ASF::Properties::setBitrate(int value) { - d->sampleRate = length; + d->bitrate = value; } -void ASF::Properties::setChannels(int length) +void ASF::Properties::setSampleRate(int value) { - d->channels = length; + d->sampleRate = value; } -void ASF::Properties::setEncrypted(bool encrypted) +void ASF::Properties::setChannels(int value) { - d->encrypted = encrypted; + d->channels = value; } +void ASF::Properties::setBitsPerSample(int value) +{ + d->bitsPerSample = value; +} + +void ASF::Properties::setCodec(int value) +{ + switch(value) + { + case 0x0160: + d->codec = WMA1; + break; + case 0x0161: + d->codec = WMA2; + break; + case 0x0162: + d->codec = WMA9Pro; + break; + case 0x0163: + d->codec = WMA9Lossless; + break; + default: + d->codec = Unknown; + break; + } +} + +void ASF::Properties::setCodecName(const String &value) +{ + d->codecName = value; +} + +void ASF::Properties::setCodecDescription(const String &value) +{ + d->codecDescription = value; +} + +void ASF::Properties::setEncrypted(bool value) +{ + d->encrypted = value; +} diff --git a/3rdparty/taglib/asf/asfproperties.h b/3rdparty/taglib/asf/asfproperties.h index 95730d8ea..b89349b36 100644 --- a/3rdparty/taglib/asf/asfproperties.h +++ b/3rdparty/taglib/asf/asfproperties.h @@ -40,7 +40,38 @@ namespace TagLib { public: /*! - * Create an instance of ASF::Properties. + * Audio codec types can be used in ASF file. + */ + enum Codec + { + /*! + * Couldn't detect the codec. + */ + Unknown = 0, + + /*! + * Windows Media Audio 1 + */ + WMA1, + + /*! + * Windows Media Audio 2 or above + */ + WMA2, + + /*! + * Windows Media Audio 9 Professional + */ + WMA9Pro, + + /*! + * Windows Media Audio 9 Lossless + */ + WMA9Lossless, + }; + + /*! + * Creates an instance of ASF::Properties. */ Properties(); @@ -49,18 +80,97 @@ namespace TagLib { */ virtual ~Properties(); - // Reimplementations. + /*! + * Returns the length of the file in seconds. The length is rounded down to + * the nearest whole second. + * + * \note This method is just an alias of lengthInSeconds(). + * + * \deprecated + */ virtual int length() const; + + /*! + * Returns the length of the file in seconds. The length is rounded down to + * the nearest whole second. + * + * \see lengthInMilliseconds() + */ + // BIC: make virtual + int lengthInSeconds() const; + + /*! + * Returns the length of the file in milliseconds. + * + * \see lengthInSeconds() + */ + // BIC: make virtual + int lengthInMilliseconds() const; + + /*! + * Returns the average bit rate of the file in kb/s. + */ virtual int bitrate() const; + + /*! + * Returns the sample rate in Hz. + */ virtual int sampleRate() const; + + /*! + * Returns the number of audio channels. + */ virtual int channels() const; + + /*! + * Returns the number of bits per audio sample. + */ + int bitsPerSample() const; + + /*! + * Returns the codec used in the file. + * + * \see codecName() + * \see codecDescription() + */ + Codec codec() const; + + /*! + * Returns the concrete codec name, for example "Windows Media Audio 9.1" + * used in the file if available, otherwise an empty string. + * + * \see codec() + * \see codecDescription() + */ + String codecName() const; + + /*! + * Returns the codec description, typically contains the encoder settings, + * for example "VBR Quality 50, 44kHz, stereo 1-pass VBR" if available, + * otherwise an empty string. + * + * \see codec() + * \see codecName() + */ + String codecDescription() const; + + /*! + * Returns whether or not the file is encrypted. + */ bool isEncrypted() const; #ifndef DO_NOT_DOCUMENT + // deprecated void setLength(int value); + + void setLengthInMilliseconds(int value); void setBitrate(int value); void setSampleRate(int value); void setChannels(int value); + void setBitsPerSample(int value); + void setCodec(int value); + void setCodecName(const String &value); + void setCodecDescription(const String &value); void setEncrypted(bool value); #endif diff --git a/3rdparty/taglib/asf/asftag.cpp b/3rdparty/taglib/asf/asftag.cpp index c07abe94d..bdf3c5dad 100644 --- a/3rdparty/taglib/asf/asftag.cpp +++ b/3rdparty/taglib/asf/asftag.cpp @@ -47,8 +47,7 @@ ASF::Tag::Tag() ASF::Tag::~Tag() { - if(d) - delete d; + delete d; } String ASF::Tag::title() const @@ -161,11 +160,24 @@ ASF::AttributeListMap& ASF::Tag::attributeListMap() return d->attributeListMap; } +const ASF::AttributeListMap &ASF::Tag::attributeListMap() const +{ + return d->attributeListMap; +} + +bool ASF::Tag::contains(const String &key) const +{ + return d->attributeListMap.contains(key); +} + void ASF::Tag::removeItem(const String &key) { - AttributeListMap::Iterator it = d->attributeListMap.find(key); - if(it != d->attributeListMap.end()) - d->attributeListMap.erase(it); + d->attributeListMap.erase(key); +} + +ASF::AttributeList ASF::Tag::attribute(const String &name) const +{ + return d->attributeListMap[name]; } void ASF::Tag::setAttribute(const String &name, const Attribute &attribute) @@ -175,6 +187,11 @@ void ASF::Tag::setAttribute(const String &name, const Attribute &attribute) d->attributeListMap.insert(name, value); } +void ASF::Tag::setAttribute(const String &name, const AttributeList &values) +{ + d->attributeListMap.insert(name, values); +} + void ASF::Tag::addAttribute(const String &name, const Attribute &attribute) { if(d->attributeListMap.contains(name)) { @@ -195,6 +212,7 @@ bool ASF::Tag::isEmpty() const static const char *keyTranslation[][2] = { { "WM/AlbumTitle", "ALBUM" }, + { "WM/AlbumArtist", "ALBUMARTIST" }, { "WM/Composer", "COMPOSER" }, { "WM/Writer", "WRITER" }, { "WM/Conductor", "CONDUCTOR" }, diff --git a/3rdparty/taglib/asf/asftag.h b/3rdparty/taglib/asf/asftag.h index f68579d88..8f322b186 100644 --- a/3rdparty/taglib/asf/asftag.h +++ b/3rdparty/taglib/asf/asftag.h @@ -152,24 +152,43 @@ namespace TagLib { virtual bool isEmpty() const; /*! - * Returns a reference to the item list map. This is an AttributeListMap of - * all of the items in the tag. - * - * This is the most powerfull structure for accessing the items of the tag. + * \deprecated */ AttributeListMap &attributeListMap(); + /*! + * Returns a reference to the item list map. This is an AttributeListMap of + * all of the items in the tag. + */ + const AttributeListMap &attributeListMap() const; + + /*! + * \return True if a value for \a attribute is currently set. + */ + bool contains(const String &name) const; + /*! * Removes the \a key attribute from the tag */ void removeItem(const String &name); + /*! + * \return The list of values for the key \a name, or an empty list if no + * values have been set. + */ + AttributeList attribute(const String &name) const; + /*! * Sets the \a key attribute to the value of \a attribute. If an attribute * with the \a key is already present, it will be replaced. */ void setAttribute(const String &name, const Attribute &attribute); + /*! + * Sets multiple \a values to the key \a name. + */ + void setAttribute(const String &name, const AttributeList &values); + /*! * Sets the \a key attribute to the value of \a attribute. If an attribute * with the \a key is already present, it will be added to the list. diff --git a/3rdparty/taglib/asf/asfutils.h b/3rdparty/taglib/asf/asfutils.h new file mode 100644 index 000000000..21dbd0342 --- /dev/null +++ b/3rdparty/taglib/asf/asfutils.h @@ -0,0 +1,101 @@ +/*************************************************************************** + copyright : (C) 2015 by Tsuda Kageyu + email : tsuda.kageyu@gmail.com + ***************************************************************************/ + +/*************************************************************************** + * 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_ASFUTILS_H +#define TAGLIB_ASFUTILS_H + +// THIS FILE IS NOT A PART OF THE TAGLIB API + +#ifndef DO_NOT_DOCUMENT // tell Doxygen not to document this header + +namespace TagLib +{ + namespace ASF + { + + inline ushort readWORD(File *file, bool *ok = 0) + { + const ByteVector v = file->readBlock(2); + if(v.size() != 2) { + if(ok) *ok = false; + return 0; + } + if(ok) *ok = true; + return v.toUShort(false); + } + + inline uint readDWORD(File *file, bool *ok = 0) + { + const ByteVector v = file->readBlock(4); + if(v.size() != 4) { + if(ok) *ok = false; + return 0; + } + if(ok) *ok = true; + return v.toUInt(false); + } + + inline long long readQWORD(File *file, bool *ok = 0) + { + const ByteVector v = file->readBlock(8); + if(v.size() != 8) { + if(ok) *ok = false; + return 0; + } + if(ok) *ok = true; + return v.toLongLong(false); + } + + inline String readString(File *file, int length) + { + ByteVector data = file->readBlock(length); + unsigned int size = data.size(); + while (size >= 2) { + if(data[size - 1] != '\0' || data[size - 2] != '\0') { + break; + } + size -= 2; + } + if(size != data.size()) { + data.resize(size); + } + return String(data, String::UTF16LE); + } + + inline ByteVector renderString(const String &str, bool includeLength = false) + { + ByteVector data = str.data(String::UTF16LE) + ByteVector::fromShort(0, false); + if(includeLength) { + data = ByteVector::fromShort(data.size(), false) + data; + } + return data; + } + + } +} + +#endif + +#endif diff --git a/3rdparty/taglib/audioproperties.cpp b/3rdparty/taglib/audioproperties.cpp index 298b97da1..4598164cc 100644 --- a/3rdparty/taglib/audioproperties.cpp +++ b/3rdparty/taglib/audioproperties.cpp @@ -23,6 +23,22 @@ * http://www.mozilla.org/MPL/ * ***************************************************************************/ +#include + +#include "aiffproperties.h" +#include "apeproperties.h" +#include "asfproperties.h" +#include "flacproperties.h" +#include "mp4properties.h" +#include "mpcproperties.h" +#include "mpegproperties.h" +#include "opusproperties.h" +#include "speexproperties.h" +#include "trueaudioproperties.h" +#include "vorbisproperties.h" +#include "wavproperties.h" +#include "wavpackproperties.h" + #include "audioproperties.h" using namespace TagLib; @@ -41,11 +57,108 @@ AudioProperties::~AudioProperties() } +int TagLib::AudioProperties::lengthInSeconds() const +{ + // This is an ugly workaround but we can't add a virtual function. + // Should be virtual in taglib2. + + if(dynamic_cast(this)) + return dynamic_cast(this)->lengthInSeconds(); + + else if(dynamic_cast(this)) + return dynamic_cast(this)->lengthInSeconds(); + + else if(dynamic_cast(this)) + return dynamic_cast(this)->lengthInSeconds(); + + else if(dynamic_cast(this)) + return dynamic_cast(this)->lengthInSeconds(); + + else if(dynamic_cast(this)) + return dynamic_cast(this)->lengthInSeconds(); + + else if(dynamic_cast(this)) + return dynamic_cast(this)->lengthInSeconds(); + + else if(dynamic_cast(this)) + return dynamic_cast(this)->lengthInSeconds(); + + else if(dynamic_cast(this)) + return dynamic_cast(this)->lengthInSeconds(); + + else if(dynamic_cast(this)) + return dynamic_cast(this)->lengthInSeconds(); + + else if (dynamic_cast(this)) + return dynamic_cast(this)->lengthInSeconds(); + + else if(dynamic_cast(this)) + return dynamic_cast(this)->lengthInSeconds(); + + else if(dynamic_cast(this)) + return dynamic_cast(this)->lengthInSeconds(); + + else if(dynamic_cast(this)) + return dynamic_cast(this)->lengthInSeconds(); + + else + return 0; +} + +int TagLib::AudioProperties::lengthInMilliseconds() const +{ + // This is an ugly workaround but we can't add a virtual function. + // Should be virtual in taglib2. + + if(dynamic_cast(this)) + return dynamic_cast(this)->lengthInMilliseconds(); + + else if(dynamic_cast(this)) + return dynamic_cast(this)->lengthInMilliseconds(); + + else if(dynamic_cast(this)) + return dynamic_cast(this)->lengthInMilliseconds(); + + else if(dynamic_cast(this)) + return dynamic_cast(this)->lengthInMilliseconds(); + + else if(dynamic_cast(this)) + return dynamic_cast(this)->lengthInMilliseconds(); + + else if(dynamic_cast(this)) + return dynamic_cast(this)->lengthInMilliseconds(); + + else if(dynamic_cast(this)) + return dynamic_cast(this)->lengthInMilliseconds(); + + else if(dynamic_cast(this)) + return dynamic_cast(this)->lengthInMilliseconds(); + + else if(dynamic_cast(this)) + return dynamic_cast(this)->lengthInMilliseconds(); + + else if(dynamic_cast(this)) + return dynamic_cast(this)->lengthInMilliseconds(); + + else if(dynamic_cast(this)) + return dynamic_cast(this)->lengthInMilliseconds(); + + else if(dynamic_cast(this)) + return dynamic_cast(this)->lengthInMilliseconds(); + + else if(dynamic_cast(this)) + return dynamic_cast(this)->lengthInMilliseconds(); + + else + return 0; +} + //////////////////////////////////////////////////////////////////////////////// // protected methods //////////////////////////////////////////////////////////////////////////////// -AudioProperties::AudioProperties(ReadStyle) +AudioProperties::AudioProperties(ReadStyle) : + d(0) { } diff --git a/3rdparty/taglib/audioproperties.h b/3rdparty/taglib/audioproperties.h index e9844fa0c..f27aefca8 100644 --- a/3rdparty/taglib/audioproperties.h +++ b/3rdparty/taglib/audioproperties.h @@ -34,7 +34,7 @@ namespace TagLib { /*! * The values here are common to most audio formats. For more specific, codec - * dependant values, please see see the subclasses APIs. This is meant to + * dependent values, please see see the subclasses APIs. This is meant to * compliment the TagLib::File and TagLib::Tag APIs in providing a simple * interface that is sufficient for most applications. */ @@ -69,6 +69,23 @@ namespace TagLib { */ virtual int length() const = 0; + /*! + * Returns the length of the file in seconds. The length is rounded down to + * the nearest whole second. + * + * \see lengthInMilliseconds() + */ + // BIC: make virtual + int lengthInSeconds() const; + + /*! + * Returns the length of the file in milliseconds. + * + * \see lengthInSeconds() + */ + // BIC: make virtual + int lengthInMilliseconds() const; + /*! * Returns the most appropriate bit rate for the file in kb/s. For constant * bitrate formats this is simply the bitrate of the file. For variable diff --git a/3rdparty/taglib/fileref.cpp b/3rdparty/taglib/fileref.cpp index 4403a5fbf..2b5d5f235 100644 --- a/3rdparty/taglib/fileref.cpp +++ b/3rdparty/taglib/fileref.cpp @@ -265,7 +265,7 @@ File *FileRef::create(FileName fileName, bool readAudioProperties, return new MP4::File(fileName, readAudioProperties, audioPropertiesStyle); if(ext == "WMA" || ext == "ASF") return new ASF::File(fileName, readAudioProperties, audioPropertiesStyle); - if(ext == "AIF" || ext == "AIFF") + if(ext == "AIF" || ext == "AIFF" || ext == "AFC" || ext == "AIFC") return new RIFF::AIFF::File(fileName, readAudioProperties, audioPropertiesStyle); if(ext == "WAV") return new RIFF::WAV::File(fileName, readAudioProperties, audioPropertiesStyle); diff --git a/3rdparty/taglib/fileref.h b/3rdparty/taglib/fileref.h index 0f0c21a4d..1e965229e 100644 --- a/3rdparty/taglib/fileref.h +++ b/3rdparty/taglib/fileref.h @@ -72,7 +72,7 @@ namespace TagLib { * * class MyFileTypeResolver : FileTypeResolver * { - * TagLib::File *createFile(TagLib::FileName *fileName, bool, AudioProperties::ReadStyle) + * TagLib::File *createFile(TagLib::FileName *fileName, bool, AudioProperties::ReadStyle) const * { * if(someCheckForAnMP3File(fileName)) * return new TagLib::MPEG::File(fileName); @@ -128,7 +128,7 @@ namespace TagLib { audioPropertiesStyle = AudioProperties::Average); /*! - * Contruct a FileRef using \a file. The FileRef now takes ownership of the + * Construct a FileRef using \a file. The FileRef now takes ownership of the * pointer and will delete the File when it passes out of scope. */ explicit FileRef(File *file); @@ -191,7 +191,7 @@ namespace TagLib { * is tried. * * Returns a pointer to the added resolver (the same one that's passed in -- - * this is mostly so that static inialializers have something to use for + * this is mostly so that static initializers have something to use for * assignment). * * \see FileTypeResolver @@ -209,7 +209,7 @@ namespace TagLib { * by TagLib for resolution is case-insensitive. * * \note This does not account for any additional file type resolvers that - * are plugged in. Also note that this is not intended to replace a propper + * are plugged in. Also note that this is not intended to replace a proper * mime-type resolution system, but is just here for reference. * * \see FileTypeResolver diff --git a/3rdparty/taglib/flac/flacfile.cpp b/3rdparty/taglib/flac/flacfile.cpp index 823170aae..4d2d69e65 100644 --- a/3rdparty/taglib/flac/flacfile.cpp +++ b/3rdparty/taglib/flac/flacfile.cpp @@ -60,7 +60,6 @@ public: properties(0), flacStart(0), streamStart(0), - streamLength(0), scanned(false), hasXiphComment(false), hasID3v2(false), @@ -86,13 +85,11 @@ public: TagUnion tag; Properties *properties; - ByteVector streamInfoData; ByteVector xiphCommentData; List blocks; long flacStart; long streamStart; - long streamLength; bool scanned; bool hasXiphComment; @@ -104,33 +101,32 @@ public: // public members //////////////////////////////////////////////////////////////////////////////// -FLAC::File::File(FileName file, bool readProperties, - Properties::ReadStyle propertiesStyle) : - TagLib::File(file) +FLAC::File::File(FileName file, bool readProperties, Properties::ReadStyle) : + TagLib::File(file), + d(new FilePrivate()) { - d = new FilePrivate; if(isOpen()) - read(readProperties, propertiesStyle); + read(readProperties); } FLAC::File::File(FileName file, ID3v2::FrameFactory *frameFactory, - bool readProperties, Properties::ReadStyle propertiesStyle) : - TagLib::File(file) + bool readProperties, Properties::ReadStyle) : + TagLib::File(file), + d(new FilePrivate()) { - d = new FilePrivate; d->ID3v2FrameFactory = frameFactory; if(isOpen()) - read(readProperties, propertiesStyle); + read(readProperties); } FLAC::File::File(IOStream *stream, ID3v2::FrameFactory *frameFactory, - bool readProperties, Properties::ReadStyle propertiesStyle) : - TagLib::File(stream) + bool readProperties, Properties::ReadStyle) : + TagLib::File(stream), + d(new FilePrivate()) { - d = new FilePrivate; d->ID3v2FrameFactory = frameFactory; if(isOpen()) - read(readProperties, propertiesStyle); + read(readProperties); } FLAC::File::~File() @@ -176,7 +172,6 @@ FLAC::Properties *FLAC::File::audioProperties() const return d->properties; } - bool FLAC::File::save() { if(readOnly()) { @@ -235,7 +230,7 @@ bool FLAC::File::save() long originalLength = d->streamStart - d->flacStart; int paddingLength = originalLength - data.size() - 4; - if (paddingLength < 0) { + if(paddingLength <= 0) { paddingLength = MinPaddingLength; } ByteVector padding = ByteVector::fromUInt(paddingLength); @@ -263,8 +258,16 @@ bool FLAC::File::save() } if(ID3v1Tag()) { - seek(-128, End); + if(d->hasID3v1) { + seek(d->ID3v1Location); + } + else { + seek(0, End); + d->ID3v1Location = tell(); + } + writeBlock(ID3v1Tag()->render()); + d->hasID3v1 = true; } return true; @@ -294,219 +297,16 @@ void FLAC::File::setID3v2FrameFactory(const ID3v2::FrameFactory *factory) d->ID3v2FrameFactory = factory; } - -//////////////////////////////////////////////////////////////////////////////// -// private members -//////////////////////////////////////////////////////////////////////////////// - -void FLAC::File::read(bool readProperties, Properties::ReadStyle propertiesStyle) -{ - // Look for an ID3v2 tag - - d->ID3v2Location = findID3v2(); - - if(d->ID3v2Location >= 0) { - - d->tag.set(FlacID3v2Index, new ID3v2::Tag(this, d->ID3v2Location, d->ID3v2FrameFactory)); - - d->ID3v2OriginalSize = ID3v2Tag()->header()->completeTagSize(); - - if(ID3v2Tag()->header()->tagSize() <= 0) - d->tag.set(FlacID3v2Index, 0); - else - d->hasID3v2 = true; - } - - // Look for an ID3v1 tag - - d->ID3v1Location = findID3v1(); - - if(d->ID3v1Location >= 0) { - d->tag.set(FlacID3v1Index, new ID3v1::Tag(this, d->ID3v1Location)); - d->hasID3v1 = true; - } - - // Look for FLAC metadata, including vorbis comments - - scan(); - - if(!isValid()) - return; - - if(d->hasXiphComment) - d->tag.set(FlacXiphIndex, new Ogg::XiphComment(xiphCommentData())); - else - d->tag.set(FlacXiphIndex, new Ogg::XiphComment); - - if(readProperties) - d->properties = new Properties(streamInfoData(), streamLength(), propertiesStyle); -} - ByteVector FLAC::File::streamInfoData() { - return isValid() ? d->streamInfoData : ByteVector(); -} - -ByteVector FLAC::File::xiphCommentData() const -{ - return (isValid() && d->hasXiphComment) ? d->xiphCommentData : ByteVector(); + debug("FLAC::File::streamInfoData() -- This function is obsolete. Returning an empty ByteVector."); + return ByteVector(); } long FLAC::File::streamLength() { - return d->streamLength; -} - -void FLAC::File::scan() -{ - // Scan the metadata pages - - if(d->scanned) - return; - - if(!isValid()) - return; - - long nextBlockOffset; - - if(d->hasID3v2) - nextBlockOffset = find("fLaC", d->ID3v2Location + d->ID3v2OriginalSize); - else - nextBlockOffset = find("fLaC"); - - if(nextBlockOffset < 0) { - debug("FLAC::File::scan() -- FLAC stream not found"); - setValid(false); - return; - } - - nextBlockOffset += 4; - d->flacStart = nextBlockOffset; - - seek(nextBlockOffset); - - ByteVector header = readBlock(4); - - // Header format (from spec): - // <1> Last-metadata-block flag - // <7> BLOCK_TYPE - // 0 : STREAMINFO - // 1 : PADDING - // .. - // 4 : VORBIS_COMMENT - // .. - // <24> Length of metadata to follow - - char blockType = header[0] & 0x7f; - bool isLastBlock = (header[0] & 0x80) != 0; - uint length = header.toUInt(1U, 3U); - - // First block should be the stream_info metadata - - if(blockType != MetadataBlock::StreamInfo) { - debug("FLAC::File::scan() -- invalid FLAC stream"); - setValid(false); - return; - } - - d->streamInfoData = readBlock(length); - d->blocks.append(new UnknownMetadataBlock(blockType, d->streamInfoData)); - nextBlockOffset += length + 4; - - // Search through the remaining metadata - while(!isLastBlock) { - - header = readBlock(4); - blockType = header[0] & 0x7f; - isLastBlock = (header[0] & 0x80) != 0; - length = header.toUInt(1U, 3U); - - ByteVector data = readBlock(length); - if(data.size() != length || length == 0) { - debug("FLAC::File::scan() -- FLAC stream corrupted"); - setValid(false); - return; - } - - MetadataBlock *block = 0; - - // Found the vorbis-comment - if(blockType == MetadataBlock::VorbisComment) { - if(!d->hasXiphComment) { - d->xiphCommentData = data; - d->hasXiphComment = true; - } - else { - debug("FLAC::File::scan() -- multiple Vorbis Comment blocks found, using the first one"); - } - } - else if(blockType == MetadataBlock::Picture) { - FLAC::Picture *picture = new FLAC::Picture(); - if(picture->parse(data)) { - block = picture; - } - else { - debug("FLAC::File::scan() -- invalid picture found, discarting"); - delete picture; - } - } - - if(!block) { - block = new UnknownMetadataBlock(blockType, data); - } - if(block->code() != MetadataBlock::Padding) { - d->blocks.append(block); - } - else { - delete block; - } - - nextBlockOffset += length + 4; - - if(nextBlockOffset >= File::length()) { - debug("FLAC::File::scan() -- FLAC stream corrupted"); - setValid(false); - return; - } - seek(nextBlockOffset); - } - - // End of metadata, now comes the datastream - - d->streamStart = nextBlockOffset; - d->streamLength = File::length() - d->streamStart; - - if(d->hasID3v1) - d->streamLength -= 128; - - d->scanned = true; -} - -long FLAC::File::findID3v1() -{ - if(!isValid()) - return -1; - - seek(-128, End); - long p = tell(); - - if(readBlock(3) == ID3v1::Tag::fileIdentifier()) - return p; - - return -1; -} - -long FLAC::File::findID3v2() -{ - if(!isValid()) - return -1; - - seek(0); - - if(readBlock(3) == ID3v2::Header::fileIdentifier()) - return 0; - - return -1; + debug("FLAC::File::streamLength() -- This function is obsolete. Returning zero."); + return 0; } List FLAC::File::pictureList() @@ -566,3 +366,216 @@ bool FLAC::File::hasID3v2Tag() const { return d->hasID3v2; } + +//////////////////////////////////////////////////////////////////////////////// +// private members +//////////////////////////////////////////////////////////////////////////////// + +void FLAC::File::read(bool readProperties) +{ + // Look for an ID3v2 tag + + d->ID3v2Location = findID3v2(); + + if(d->ID3v2Location >= 0) { + + d->tag.set(FlacID3v2Index, new ID3v2::Tag(this, d->ID3v2Location, d->ID3v2FrameFactory)); + + d->ID3v2OriginalSize = ID3v2Tag()->header()->completeTagSize(); + + if(ID3v2Tag()->header()->tagSize() <= 0) + d->tag.set(FlacID3v2Index, 0); + else + d->hasID3v2 = true; + } + + // Look for an ID3v1 tag + + d->ID3v1Location = findID3v1(); + + if(d->ID3v1Location >= 0) { + d->tag.set(FlacID3v1Index, new ID3v1::Tag(this, d->ID3v1Location)); + d->hasID3v1 = true; + } + + // Look for FLAC metadata, including vorbis comments + + scan(); + + if(!isValid()) + return; + + if(d->hasXiphComment) + d->tag.set(FlacXiphIndex, new Ogg::XiphComment(d->xiphCommentData)); + else + d->tag.set(FlacXiphIndex, new Ogg::XiphComment); + + if(readProperties) { + + // First block should be the stream_info metadata + + const ByteVector infoData = d->blocks.front()->render(); + + long streamLength; + + if(d->hasID3v1) + streamLength = d->ID3v1Location - d->streamStart; + else + streamLength = File::length() - d->streamStart; + + d->properties = new Properties(infoData, streamLength); + } +} + +void FLAC::File::scan() +{ + // Scan the metadata pages + + if(d->scanned) + return; + + if(!isValid()) + return; + + long nextBlockOffset; + + if(d->hasID3v2) + nextBlockOffset = find("fLaC", d->ID3v2Location + d->ID3v2OriginalSize); + else + nextBlockOffset = find("fLaC"); + + if(nextBlockOffset < 0) { + debug("FLAC::File::scan() -- FLAC stream not found"); + setValid(false); + return; + } + + nextBlockOffset += 4; + d->flacStart = nextBlockOffset; + + seek(nextBlockOffset); + + ByteVector header = readBlock(4); + + // Header format (from spec): + // <1> Last-metadata-block flag + // <7> BLOCK_TYPE + // 0 : STREAMINFO + // 1 : PADDING + // .. + // 4 : VORBIS_COMMENT + // .. + // <24> Length of metadata to follow + + char blockType = header[0] & 0x7f; + bool isLastBlock = (header[0] & 0x80) != 0; + uint length = header.toUInt(1U, 3U); + + // First block should be the stream_info metadata + + if(blockType != MetadataBlock::StreamInfo) { + debug("FLAC::File::scan() -- invalid FLAC stream"); + setValid(false); + return; + } + + d->blocks.append(new UnknownMetadataBlock(blockType, readBlock(length))); + nextBlockOffset += length + 4; + + // Search through the remaining metadata + while(!isLastBlock) { + + header = readBlock(4); + blockType = header[0] & 0x7f; + isLastBlock = (header[0] & 0x80) != 0; + length = header.toUInt(1U, 3U); + + if(length == 0 && blockType != MetadataBlock::Padding) { + debug("FLAC::File::scan() -- Zero-sized metadata block found"); + setValid(false); + return; + } + + const ByteVector data = readBlock(length); + if(data.size() != length) { + debug("FLAC::File::scan() -- Failed to read a metadata block"); + setValid(false); + return; + } + + MetadataBlock *block = 0; + + // Found the vorbis-comment + if(blockType == MetadataBlock::VorbisComment) { + if(!d->hasXiphComment) { + d->xiphCommentData = data; + d->hasXiphComment = true; + } + else { + debug("FLAC::File::scan() -- multiple Vorbis Comment blocks found, using the first one"); + } + } + else if(blockType == MetadataBlock::Picture) { + FLAC::Picture *picture = new FLAC::Picture(); + if(picture->parse(data)) { + block = picture; + } + else { + debug("FLAC::File::scan() -- invalid picture found, discarding"); + delete picture; + } + } + + if(!block) { + block = new UnknownMetadataBlock(blockType, data); + } + if(block->code() != MetadataBlock::Padding) { + d->blocks.append(block); + } + else { + delete block; + } + + nextBlockOffset += length + 4; + + if(nextBlockOffset >= File::length()) { + debug("FLAC::File::scan() -- FLAC stream corrupted"); + setValid(false); + return; + } + seek(nextBlockOffset); + } + + // End of metadata, now comes the datastream + + d->streamStart = nextBlockOffset; + + d->scanned = true; +} + +long FLAC::File::findID3v1() +{ + if(!isValid()) + return -1; + + seek(-128, End); + long p = tell(); + + if(readBlock(3) == ID3v1::Tag::fileIdentifier()) + return p; + + return -1; +} + +long FLAC::File::findID3v2() +{ + if(!isValid()) + return -1; + + seek(0); + + if(readBlock(3) == ID3v2::Header::fileIdentifier()) + return 0; + + return -1; +} diff --git a/3rdparty/taglib/flac/flacfile.h b/3rdparty/taglib/flac/flacfile.h index 0963f4af6..1c055d339 100644 --- a/3rdparty/taglib/flac/flacfile.h +++ b/3rdparty/taglib/flac/flacfile.h @@ -46,7 +46,7 @@ namespace TagLib { /*! * This is implementation of FLAC metadata for non-Ogg FLAC files. At some * point when Ogg / FLAC is more common there will be a similar implementation - * under the Ogg hiearchy. + * under the Ogg hierarchy. * * This supports ID3v1, ID3v2 and Xiph style comments as well as reading stream * properties from the file. @@ -79,7 +79,7 @@ namespace TagLib { Properties::ReadStyle propertiesStyle = Properties::Average); /*! - * Constructs an APE file from \a file. If \a readProperties is true the + * Constructs an FLAC file from \a file. If \a readProperties is true the * file's audio properties will also be read. * * If this file contains and ID3v2 tag the frames will be created using @@ -155,6 +155,9 @@ namespace TagLib { * has no XiphComment, one will be constructed from the ID3-tags. * * This returns true if the save was successful. + * + * \warning In the current implementation, it's dangerous to call save() + * repeatedly. At worst it will corrupt the file. */ virtual bool save(); @@ -165,8 +168,8 @@ namespace TagLib { * if there is no valid ID3v2 tag. If \a create is true it will create * an ID3v2 tag if one does not exist and returns a valid pointer. * - * \note This may return a valid pointer regardless of whether or not the - * file on disk has an ID3v2 tag. Use hasID3v2Tag() to check if the file + * \note This may return a valid pointer regardless of whether or not the + * file on disk has an ID3v2 tag. Use hasID3v2Tag() to check if the file * on disk actually has an ID3v2 tag. * * \note The Tag is still owned by the MPEG::File and should not be @@ -184,8 +187,8 @@ namespace TagLib { * if there is no valid APE tag. If \a create is true it will create * an APE tag if one does not exist and returns a valid pointer. * - * \note This may return a valid pointer regardless of whether or not the - * file on disk has an ID3v1 tag. Use hasID3v1Tag() to check if the file + * \note This may return a valid pointer regardless of whether or not the + * file on disk has an ID3v1 tag. Use hasID3v1Tag() to check if the file * on disk actually has an ID3v1 tag. * * \note The Tag is still owned by the MPEG::File and should not be @@ -203,10 +206,10 @@ namespace TagLib { * if there is no valid XiphComment. If \a create is true it will create * a XiphComment if one does not exist and returns a valid pointer. * - * \note This may return a valid pointer regardless of whether or not the - * file on disk has a XiphComment. Use hasXiphComment() to check if the + * \note This may return a valid pointer regardless of whether or not the + * file on disk has a XiphComment. Use hasXiphComment() to check if the * file on disk actually has a XiphComment. - * + * * \note The Tag is still owned by the FLAC::File and should not be * deleted by the user. It will be deleted when the file (object) is * destroyed. @@ -221,6 +224,7 @@ namespace TagLib { * when * * \see ID3v2FrameFactory + * \deprecated This value should be passed in via the constructor */ void setID3v2FrameFactory(const ID3v2::FrameFactory *factory); @@ -228,7 +232,7 @@ namespace TagLib { * Returns the block of data used by FLAC::Properties for parsing the * stream properties. * - * \deprecated This method will not be public in a future release. + * \deprecated Always returns an empty vector. */ ByteVector streamInfoData(); // BIC: remove @@ -236,7 +240,7 @@ namespace TagLib { * Returns the length of the audio-stream, used by FLAC::Properties for * calculating the bitrate. * - * \deprecated This method will not be public in a future release. + * \deprecated Always returns zero. */ long streamLength(); // BIC: remove @@ -289,12 +293,10 @@ namespace TagLib { File(const File &); File &operator=(const File &); - void read(bool readProperties, Properties::ReadStyle propertiesStyle); + void read(bool readProperties); void scan(); long findID3v2(); long findID3v1(); - ByteVector xiphCommentData() const; - long findPaddingBreak(long nextPageOffset, long targetOffset, bool *isLast); class FilePrivate; FilePrivate *d; diff --git a/3rdparty/taglib/flac/flacproperties.cpp b/3rdparty/taglib/flac/flacproperties.cpp index e591193ef..efc9fa1b4 100644 --- a/3rdparty/taglib/flac/flacproperties.cpp +++ b/3rdparty/taglib/flac/flacproperties.cpp @@ -34,24 +34,18 @@ using namespace TagLib; class FLAC::Properties::PropertiesPrivate { public: - PropertiesPrivate(ByteVector d, long st, ReadStyle s) : - data(d), - streamLength(st), - style(s), + PropertiesPrivate() : length(0), bitrate(0), sampleRate(0), - sampleWidth(0), + bitsPerSample(0), channels(0), sampleFrames(0) {} - ByteVector data; - long streamLength; - ReadStyle style; int length; int bitrate; int sampleRate; - int sampleWidth; + int bitsPerSample; int channels; unsigned long long sampleFrames; ByteVector signature; @@ -61,16 +55,18 @@ public: // public members //////////////////////////////////////////////////////////////////////////////// -FLAC::Properties::Properties(ByteVector data, long streamLength, ReadStyle style) : AudioProperties(style) +FLAC::Properties::Properties(ByteVector data, long streamLength, ReadStyle style) : + AudioProperties(style), + d(new PropertiesPrivate()) { - d = new PropertiesPrivate(data, streamLength, style); - read(); + read(data, streamLength); } -FLAC::Properties::Properties(File *file, ReadStyle style) : AudioProperties(style) +FLAC::Properties::Properties(File *, ReadStyle style) : + AudioProperties(style), + d(new PropertiesPrivate()) { - d = new PropertiesPrivate(file->streamInfoData(), file->streamLength(), style); - read(); + debug("FLAC::Properties::Properties() - This constructor is no longer used."); } FLAC::Properties::~Properties() @@ -79,6 +75,16 @@ FLAC::Properties::~Properties() } int FLAC::Properties::length() const +{ + return lengthInSeconds(); +} + +int FLAC::Properties::lengthInSeconds() const +{ + return d->length / 1000; +} + +int FLAC::Properties::lengthInMilliseconds() const { return d->length; } @@ -93,9 +99,14 @@ int FLAC::Properties::sampleRate() const return d->sampleRate; } +int FLAC::Properties::bitsPerSample() const +{ + return d->bitsPerSample; +} + int FLAC::Properties::sampleWidth() const { - return d->sampleWidth; + return bitsPerSample(); } int FLAC::Properties::channels() const @@ -117,9 +128,9 @@ ByteVector FLAC::Properties::signature() const // private members //////////////////////////////////////////////////////////////////////////////// -void FLAC::Properties::read() +void FLAC::Properties::read(const ByteVector &data, long streamLength) { - if(d->data.size() < 18) { + if(data.size() < 18) { debug("FLAC::Properties::read() - FLAC properties must contain at least 18 bytes."); return; } @@ -138,32 +149,28 @@ void FLAC::Properties::read() // Maximum frame size (in bytes) pos += 3; - uint flags = d->data.toUInt(pos, true); + const uint flags = data.toUInt(pos, true); pos += 4; - d->sampleRate = flags >> 12; - d->channels = ((flags >> 9) & 7) + 1; - d->sampleWidth = ((flags >> 4) & 31) + 1; + d->sampleRate = flags >> 12; + d->channels = ((flags >> 9) & 7) + 1; + d->bitsPerSample = ((flags >> 4) & 31) + 1; // The last 4 bits are the most significant 4 bits for the 36 bit // stream length in samples. (Audio files measured in days) - unsigned long long hi = flags & 0xf; - unsigned long long lo = d->data.toUInt(pos, true); + const ulonglong hi = flags & 0xf; + const ulonglong lo = data.toUInt(pos, true); pos += 4; d->sampleFrames = (hi << 32) | lo; - if(d->sampleRate > 0) - d->length = int(d->sampleFrames / d->sampleRate); + if(d->sampleFrames > 0 && d->sampleRate > 0) { + const double length = d->sampleFrames * 1000.0 / d->sampleRate; + d->length = static_cast(length + 0.5); + d->bitrate = static_cast(streamLength * 8.0 / length + 0.5); + } - // Uncompressed bitrate: - - //d->bitrate = ((d->sampleRate * d->channels) / 1000) * d->sampleWidth; - - // Real bitrate: - - d->bitrate = d->length > 0 ? ((d->streamLength * 8UL) / d->length) / 1000 : 0; - - d->signature = d->data.mid(pos, 32); + if(data.size() >= pos + 16) + d->signature = data.mid(pos, 16); } diff --git a/3rdparty/taglib/flac/flacproperties.h b/3rdparty/taglib/flac/flacproperties.h index c14589819..6f13ce622 100644 --- a/3rdparty/taglib/flac/flacproperties.h +++ b/3rdparty/taglib/flac/flacproperties.h @@ -64,27 +64,72 @@ namespace TagLib { */ virtual ~Properties(); - // Reimplementations. - + /*! + * Returns the length of the file in seconds. The length is rounded down to + * the nearest whole second. + * + * \note This method is just an alias of lengthInSeconds(). + * + * \deprecated + */ virtual int length() const; + + /*! + * Returns the length of the file in seconds. The length is rounded down to + * the nearest whole second. + * + * \see lengthInMilliseconds() + */ + // BIC: make virtual + int lengthInSeconds() const; + + /*! + * Returns the length of the file in milliseconds. + * + * \see lengthInSeconds() + */ + // BIC: make virtual + int lengthInMilliseconds() const; + + /*! + * Returns the average bit rate of the file in kb/s. + */ virtual int bitrate() const; + + /*! + * Returns the sample rate in Hz. + */ virtual int sampleRate() const; + + /*! + * Returns the number of audio channels. + */ virtual int channels() const; + /*! + * Returns the number of bits per audio sample as read from the FLAC + * identification header. + */ + int bitsPerSample() const; + /*! * Returns the sample width as read from the FLAC identification * header. + * + * \note This method is just an alias of bitsPerSample(). + * + * \deprecated */ int sampleWidth() const; /*! - * Return the number of sample frames + * Return the number of sample frames. */ unsigned long long sampleFrames() const; /*! * Returns the MD5 signature of the uncompressed audio stream as read - * from the stream info header header. + * from the stream info header. */ ByteVector signature() const; @@ -92,7 +137,7 @@ namespace TagLib { Properties(const Properties &); Properties &operator=(const Properties &); - void read(); + void read(const ByteVector &data, long streamLength); class PropertiesPrivate; PropertiesPrivate *d; diff --git a/3rdparty/taglib/it/itproperties.cpp b/3rdparty/taglib/it/itproperties.cpp index 7a328fb94..0359533de 100644 --- a/3rdparty/taglib/it/itproperties.cpp +++ b/3rdparty/taglib/it/itproperties.cpp @@ -79,6 +79,16 @@ int IT::Properties::length() const return 0; } +int IT::Properties::lengthInSeconds() const +{ + return 0; +} + +int IT::Properties::lengthInMilliseconds() const +{ + return 0; +} + int IT::Properties::bitrate() const { return 0; diff --git a/3rdparty/taglib/it/itproperties.h b/3rdparty/taglib/it/itproperties.h index da2a7c4fb..060ad358f 100644 --- a/3rdparty/taglib/it/itproperties.h +++ b/3rdparty/taglib/it/itproperties.h @@ -51,10 +51,12 @@ namespace TagLib { Properties(AudioProperties::ReadStyle propertiesStyle); virtual ~Properties(); - int length() const; - int bitrate() const; - int sampleRate() const; - int channels() const; + int length() const; + int lengthInSeconds() const; + int lengthInMilliseconds() const; + int bitrate() const; + int sampleRate() const; + int channels() const; ushort lengthInPatterns() const; bool stereo() const; diff --git a/3rdparty/taglib/mod/modproperties.cpp b/3rdparty/taglib/mod/modproperties.cpp index 4d3c35484..db5a98e27 100644 --- a/3rdparty/taglib/mod/modproperties.cpp +++ b/3rdparty/taglib/mod/modproperties.cpp @@ -55,6 +55,16 @@ int Mod::Properties::length() const return 0; } +int Mod::Properties::lengthInSeconds() const +{ + return 0; +} + +int Mod::Properties::lengthInMilliseconds() const +{ + return 0; +} + int Mod::Properties::bitrate() const { return 0; diff --git a/3rdparty/taglib/mod/modproperties.h b/3rdparty/taglib/mod/modproperties.h index d50353a48..ac4bf7f37 100644 --- a/3rdparty/taglib/mod/modproperties.h +++ b/3rdparty/taglib/mod/modproperties.h @@ -35,12 +35,14 @@ namespace TagLib { Properties(AudioProperties::ReadStyle propertiesStyle); virtual ~Properties(); - int length() const; - int bitrate() const; - int sampleRate() const; - int channels() const; + int length() const; + int lengthInSeconds() const; + int lengthInMilliseconds() const; + int bitrate() const; + int sampleRate() const; + int channels() const; - uint instrumentCount() const; + uint instrumentCount() const; uchar lengthInPatterns() const; void setChannels(int channels); diff --git a/3rdparty/taglib/mod/modtag.cpp b/3rdparty/taglib/mod/modtag.cpp index 14c4c741f..4ba72117d 100644 --- a/3rdparty/taglib/mod/modtag.cpp +++ b/3rdparty/taglib/mod/modtag.cpp @@ -158,7 +158,7 @@ PropertyMap Mod::Tag::setProperties(const PropertyMap &origProps) // for each tag that has been set above, remove the first entry in the corresponding // value list. The others will be returned as unsupported by this format. - for(StringList::Iterator it = oneValueSet.begin(); it != oneValueSet.end(); ++it) { + for(StringList::ConstIterator it = oneValueSet.begin(); it != oneValueSet.end(); ++it) { if(properties[*it].size() == 1) properties.erase(*it); else diff --git a/3rdparty/taglib/mod/modtag.h b/3rdparty/taglib/mod/modtag.h index f33e33f29..5b660359c 100644 --- a/3rdparty/taglib/mod/modtag.h +++ b/3rdparty/taglib/mod/modtag.h @@ -97,7 +97,7 @@ namespace TagLib { * Sets the title to \a title. If \a title is String::null then this * value will be cleared. * - * The length limits per file type are (1 characetr = 1 byte): + * The length limits per file type are (1 character = 1 byte): * Mod 20 characters, S3M 27 characters, IT 25 characters and XM 20 * characters. */ @@ -126,7 +126,7 @@ namespace TagLib { * an thus the line length in comments are limited. Too big comments * will be truncated. * - * The line length limits per file type are (1 characetr = 1 byte): + * The line length limits per file type are (1 character = 1 byte): * Mod 22 characters, S3M 27 characters, IT 25 characters and XM 22 * characters. */ @@ -169,7 +169,7 @@ namespace TagLib { * Implements the unified property interface -- import function. * Because of the limitations of the module file tag, any tags besides * COMMENT, TITLE and, if it is an XM file, TRACKERNAME, will be - * returened. Additionally, if the map contains tags with multiple values, + * returned. Additionally, if the map contains tags with multiple values, * all but the first will be contained in the returned map of unsupported * properties. */ diff --git a/3rdparty/taglib/mp4/mp4atom.cpp b/3rdparty/taglib/mp4/mp4atom.cpp index 7b87a4796..5e94690f5 100644 --- a/3rdparty/taglib/mp4/mp4atom.cpp +++ b/3rdparty/taglib/mp4/mp4atom.cpp @@ -23,6 +23,12 @@ * http://www.mozilla.org/MPL/ * ***************************************************************************/ +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + #include #include #include "mp4atom.h" @@ -50,20 +56,25 @@ MP4::Atom::Atom(File *file) length = header.toUInt(); - if (length == 1) { + if(length == 1) { const long long longLength = file->readBlock(8).toLongLong(); - if (longLength >= 8 && longLength <= 0xFFFFFFFF) { - // The atom has a 64-bit length, but it's actually a 32-bit value - length = (long)longLength; + if(sizeof(long) == sizeof(long long)) { + length = static_cast(longLength); } else { + if(longLength <= LONG_MAX) { + // The atom has a 64-bit length, but it's actually a 31-bit value + length = static_cast(longLength); + } + else { debug("MP4: 64-bit atoms are not supported"); length = 0; file->seek(0, File::End); return; + } } } - if (length < 8) { + if(length < 8) { debug("MP4: Invalid atom size"); length = 0; file->seek(0, File::End); diff --git a/3rdparty/taglib/mp4/mp4atom.h b/3rdparty/taglib/mp4/mp4atom.h index ea5091a8b..6cdb1d42b 100644 --- a/3rdparty/taglib/mp4/mp4atom.h +++ b/3rdparty/taglib/mp4/mp4atom.h @@ -59,7 +59,7 @@ namespace TagLib { TypeDateTime = 17, // in UTC, counting seconds since midnight, January 1, 1904; 32 or 64-bits TypeGenred = 18, // a list of enumerated values TypeInteger = 21, // a signed big-endian integer with length one of { 1,2,3,4,8 } bytes - TypeRIAAPA = 24, // RIAA parental advisory; { -1=no, 1=yes, 0=unspecified }, 8-bit ingteger + TypeRIAAPA = 24, // RIAA parental advisory; { -1=no, 1=yes, 0=unspecified }, 8-bit integer TypeUPC = 25, // Universal Product Code, in text UTF-8 format (valid as an ID) TypeBMP = 27, // Windows bitmap image TypeUndefined = 255 // undefined diff --git a/3rdparty/taglib/mp4/mp4coverart.cpp b/3rdparty/taglib/mp4/mp4coverart.cpp index 2746469d3..f21523356 100644 --- a/3rdparty/taglib/mp4/mp4coverart.cpp +++ b/3rdparty/taglib/mp4/mp4coverart.cpp @@ -54,11 +54,12 @@ MP4::CoverArt::CoverArt(const CoverArt &item) : d(item.d) MP4::CoverArt & MP4::CoverArt::operator=(const CoverArt &item) { - if(d->deref()) { - delete d; + if(&item != this) { + if(d->deref()) + delete d; + d = item.d; + d->ref(); } - d = item.d; - d->ref(); return *this; } diff --git a/3rdparty/taglib/mp4/mp4file.cpp b/3rdparty/taglib/mp4/mp4file.cpp index aab1a2bea..1fc1524fd 100644 --- a/3rdparty/taglib/mp4/mp4file.cpp +++ b/3rdparty/taglib/mp4/mp4file.cpp @@ -32,48 +32,57 @@ using namespace TagLib; +namespace +{ + bool checkValid(const MP4::AtomList &list) + { + for(MP4::AtomList::ConstIterator it = list.begin(); it != list.end(); ++it) { + + if((*it)->length == 0) + return false; + + if(!checkValid((*it)->children)) + return false; + } + + return true; + } +} + class MP4::File::FilePrivate { public: - FilePrivate() : tag(0), atoms(0), properties(0) - { - } + FilePrivate() : + tag(0), + atoms(0), + properties(0) {} ~FilePrivate() { - if(atoms) { - delete atoms; - atoms = 0; - } - if(tag) { - delete tag; - tag = 0; - } - if(properties) { - delete properties; - properties = 0; - } + delete atoms; + delete tag; + delete properties; } - MP4::Tag *tag; - MP4::Atoms *atoms; + MP4::Tag *tag; + MP4::Atoms *atoms; MP4::Properties *properties; }; -MP4::File::File(FileName file, bool readProperties, AudioProperties::ReadStyle audioPropertiesStyle) - : TagLib::File(file) +MP4::File::File(FileName file, bool readProperties, AudioProperties::ReadStyle) : + TagLib::File(file), + d(new FilePrivate()) { - d = new FilePrivate; if(isOpen()) - read(readProperties, audioPropertiesStyle); + read(readProperties); } -MP4::File::File(IOStream *stream, bool readProperties, AudioProperties::ReadStyle audioPropertiesStyle) - : TagLib::File(stream) +MP4::File::File(IOStream *stream, bool readProperties, AudioProperties::ReadStyle) : + TagLib::File(stream), + d(new FilePrivate()) { - d = new FilePrivate; if(isOpen()) - read(readProperties, audioPropertiesStyle); + read(readProperties); } MP4::File::~File() @@ -108,26 +117,14 @@ MP4::File::audioProperties() const return d->properties; } -bool -MP4::File::checkValid(const MP4::AtomList &list) -{ - for(uint i = 0; i < list.size(); i++) { - if(list[i]->length == 0) - return false; - if(!checkValid(list[i]->children)) - return false; - } - return true; -} - void -MP4::File::read(bool readProperties, Properties::ReadStyle audioPropertiesStyle) +MP4::File::read(bool readProperties) { if(!isValid()) return; d->atoms = new Atoms(this); - if (!checkValid(d->atoms->atoms)) { + if(!checkValid(d->atoms->atoms)) { setValid(false); return; } @@ -141,7 +138,7 @@ MP4::File::read(bool readProperties, Properties::ReadStyle audioPropertiesStyle) d->tag = new Tag(this, d->atoms); if(readProperties) { - d->properties = new Properties(this, d->atoms, audioPropertiesStyle); + d->properties = new Properties(this, d->atoms); } } diff --git a/3rdparty/taglib/mp4/mp4file.h b/3rdparty/taglib/mp4/mp4file.h index 39693d369..791a01923 100644 --- a/3rdparty/taglib/mp4/mp4file.h +++ b/3rdparty/taglib/mp4/mp4file.h @@ -54,7 +54,7 @@ namespace TagLib { * * \note In the current implementation, \a propertiesStyle is ignored. */ - File(FileName file, bool readProperties = true, + File(FileName file, bool readProperties = true, Properties::ReadStyle audioPropertiesStyle = Properties::Average); /*! @@ -66,7 +66,7 @@ namespace TagLib { * * \note In the current implementation, \a propertiesStyle is ignored. */ - File(IOStream *stream, bool readProperties = true, + File(IOStream *stream, bool readProperties = true, Properties::ReadStyle audioPropertiesStyle = Properties::Average); /*! @@ -111,13 +111,14 @@ namespace TagLib { * Save the file. * * This returns true if the save was successful. + * + * \warning In the current implementation, it's dangerous to call save() + * repeatedly. At worst it will corrupt the file. */ bool save(); private: - - void read(bool readProperties, Properties::ReadStyle audioPropertiesStyle); - bool checkValid(const MP4::AtomList &list); + void read(bool readProperties); class FilePrivate; FilePrivate *d; diff --git a/3rdparty/taglib/mp4/mp4item.cpp b/3rdparty/taglib/mp4/mp4item.cpp index 671f26b4d..aa59fedad 100644 --- a/3rdparty/taglib/mp4/mp4item.cpp +++ b/3rdparty/taglib/mp4/mp4item.cpp @@ -64,11 +64,13 @@ MP4::Item::Item(const Item &item) : d(item.d) MP4::Item & MP4::Item::operator=(const Item &item) { - if(d->deref()) { - delete d; + if(&item != this) { + if(d->deref()) { + delete d; + } + d = item.d; + d->ref(); } - d = item.d; - d->ref(); return *this; } diff --git a/3rdparty/taglib/mp4/mp4properties.cpp b/3rdparty/taglib/mp4/mp4properties.cpp index 5a41c0814..0ac77342c 100644 --- a/3rdparty/taglib/mp4/mp4properties.cpp +++ b/3rdparty/taglib/mp4/mp4properties.cpp @@ -34,7 +34,14 @@ using namespace TagLib; class MP4::Properties::PropertiesPrivate { public: - PropertiesPrivate() : length(0), bitrate(0), sampleRate(0), channels(0), bitsPerSample(0), encrypted(false), codec(MP4::Properties::Unknown) {} + PropertiesPrivate() : + length(0), + bitrate(0), + sampleRate(0), + channels(0), + bitsPerSample(0), + encrypted(false), + codec(MP4::Properties::Unknown) {} int length; int bitrate; @@ -45,110 +52,15 @@ public: Codec codec; }; -MP4::Properties::Properties(File *file, MP4::Atoms *atoms, ReadStyle style) - : AudioProperties(style) +//////////////////////////////////////////////////////////////////////////////// +// public members +//////////////////////////////////////////////////////////////////////////////// + +MP4::Properties::Properties(File *file, MP4::Atoms *atoms, ReadStyle style) : + AudioProperties(style), + d(new PropertiesPrivate()) { - d = new PropertiesPrivate; - - MP4::Atom *moov = atoms->find("moov"); - if(!moov) { - debug("MP4: Atom 'moov' not found"); - return; - } - - MP4::Atom *trak = 0; - ByteVector data; - - MP4::AtomList trakList = moov->findall("trak"); - for (unsigned int i = 0; i < trakList.size(); i++) { - trak = trakList[i]; - MP4::Atom *hdlr = trak->find("mdia", "hdlr"); - if(!hdlr) { - debug("MP4: Atom 'trak.mdia.hdlr' not found"); - return; - } - file->seek(hdlr->offset); - data = file->readBlock(hdlr->length); - if(data.mid(16, 4) == "soun") { - break; - } - trak = 0; - } - if (!trak) { - debug("MP4: No audio tracks"); - return; - } - - MP4::Atom *mdhd = trak->find("mdia", "mdhd"); - if(!mdhd) { - debug("MP4: Atom 'trak.mdia.mdhd' not found"); - return; - } - - file->seek(mdhd->offset); - data = file->readBlock(mdhd->length); - uint version = data[8]; - if(version == 1) { - if (data.size() < 36 + 8) { - debug("MP4: Atom 'trak.mdia.mdhd' is smaller than expected"); - return; - } - const long long unit = data.toLongLong(28U); - const long long length = data.toLongLong(36U); - d->length = unit ? int(length / unit) : 0; - } - else { - if (data.size() < 24 + 4) { - debug("MP4: Atom 'trak.mdia.mdhd' is smaller than expected"); - return; - } - const unsigned int unit = data.toUInt(20U); - const unsigned int length = data.toUInt(24U); - d->length = unit ? length / unit : 0; - } - - MP4::Atom *atom = trak->find("mdia", "minf", "stbl", "stsd"); - if(!atom) { - return; - } - - file->seek(atom->offset); - data = file->readBlock(atom->length); - if(data.mid(20, 4) == "mp4a") { - d->codec = AAC; - d->channels = data.toShort(40U); - d->bitsPerSample = data.toShort(42U); - d->sampleRate = data.toUInt(46U); - if(data.mid(56, 4) == "esds" && data[64] == 0x03) { - uint pos = 65; - if(data.mid(pos, 3) == "\x80\x80\x80") { - pos += 3; - } - pos += 4; - if(data[pos] == 0x04) { - pos += 1; - if(data.mid(pos, 3) == "\x80\x80\x80") { - pos += 3; - } - pos += 10; - d->bitrate = (data.toUInt(pos) + 500) / 1000; - } - } - } - else if (data.mid(20, 4) == "alac") { - if (atom->length == 88 && data.mid(56, 4) == "alac") { - d->codec = ALAC; - d->bitsPerSample = data.at(69); - d->channels = data.at(73); - d->bitrate = data.toUInt(80U) / 1000; - d->sampleRate = data.toUInt(84U); - } - } - - MP4::Atom *drms = atom->find("drms"); - if(drms) { - d->encrypted = true; - } + read(file, atoms); } MP4::Properties::~Properties() @@ -170,6 +82,18 @@ MP4::Properties::sampleRate() const int MP4::Properties::length() const +{ + return lengthInSeconds(); +} + +int +MP4::Properties::lengthInSeconds() const +{ + return d->length / 1000; +} + +int +MP4::Properties::lengthInMilliseconds() const { return d->length; } @@ -192,8 +116,119 @@ MP4::Properties::isEncrypted() const return d->encrypted; } -MP4::Properties::Codec MP4::Properties::codec() const +MP4::Properties::Codec +MP4::Properties::codec() const { return d->codec; } +//////////////////////////////////////////////////////////////////////////////// +// private members +//////////////////////////////////////////////////////////////////////////////// + +void +MP4::Properties::read(File *file, Atoms *atoms) +{ + MP4::Atom *moov = atoms->find("moov"); + if(!moov) { + debug("MP4: Atom 'moov' not found"); + return; + } + + MP4::Atom *trak = 0; + ByteVector data; + + const MP4::AtomList trakList = moov->findall("trak"); + for(MP4::AtomList::ConstIterator it = trakList.begin(); it != trakList.end(); ++it) { + trak = *it; + MP4::Atom *hdlr = trak->find("mdia", "hdlr"); + if(!hdlr) { + debug("MP4: Atom 'trak.mdia.hdlr' not found"); + return; + } + file->seek(hdlr->offset); + data = file->readBlock(hdlr->length); + if(data.containsAt("soun", 16)) { + break; + } + trak = 0; + } + if(!trak) { + debug("MP4: No audio tracks"); + return; + } + + MP4::Atom *mdhd = trak->find("mdia", "mdhd"); + if(!mdhd) { + debug("MP4: Atom 'trak.mdia.mdhd' not found"); + return; + } + + file->seek(mdhd->offset); + data = file->readBlock(mdhd->length); + + const uint version = data[8]; + long long unit; + long long length; + if(version == 1) { + if(data.size() < 36 + 8) { + debug("MP4: Atom 'trak.mdia.mdhd' is smaller than expected"); + return; + } + unit = data.toLongLong(28U); + length = data.toLongLong(36U); + } + else { + if(data.size() < 24 + 4) { + debug("MP4: Atom 'trak.mdia.mdhd' is smaller than expected"); + return; + } + unit = data.toUInt(20U); + length = data.toUInt(24U); + } + if(unit > 0 && length > 0) + d->length = static_cast(length * 1000.0 / unit); + + MP4::Atom *atom = trak->find("mdia", "minf", "stbl", "stsd"); + if(!atom) { + return; + } + + file->seek(atom->offset); + data = file->readBlock(atom->length); + if(data.containsAt("mp4a", 20)) { + d->codec = AAC; + d->channels = data.toShort(40U); + d->bitsPerSample = data.toShort(42U); + d->sampleRate = data.toUInt(46U); + if(data.containsAt("esds", 56) && data[64] == 0x03) { + uint pos = 65; + if(data.containsAt("\x80\x80\x80", pos)) { + pos += 3; + } + pos += 4; + if(data[pos] == 0x04) { + pos += 1; + if(data.containsAt("\x80\x80\x80", pos)) { + pos += 3; + } + pos += 10; + d->bitrate = static_cast((data.toUInt(pos) + 500) / 1000.0 + 0.5); + } + } + } + else if(data.containsAt("alac", 20)) { + if(atom->length == 88 && data.containsAt("alac", 56)) { + d->codec = ALAC; + d->bitsPerSample = data.at(69); + d->channels = data.at(73); + d->bitrate = static_cast(data.toUInt(80U) / 1000.0 + 0.5); + d->sampleRate = data.toUInt(84U); + } + } + + MP4::Atom *drms = atom->find("drms"); + if(drms) { + d->encrypted = true; + } +} diff --git a/3rdparty/taglib/mp4/mp4properties.h b/3rdparty/taglib/mp4/mp4properties.h index 2607c3664..410d53482 100644 --- a/3rdparty/taglib/mp4/mp4properties.h +++ b/3rdparty/taglib/mp4/mp4properties.h @@ -49,17 +49,66 @@ namespace TagLib { Properties(File *file, Atoms *atoms, ReadStyle style = Average); virtual ~Properties(); + /*! + * Returns the length of the file in seconds. The length is rounded down to + * the nearest whole second. + * + * \note This method is just an alias of lengthInSeconds(). + * + * \deprecated + */ virtual int length() const; + + /*! + * Returns the length of the file in seconds. The length is rounded down to + * the nearest whole second. + * + * \see lengthInMilliseconds() + */ + // BIC: make virtual + int lengthInSeconds() const; + + /*! + * Returns the length of the file in milliseconds. + * + * \see lengthInSeconds() + */ + // BIC: make virtual + int lengthInMilliseconds() const; + + /*! + * Returns the average bit rate of the file in kb/s. + */ virtual int bitrate() const; + + /*! + * Returns the sample rate in Hz. + */ virtual int sampleRate() const; + + /*! + * Returns the number of audio channels. + */ virtual int channels() const; + + /*! + * Returns the number of bits per audio sample. + */ virtual int bitsPerSample() const; + + /*! + * Returns whether or not the file is encrypted. + */ bool isEncrypted() const; - //! Audio codec used in the MP4 file + /*! + * Returns the codec used in the file. + */ Codec codec() const; private: + void read(File *file, Atoms *atoms); + class PropertiesPrivate; PropertiesPrivate *d; }; diff --git a/3rdparty/taglib/mp4/mp4tag.cpp b/3rdparty/taglib/mp4/mp4tag.cpp index bbe3301d9..ce557c0ff 100644 --- a/3rdparty/taglib/mp4/mp4tag.cpp +++ b/3rdparty/taglib/mp4/mp4tag.cpp @@ -39,7 +39,7 @@ public: ~TagPrivate() {} TagLib::File *file; Atoms *atoms; - ItemListMap items; + ItemMap items; }; MP4::Tag::Tag() @@ -63,36 +63,36 @@ MP4::Tag::Tag(TagLib::File *file, MP4::Atoms *atoms) MP4::Atom *atom = ilst->children[i]; file->seek(atom->offset + 8); if(atom->name == "----") { - parseFreeForm(atom, file); + parseFreeForm(atom); } else if(atom->name == "trkn" || atom->name == "disk") { - parseIntPair(atom, file); + parseIntPair(atom); } else if(atom->name == "cpil" || atom->name == "pgap" || atom->name == "pcst" || atom->name == "hdvd") { - parseBool(atom, file); + parseBool(atom); } else if(atom->name == "tmpo") { - parseInt(atom, file); + parseInt(atom); } else if(atom->name == "tvsn" || atom->name == "tves" || atom->name == "cnID" || atom->name == "sfID" || atom->name == "atID" || atom->name == "geID") { - parseUInt(atom, file); + parseUInt(atom); } else if(atom->name == "plID") { - parseLongLong(atom, file); + parseLongLong(atom); } else if(atom->name == "stik" || atom->name == "rtng" || atom->name == "akID") { - parseByte(atom, file); + parseByte(atom); } else if(atom->name == "gnre") { - parseGnre(atom, file); + parseGnre(atom); } else if(atom->name == "covr") { - parseCovr(atom, file); + parseCovr(atom); } else { - parseText(atom, file); + parseText(atom); } } } @@ -103,15 +103,20 @@ MP4::Tag::~Tag() } MP4::AtomDataList -MP4::Tag::parseData2(MP4::Atom *atom, TagLib::File *file, int expectedFlags, bool freeForm) +MP4::Tag::parseData2(const MP4::Atom *atom, int expectedFlags, bool freeForm) { AtomDataList result; - ByteVector data = file->readBlock(atom->length - 8); + ByteVector data = d->file->readBlock(atom->length - 8); int i = 0; unsigned int pos = 0; while(pos < data.size()) { const int length = static_cast(data.toUInt(pos)); - ByteVector name = data.mid(pos + 4, 4); + if(length < 12) { + debug("MP4: Too short atom"); + return result; + } + + const ByteVector name = data.mid(pos + 4, 4); const int flags = static_cast(data.toUInt(pos + 8)); if(freeForm && i < 2) { if(i == 0 && name != "mean") { @@ -140,9 +145,9 @@ MP4::Tag::parseData2(MP4::Atom *atom, TagLib::File *file, int expectedFlags, boo } ByteVectorList -MP4::Tag::parseData(MP4::Atom *atom, TagLib::File *file, int expectedFlags, bool freeForm) +MP4::Tag::parseData(const MP4::Atom *atom, int expectedFlags, bool freeForm) { - AtomDataList data = parseData2(atom, file, expectedFlags, freeForm); + AtomDataList data = parseData2(atom, expectedFlags, freeForm); ByteVectorList result; for(uint i = 0; i < data.size(); i++) { result.append(data[i].data); @@ -151,45 +156,45 @@ MP4::Tag::parseData(MP4::Atom *atom, TagLib::File *file, int expectedFlags, bool } void -MP4::Tag::parseInt(MP4::Atom *atom, TagLib::File *file) +MP4::Tag::parseInt(const MP4::Atom *atom) { - ByteVectorList data = parseData(atom, file); + ByteVectorList data = parseData(atom); if(data.size()) { addItem(atom->name, (int)data[0].toShort()); } } void -MP4::Tag::parseUInt(MP4::Atom *atom, TagLib::File *file) +MP4::Tag::parseUInt(const MP4::Atom *atom) { - ByteVectorList data = parseData(atom, file); + ByteVectorList data = parseData(atom); if(data.size()) { addItem(atom->name, data[0].toUInt()); } } void -MP4::Tag::parseLongLong(MP4::Atom *atom, TagLib::File *file) +MP4::Tag::parseLongLong(const MP4::Atom *atom) { - ByteVectorList data = parseData(atom, file); + ByteVectorList data = parseData(atom); if(data.size()) { addItem(atom->name, data[0].toLongLong()); } } void -MP4::Tag::parseByte(MP4::Atom *atom, TagLib::File *file) +MP4::Tag::parseByte(const MP4::Atom *atom) { - ByteVectorList data = parseData(atom, file); + ByteVectorList data = parseData(atom); if(data.size()) { addItem(atom->name, (uchar)data[0].at(0)); } } void -MP4::Tag::parseGnre(MP4::Atom *atom, TagLib::File *file) +MP4::Tag::parseGnre(const MP4::Atom *atom) { - ByteVectorList data = parseData(atom, file); + ByteVectorList data = parseData(atom); if(data.size()) { int idx = (int)data[0].toShort(); if(idx > 0) { @@ -199,9 +204,9 @@ MP4::Tag::parseGnre(MP4::Atom *atom, TagLib::File *file) } void -MP4::Tag::parseIntPair(MP4::Atom *atom, TagLib::File *file) +MP4::Tag::parseIntPair(const MP4::Atom *atom) { - ByteVectorList data = parseData(atom, file); + ByteVectorList data = parseData(atom); if(data.size()) { const int a = data[0].toShort(2U); const int b = data[0].toShort(4U); @@ -210,9 +215,9 @@ MP4::Tag::parseIntPair(MP4::Atom *atom, TagLib::File *file) } void -MP4::Tag::parseBool(MP4::Atom *atom, TagLib::File *file) +MP4::Tag::parseBool(const MP4::Atom *atom) { - ByteVectorList data = parseData(atom, file); + ByteVectorList data = parseData(atom); if(data.size()) { bool value = data[0].size() ? data[0][0] != '\0' : false; addItem(atom->name, value); @@ -220,9 +225,9 @@ MP4::Tag::parseBool(MP4::Atom *atom, TagLib::File *file) } void -MP4::Tag::parseText(MP4::Atom *atom, TagLib::File *file, int expectedFlags) +MP4::Tag::parseText(const MP4::Atom *atom, int expectedFlags) { - ByteVectorList data = parseData(atom, file, expectedFlags); + ByteVectorList data = parseData(atom, expectedFlags); if(data.size()) { StringList value; for(unsigned int i = 0; i < data.size(); i++) { @@ -233,9 +238,9 @@ MP4::Tag::parseText(MP4::Atom *atom, TagLib::File *file, int expectedFlags) } void -MP4::Tag::parseFreeForm(MP4::Atom *atom, TagLib::File *file) +MP4::Tag::parseFreeForm(const MP4::Atom *atom) { - AtomDataList data = parseData2(atom, file, -1, true); + AtomDataList data = parseData2(atom, -1, true); if(data.size() > 2) { String name = "----:" + String(data[0].data, String::UTF8) + ':' + String(data[1].data, String::UTF8); AtomDataType type = data[2].type; @@ -267,20 +272,26 @@ MP4::Tag::parseFreeForm(MP4::Atom *atom, TagLib::File *file) } void -MP4::Tag::parseCovr(MP4::Atom *atom, TagLib::File *file) +MP4::Tag::parseCovr(const MP4::Atom *atom) { MP4::CoverArtList value; - ByteVector data = file->readBlock(atom->length - 8); + ByteVector data = d->file->readBlock(atom->length - 8); unsigned int pos = 0; while(pos < data.size()) { const int length = static_cast(data.toUInt(pos)); - ByteVector name = data.mid(pos + 4, 4); + if(length < 12) { + debug("MP4: Too short atom"); + break;; + } + + const ByteVector name = data.mid(pos + 4, 4); const int flags = static_cast(data.toUInt(pos + 8)); if(name != "data") { debug("MP4: Unexpected atom \"" + name + "\", expecting \"data\""); break; } - if(flags == TypeJPEG || flags == TypePNG || flags == TypeBMP || flags == TypeGIF || flags == TypeImplicit) { + if(flags == TypeJPEG || flags == TypePNG || flags == TypeBMP || + flags == TypeGIF || flags == TypeImplicit) { value.append(MP4::CoverArt(MP4::CoverArt::Format(flags), data.mid(pos + 16, length - 16))); } @@ -294,22 +305,22 @@ MP4::Tag::parseCovr(MP4::Atom *atom, TagLib::File *file) } ByteVector -MP4::Tag::padIlst(const ByteVector &data, int length) +MP4::Tag::padIlst(const ByteVector &data, int length) const { - if (length == -1) { + if(length == -1) { length = ((data.size() + 1023) & ~1023) - data.size(); } return renderAtom("free", ByteVector(length, '\1')); } ByteVector -MP4::Tag::renderAtom(const ByteVector &name, const ByteVector &data) +MP4::Tag::renderAtom(const ByteVector &name, const ByteVector &data) const { return ByteVector::fromUInt(data.size() + 8) + name + data; } ByteVector -MP4::Tag::renderData(const ByteVector &name, int flags, const ByteVectorList &data) +MP4::Tag::renderData(const ByteVector &name, int flags, const ByteVectorList &data) const { ByteVector result; for(unsigned int i = 0; i < data.size(); i++) { @@ -319,7 +330,7 @@ MP4::Tag::renderData(const ByteVector &name, int flags, const ByteVectorList &da } ByteVector -MP4::Tag::renderBool(const ByteVector &name, MP4::Item &item) +MP4::Tag::renderBool(const ByteVector &name, const MP4::Item &item) const { ByteVectorList data; data.append(ByteVector(1, item.toBool() ? '\1' : '\0')); @@ -327,7 +338,7 @@ MP4::Tag::renderBool(const ByteVector &name, MP4::Item &item) } ByteVector -MP4::Tag::renderInt(const ByteVector &name, MP4::Item &item) +MP4::Tag::renderInt(const ByteVector &name, const MP4::Item &item) const { ByteVectorList data; data.append(ByteVector::fromShort(item.toInt())); @@ -335,7 +346,7 @@ MP4::Tag::renderInt(const ByteVector &name, MP4::Item &item) } ByteVector -MP4::Tag::renderUInt(const ByteVector &name, MP4::Item &item) +MP4::Tag::renderUInt(const ByteVector &name, const MP4::Item &item) const { ByteVectorList data; data.append(ByteVector::fromUInt(item.toUInt())); @@ -343,7 +354,7 @@ MP4::Tag::renderUInt(const ByteVector &name, MP4::Item &item) } ByteVector -MP4::Tag::renderLongLong(const ByteVector &name, MP4::Item &item) +MP4::Tag::renderLongLong(const ByteVector &name, const MP4::Item &item) const { ByteVectorList data; data.append(ByteVector::fromLongLong(item.toLongLong())); @@ -351,7 +362,7 @@ MP4::Tag::renderLongLong(const ByteVector &name, MP4::Item &item) } ByteVector -MP4::Tag::renderByte(const ByteVector &name, MP4::Item &item) +MP4::Tag::renderByte(const ByteVector &name, const MP4::Item &item) const { ByteVectorList data; data.append(ByteVector(1, item.toByte())); @@ -359,7 +370,7 @@ MP4::Tag::renderByte(const ByteVector &name, MP4::Item &item) } ByteVector -MP4::Tag::renderIntPair(const ByteVector &name, MP4::Item &item) +MP4::Tag::renderIntPair(const ByteVector &name, const MP4::Item &item) const { ByteVectorList data; data.append(ByteVector(2, '\0') + @@ -370,7 +381,7 @@ MP4::Tag::renderIntPair(const ByteVector &name, MP4::Item &item) } ByteVector -MP4::Tag::renderIntPairNoTrailing(const ByteVector &name, MP4::Item &item) +MP4::Tag::renderIntPairNoTrailing(const ByteVector &name, const MP4::Item &item) const { ByteVectorList data; data.append(ByteVector(2, '\0') + @@ -380,7 +391,7 @@ MP4::Tag::renderIntPairNoTrailing(const ByteVector &name, MP4::Item &item) } ByteVector -MP4::Tag::renderText(const ByteVector &name, MP4::Item &item, int flags) +MP4::Tag::renderText(const ByteVector &name, const MP4::Item &item, int flags) const { ByteVectorList data; StringList value = item.toStringList(); @@ -391,7 +402,7 @@ MP4::Tag::renderText(const ByteVector &name, MP4::Item &item, int flags) } ByteVector -MP4::Tag::renderCovr(const ByteVector &name, MP4::Item &item) +MP4::Tag::renderCovr(const ByteVector &name, const MP4::Item &item) const { ByteVector data; MP4::CoverArtList value = item.toCoverArtList(); @@ -403,7 +414,7 @@ MP4::Tag::renderCovr(const ByteVector &name, MP4::Item &item) } ByteVector -MP4::Tag::renderFreeForm(const String &name, MP4::Item &item) +MP4::Tag::renderFreeForm(const String &name, const MP4::Item &item) const { StringList header = StringList::split(name, ":"); if (header.size() != 3) { @@ -441,38 +452,38 @@ bool MP4::Tag::save() { ByteVector data; - for(MP4::ItemListMap::Iterator i = d->items.begin(); i != d->items.end(); i++) { - const String name = i->first; + for(MP4::ItemMap::ConstIterator it = d->items.begin(); it != d->items.end(); ++it) { + const String name = it->first; if(name.startsWith("----")) { - data.append(renderFreeForm(name, i->second)); + data.append(renderFreeForm(name, it->second)); } else if(name == "trkn") { - data.append(renderIntPair(name.data(String::Latin1), i->second)); + data.append(renderIntPair(name.data(String::Latin1), it->second)); } else if(name == "disk") { - data.append(renderIntPairNoTrailing(name.data(String::Latin1), i->second)); + data.append(renderIntPairNoTrailing(name.data(String::Latin1), it->second)); } else if(name == "cpil" || name == "pgap" || name == "pcst" || name == "hdvd") { - data.append(renderBool(name.data(String::Latin1), i->second)); + data.append(renderBool(name.data(String::Latin1), it->second)); } else if(name == "tmpo") { - data.append(renderInt(name.data(String::Latin1), i->second)); + data.append(renderInt(name.data(String::Latin1), it->second)); } else if(name == "tvsn" || name == "tves" || name == "cnID" || name == "sfID" || name == "atID" || name == "geID") { - data.append(renderUInt(name.data(String::Latin1), i->second)); + data.append(renderUInt(name.data(String::Latin1), it->second)); } else if(name == "plID") { - data.append(renderLongLong(name.data(String::Latin1), i->second)); + data.append(renderLongLong(name.data(String::Latin1), it->second)); } else if(name == "stik" || name == "rtng" || name == "akID") { - data.append(renderByte(name.data(String::Latin1), i->second)); + data.append(renderByte(name.data(String::Latin1), it->second)); } else if(name == "covr") { - data.append(renderCovr(name.data(String::Latin1), i->second)); + data.append(renderCovr(name.data(String::Latin1), it->second)); } else if(name.size() == 4){ - data.append(renderText(name.data(String::Latin1), i->second)); + data.append(renderText(name.data(String::Latin1), it->second)); } else { debug("MP4: Unknown item name \"" + name + "\""); @@ -492,7 +503,7 @@ MP4::Tag::save() } void -MP4::Tag::updateParents(AtomList &path, long delta, int ignore) +MP4::Tag::updateParents(const AtomList &path, long delta, int ignore) { for(unsigned int i = 0; i < path.size() - ignore; i++) { d->file->seek(path[i]->offset); @@ -585,10 +596,11 @@ MP4::Tag::updateOffsets(long delta, long offset) } void -MP4::Tag::saveNew(ByteVector &data) +MP4::Tag::saveNew(ByteVector data) { - data = renderAtom("meta", TagLib::ByteVector(4, '\0') + - renderAtom("hdlr", TagLib::ByteVector(8, '\0') + TagLib::ByteVector("mdirappl") + TagLib::ByteVector(9, '\0')) + + data = renderAtom("meta", ByteVector(4, '\0') + + renderAtom("hdlr", ByteVector(8, '\0') + ByteVector("mdirappl") + + ByteVector(9, '\0')) + data + padIlst(data)); AtomList path = d->atoms->path("moov", "udta"); @@ -605,18 +617,18 @@ MP4::Tag::saveNew(ByteVector &data) } void -MP4::Tag::saveExisting(ByteVector &data, AtomList &path) +MP4::Tag::saveExisting(ByteVector data, const AtomList &path) { MP4::Atom *ilst = path[path.size() - 1]; long offset = ilst->offset; long length = ilst->length; MP4::Atom *meta = path[path.size() - 2]; - AtomList::Iterator index = meta->children.find(ilst); + AtomList::ConstIterator index = meta->children.find(ilst); // check if there is an atom before 'ilst', and possibly use it as padding if(index != meta->children.begin()) { - AtomList::Iterator prevIndex = index; + AtomList::ConstIterator prevIndex = index; prevIndex--; MP4::Atom *prev = *prevIndex; if(prev->name == "free") { @@ -625,7 +637,7 @@ MP4::Tag::saveExisting(ByteVector &data, AtomList &path) } } // check if there is an atom after 'ilst', and possibly use it as padding - AtomList::Iterator nextIndex = index; + AtomList::ConstIterator nextIndex = index; nextIndex++; if(nextIndex != meta->children.end()) { MP4::Atom *next = *nextIndex; @@ -750,12 +762,41 @@ MP4::Tag::setTrack(uint value) d->items["trkn"] = MP4::Item(value, 0); } -MP4::ItemListMap & -MP4::Tag::itemListMap() +bool MP4::Tag::isEmpty() const +{ + return d->items.isEmpty(); +} + +MP4::ItemMap &MP4::Tag::itemListMap() { return d->items; } +const MP4::ItemMap &MP4::Tag::itemMap() const +{ + return d->items; +} + +MP4::Item MP4::Tag::item(const String &key) const +{ + return d->items[key]; +} + +void MP4::Tag::setItem(const String &key, const Item &value) +{ + d->items[key] = value; +} + +void MP4::Tag::removeItem(const String &key) +{ + d->items.erase(key); +} + +bool MP4::Tag::contains(const String &key) const +{ + return d->items.contains(key); +} + static const char *keyTranslation[][2] = { { "\251nam", "TITLE" }, { "\251ART", "ARTIST" }, @@ -765,6 +806,7 @@ static const char *keyTranslation[][2] = { { "\251day", "DATE" }, { "\251wrt", "COMPOSER" }, { "\251grp", "GROUPING" }, + { "aART", "ALBUMARTIST" }, { "trkn", "TRACKNUMBER" }, { "disk", "DISCNUMBER" }, { "cpil", "COMPILATION" }, @@ -816,7 +858,7 @@ PropertyMap MP4::Tag::properties() const } PropertyMap props; - MP4::ItemListMap::ConstIterator it = d->items.begin(); + MP4::ItemMap::ConstIterator it = d->items.begin(); for(; it != d->items.end(); ++it) { if(keyMap.contains(it->first)) { String key = keyMap[it->first]; @@ -875,7 +917,7 @@ PropertyMap MP4::Tag::setProperties(const PropertyMap &props) for(; it != props.end(); ++it) { if(reverseKeyMap.contains(it->first)) { String name = reverseKeyMap[it->first]; - if(it->first == "TRACKNUMBER" || it->first == "DISCNUMBER") { + if((it->first == "TRACKNUMBER" || it->first == "DISCNUMBER") && !it->second.isEmpty()) { int first = 0, second = 0; StringList parts = StringList::split(it->second.front(), "/"); if(parts.size() > 0) { @@ -886,11 +928,11 @@ PropertyMap MP4::Tag::setProperties(const PropertyMap &props) d->items[name] = MP4::Item(first, second); } } - else if(it->first == "BPM") { + else if(it->first == "BPM" && !it->second.isEmpty()) { int value = it->second.front().toInt(); d->items[name] = MP4::Item(value); } - else if(it->first == "COMPILATION") { + else if(it->first == "COMPILATION" && !it->second.isEmpty()) { bool value = (it->second.front().toInt() != 0); d->items[name] = MP4::Item(value); } diff --git a/3rdparty/taglib/mp4/mp4tag.h b/3rdparty/taglib/mp4/mp4tag.h index 48d71fcb9..c299c59b9 100644 --- a/3rdparty/taglib/mp4/mp4tag.h +++ b/3rdparty/taglib/mp4/mp4tag.h @@ -39,7 +39,11 @@ namespace TagLib { namespace MP4 { + /*! + * \deprecated + */ typedef TagLib::Map ItemListMap; + typedef TagLib::Map ItemMap; class TAGLIB_EXPORT Tag: public TagLib::Tag { @@ -65,45 +69,80 @@ namespace TagLib { void setYear(uint value); void setTrack(uint value); - ItemListMap &itemListMap(); + virtual bool isEmpty() const; + + /*! + * \deprecated Use the item() and setItem() API instead + */ + ItemMap &itemListMap(); + + /*! + * Returns a string-keyed map of the MP4::Items for this tag. + */ + const ItemMap &itemMap() const; + + /*! + * \return The item, if any, corresponding to \a key. + */ + Item item(const String &key) const; + + /*! + * Sets the value of \a key to \a value, overwriting any previous value. + */ + void setItem(const String &key, const Item &value); + + /*! + * Removes the entry with \a key from the tag, or does nothing if it does + * not exist. + */ + void removeItem(const String &key); + + /*! + * \return True if the tag contains an entry for \a key. + */ + bool contains(const String &key) const; PropertyMap properties() const; void removeUnsupportedProperties(const StringList& properties); PropertyMap setProperties(const PropertyMap &properties); private: - AtomDataList parseData2(Atom *atom, TagLib::File *file, int expectedFlags = -1, bool freeForm = false); - TagLib::ByteVectorList parseData(Atom *atom, TagLib::File *file, int expectedFlags = -1, bool freeForm = false); - void parseText(Atom *atom, TagLib::File *file, int expectedFlags = 1); - void parseFreeForm(Atom *atom, TagLib::File *file); - void parseInt(Atom *atom, TagLib::File *file); - void parseByte(Atom *atom, TagLib::File *file); - void parseUInt(Atom *atom, TagLib::File *file); - void parseLongLong(Atom *atom, TagLib::File *file); - void parseGnre(Atom *atom, TagLib::File *file); - void parseIntPair(Atom *atom, TagLib::File *file); - void parseBool(Atom *atom, TagLib::File *file); - void parseCovr(Atom *atom, TagLib::File *file); + AtomDataList parseData2(const Atom *atom, int expectedFlags = -1, + bool freeForm = false); + ByteVectorList parseData(const Atom *atom, int expectedFlags = -1, + bool freeForm = false); + void parseText(const Atom *atom, int expectedFlags = 1); + void parseFreeForm(const Atom *atom); + void parseInt(const Atom *atom); + void parseByte(const Atom *atom); + void parseUInt(const Atom *atom); + void parseLongLong(const Atom *atom); + void parseGnre(const Atom *atom); + void parseIntPair(const Atom *atom); + void parseBool(const Atom *atom); + void parseCovr(const Atom *atom); - TagLib::ByteVector padIlst(const ByteVector &data, int length = -1); - TagLib::ByteVector renderAtom(const ByteVector &name, const TagLib::ByteVector &data); - TagLib::ByteVector renderData(const ByteVector &name, int flags, const TagLib::ByteVectorList &data); - TagLib::ByteVector renderText(const ByteVector &name, Item &item, int flags = TypeUTF8); - TagLib::ByteVector renderFreeForm(const String &name, Item &item); - TagLib::ByteVector renderBool(const ByteVector &name, Item &item); - TagLib::ByteVector renderInt(const ByteVector &name, Item &item); - TagLib::ByteVector renderByte(const ByteVector &name, Item &item); - TagLib::ByteVector renderUInt(const ByteVector &name, Item &item); - TagLib::ByteVector renderLongLong(const ByteVector &name, Item &item); - TagLib::ByteVector renderIntPair(const ByteVector &name, Item &item); - TagLib::ByteVector renderIntPairNoTrailing(const ByteVector &name, Item &item); - TagLib::ByteVector renderCovr(const ByteVector &name, Item &item); + ByteVector padIlst(const ByteVector &data, int length = -1) const; + ByteVector renderAtom(const ByteVector &name, const ByteVector &data) const; + ByteVector renderData(const ByteVector &name, int flags, + const ByteVectorList &data) const; + ByteVector renderText(const ByteVector &name, const Item &item, + int flags = TypeUTF8) const; + ByteVector renderFreeForm(const String &name, const Item &item) const; + ByteVector renderBool(const ByteVector &name, const Item &item) const; + ByteVector renderInt(const ByteVector &name, const Item &item) const; + ByteVector renderByte(const ByteVector &name, const Item &item) const; + ByteVector renderUInt(const ByteVector &name, const Item &item) const; + ByteVector renderLongLong(const ByteVector &name, const Item &item) const; + ByteVector renderIntPair(const ByteVector &name, const Item &item) const; + ByteVector renderIntPairNoTrailing(const ByteVector &name, const Item &item) const; + ByteVector renderCovr(const ByteVector &name, const Item &item) const; - void updateParents(AtomList &path, long delta, int ignore = 0); + void updateParents(const AtomList &path, long delta, int ignore = 0); void updateOffsets(long delta, long offset); - void saveNew(TagLib::ByteVector &data); - void saveExisting(TagLib::ByteVector &data, AtomList &path); + void saveNew(ByteVector data); + void saveExisting(ByteVector data, const AtomList &path); void addItem(const String &name, const Item &value); diff --git a/3rdparty/taglib/mpc/mpcfile.cpp b/3rdparty/taglib/mpc/mpcfile.cpp index 979c035e9..cf5f38d8a 100644 --- a/3rdparty/taglib/mpc/mpcfile.cpp +++ b/3rdparty/taglib/mpc/mpcfile.cpp @@ -53,7 +53,6 @@ public: ID3v2Location(-1), ID3v2Size(0), properties(0), - scanned(false), hasAPE(false), hasID3v1(false), hasID3v2(false) {} @@ -76,7 +75,6 @@ public: TagUnion tag; Properties *properties; - bool scanned; // These indicate whether the file *on disk* has these tags, not if // this data structure does. This is used in computing offsets. @@ -90,20 +88,20 @@ public: // public members //////////////////////////////////////////////////////////////////////////////// -MPC::File::File(FileName file, bool readProperties, - Properties::ReadStyle propertiesStyle) : TagLib::File(file) +MPC::File::File(FileName file, bool readProperties, Properties::ReadStyle) : + TagLib::File(file), + d(new FilePrivate()) { - d = new FilePrivate; if(isOpen()) - read(readProperties, propertiesStyle); + read(readProperties); } -MPC::File::File(IOStream *stream, bool readProperties, - Properties::ReadStyle propertiesStyle) : TagLib::File(stream) +MPC::File::File(IOStream *stream, bool readProperties, Properties::ReadStyle) : + TagLib::File(stream), + d(new FilePrivate()) { - d = new FilePrivate; if(isOpen()) - read(readProperties, propertiesStyle); + read(readProperties); } MPC::File::~File() @@ -270,7 +268,7 @@ bool MPC::File::hasAPETag() const // private members //////////////////////////////////////////////////////////////////////////////// -void MPC::File::read(bool readProperties, Properties::ReadStyle /* propertiesStyle */) +void MPC::File::read(bool readProperties) { // Look for an ID3v1 tag @@ -283,8 +281,6 @@ void MPC::File::read(bool readProperties, Properties::ReadStyle /* propertiesSty // Look for an APE tag - findAPE(); - d->APELocation = findAPE(); if(d->APELocation >= 0) { @@ -298,7 +294,7 @@ void MPC::File::read(bool readProperties, Properties::ReadStyle /* propertiesSty if(!d->hasID3v1) APETag(true); - // Look for and skip an ID3v2 tag + // Look for an ID3v2 tag d->ID3v2Location = findID3v2(); @@ -309,15 +305,28 @@ void MPC::File::read(bool readProperties, Properties::ReadStyle /* propertiesSty d->hasID3v2 = true; } - if(d->hasID3v2) - seek(d->ID3v2Location + d->ID3v2Size); - else - seek(0); - // Look for MPC metadata if(readProperties) { - d->properties = new Properties(this, length() - d->ID3v2Size - d->APESize); + + long streamLength; + + if(d->hasAPE) + streamLength = d->APELocation; + else if(d->hasID3v1) + streamLength = d->ID3v1Location; + else + streamLength = length(); + + if(d->hasID3v2) { + seek(d->ID3v2Location + d->ID3v2Size); + streamLength -= (d->ID3v2Location + d->ID3v2Size); + } + else { + seek(0); + } + + d->properties = new Properties(this, streamLength); } } diff --git a/3rdparty/taglib/mpc/mpcfile.h b/3rdparty/taglib/mpc/mpcfile.h index 1eef81032..0980a5cd3 100644 --- a/3rdparty/taglib/mpc/mpcfile.h +++ b/3rdparty/taglib/mpc/mpcfile.h @@ -139,6 +139,11 @@ namespace TagLib { /*! * Saves the file. + * + * This returns true if the save was successful. + * + * \warning In the current implementation, it's dangerous to call save() + * repeatedly. At worst it will corrupt the file. */ virtual bool save(); @@ -149,8 +154,8 @@ namespace TagLib { * if there is no valid APE tag. If \a create is true it will create * an APE tag if one does not exist and returns a valid pointer. * - * \note This may return a valid pointer regardless of whether or not the - * file on disk has an ID3v1 tag. Use hasID3v1Tag() to check if the file + * \note This may return a valid pointer regardless of whether or not the + * file on disk has an ID3v1 tag. Use hasID3v1Tag() to check if the file * on disk actually has an ID3v1 tag. * * \note The Tag is still owned by the MPEG::File and should not be @@ -166,11 +171,11 @@ namespace TagLib { * * If \a create is false (the default) this may return a null pointer * if there is no valid APE tag. If \a create is true it will create - * an APE tag if one does not exist and returns a valid pointer. If + * an APE tag if one does not exist and returns a valid pointer. If * there already be an ID3v1 tag, the new APE tag will be placed before it. * - * \note This may return a valid pointer regardless of whether or not the - * file on disk has an APE tag. Use hasAPETag() to check if the file + * \note This may return a valid pointer regardless of whether or not the + * file on disk has an APE tag. Use hasAPETag() to check if the file * on disk actually has an APE tag. * * \note The Tag is still owned by the MPEG::File and should not be @@ -216,8 +221,7 @@ namespace TagLib { File(const File &); File &operator=(const File &); - void read(bool readProperties, Properties::ReadStyle propertiesStyle); - void scan(); + void read(bool readProperties); long findAPE(); long findID3v1(); long findID3v2(); diff --git a/3rdparty/taglib/mpc/mpcproperties.cpp b/3rdparty/taglib/mpc/mpcproperties.cpp index d406f8d73..2eb2f2a69 100644 --- a/3rdparty/taglib/mpc/mpcproperties.cpp +++ b/3rdparty/taglib/mpc/mpcproperties.cpp @@ -36,9 +36,7 @@ using namespace TagLib; class MPC::Properties::PropertiesPrivate { public: - PropertiesPrivate(long length, ReadStyle s) : - streamLength(length), - style(s), + PropertiesPrivate() : version(0), length(0), bitrate(0), @@ -51,8 +49,6 @@ public: albumGain(0), albumPeak(0) {} - long streamLength; - ReadStyle style; int version; int length; int bitrate; @@ -71,23 +67,25 @@ public: // public members //////////////////////////////////////////////////////////////////////////////// -MPC::Properties::Properties(const ByteVector &data, long streamLength, ReadStyle style) : AudioProperties(style) +MPC::Properties::Properties(const ByteVector &data, long streamLength, ReadStyle style) : + AudioProperties(style), + d(new PropertiesPrivate()) { - d = new PropertiesPrivate(streamLength, style); - readSV7(data); + readSV7(data, streamLength); } -MPC::Properties::Properties(File *file, long streamLength, ReadStyle style) : AudioProperties(style) +MPC::Properties::Properties(File *file, long streamLength, ReadStyle style) : + AudioProperties(style), + d(new PropertiesPrivate()) { - d = new PropertiesPrivate(streamLength, style); ByteVector magic = file->readBlock(4); if(magic == "MPCK") { // Musepack version 8 - readSV8(file); + readSV8(file, streamLength); } else { // Musepack version 7 or older, fixed size header - readSV7(magic + file->readBlock(MPC::HeaderSize - 4)); + readSV7(magic + file->readBlock(MPC::HeaderSize - 4), streamLength); } } @@ -97,6 +95,16 @@ MPC::Properties::~Properties() } int MPC::Properties::length() const +{ + return lengthInSeconds(); +} + +int MPC::Properties::lengthInSeconds() const +{ + return d->length / 1000; +} + +int MPC::Properties::lengthInMilliseconds() const { return d->length; } @@ -155,78 +163,121 @@ int MPC::Properties::albumPeak() const // private members //////////////////////////////////////////////////////////////////////////////// -unsigned long readSize(File *file, TagLib::uint &sizelength) +unsigned long readSize(File *file, TagLib::uint &sizeLength, bool &eof) { + sizeLength = 0; + eof = false; + unsigned char tmp; unsigned long size = 0; do { - ByteVector b = file->readBlock(1); + const ByteVector b = file->readBlock(1); + if(b.isEmpty()) { + eof = true; + break; + } + tmp = b[0]; size = (size << 7) | (tmp & 0x7F); - sizelength++; + sizeLength++; } while((tmp & 0x80)); return size; } -unsigned long readSize(const ByteVector &data, TagLib::uint &sizelength) +unsigned long readSize(const ByteVector &data, TagLib::uint &pos) { unsigned char tmp; unsigned long size = 0; - unsigned long pos = 0; do { tmp = data[pos++]; size = (size << 7) | (tmp & 0x7F); - sizelength++; } while((tmp & 0x80) && (pos < data.size())); return size; } -static const unsigned short sftable [4] = { 44100, 48000, 37800, 32000 }; +namespace +{ + // This array looks weird, but the same as original MusePack code found at: + // https://www.musepack.net/index.php?pg=src + const unsigned short sftable [8] = { 44100, 48000, 37800, 32000, 0, 0, 0, 0 }; +} -void MPC::Properties::readSV8(File *file) +void MPC::Properties::readSV8(File *file, long streamLength) { bool readSH = false, readRG = false; while(!readSH && !readRG) { - ByteVector packetType = file->readBlock(2); - uint packetSizeLength = 0; - unsigned long packetSize = readSize(file, packetSizeLength); - unsigned long dataSize = packetSize - 2 - packetSizeLength; + const ByteVector packetType = file->readBlock(2); + + uint packetSizeLength; + bool eof; + const unsigned long packetSize = readSize(file, packetSizeLength, eof); + if(eof) { + debug("MPC::Properties::readSV8() - Reached to EOF."); + break; + } + + const unsigned long dataSize = packetSize - 2 - packetSizeLength; + + const ByteVector data = file->readBlock(dataSize); + if(data.size() != dataSize) { + debug("MPC::Properties::readSV8() - dataSize doesn't match the actual data size."); + break; + } if(packetType == "SH") { // Stream Header // http://trac.musepack.net/wiki/SV8Specification#StreamHeaderPacket - ByteVector data = file->readBlock(dataSize); + + if(dataSize <= 5) { + debug("MPC::Properties::readSV8() - \"SH\" packet is too short to parse."); + break; + } + readSH = true; TagLib::uint pos = 4; d->version = data[pos]; pos += 1; - d->sampleFrames = readSize(data.mid(pos), pos); - ulong begSilence = readSize(data.mid(pos), pos); + d->sampleFrames = readSize(data, pos); + if(pos > dataSize - 3) { + debug("MPC::Properties::readSV8() - \"SH\" packet is corrupt."); + break; + } - std::bitset<16> flags(TAGLIB_CONSTRUCT_BITSET(data.toUShort(pos, true))); + const ulong begSilence = readSize(data, pos); + if(pos > dataSize - 2) { + debug("MPC::Properties::readSV8() - \"SH\" packet is corrupt."); + break; + } + + const ushort flags = data.toUShort(pos, true); pos += 2; - d->sampleRate = sftable[flags[15] * 4 + flags[14] * 2 + flags[13]]; - d->channels = flags[7] * 8 + flags[6] * 4 + flags[5] * 2 + flags[4] + 1; + d->sampleRate = sftable[(flags >> 13) & 0x07]; + d->channels = ((flags >> 4) & 0x0F) + 1; - if((d->sampleFrames - begSilence) != 0) - d->bitrate = (int)(d->streamLength * 8.0 * d->sampleRate / (d->sampleFrames - begSilence)); - d->bitrate = d->bitrate / 1000; - - d->length = (d->sampleFrames - begSilence) / d->sampleRate; + const uint frameCount = d->sampleFrames - begSilence; + if(frameCount > 0 && d->sampleRate > 0) { + const double length = frameCount * 1000.0 / d->sampleRate; + d->length = static_cast(length + 0.5); + d->bitrate = static_cast(streamLength * 8.0 / length + 0.5); + } } - else if (packetType == "RG") { // Replay Gain // http://trac.musepack.net/wiki/SV8Specification#ReplaygainPacket - ByteVector data = file->readBlock(dataSize); + + if(dataSize <= 9) { + debug("MPC::Properties::readSV8() - \"RG\" packet is too short to parse."); + break; + } + readRG = true; - int replayGainVersion = data[0]; + const int replayGainVersion = data[0]; if(replayGainVersion == 1) { d->trackGain = data.toShort(1, true); d->trackPeak = data.toShort(3, true); @@ -245,7 +296,7 @@ void MPC::Properties::readSV8(File *file) } } -void MPC::Properties::readSV7(const ByteVector &data) +void MPC::Properties::readSV7(const ByteVector &data, long streamLength) { if(data.startsWith("MP+")) { d->version = data[3] & 15; @@ -254,11 +305,11 @@ void MPC::Properties::readSV7(const ByteVector &data) d->totalFrames = data.toUInt(4, false); - std::bitset<32> flags(TAGLIB_CONSTRUCT_BITSET(data.toUInt(8, false))); - d->sampleRate = sftable[flags[17] * 2 + flags[16]]; - d->channels = 2; + const uint flags = data.toUInt(8, false); + d->sampleRate = sftable[(flags >> 16) & 0x03]; + d->channels = 2; - uint gapless = data.toUInt(5, false); + const uint gapless = data.toUInt(5, false); d->trackGain = data.toShort(14, false); d->trackPeak = data.toShort(12, false); @@ -293,12 +344,12 @@ void MPC::Properties::readSV7(const ByteVector &data) d->sampleFrames = d->totalFrames * 1152 - 576; } else { - uint headerData = data.toUInt(0, false); + const uint headerData = data.toUInt(0, false); - d->bitrate = (headerData >> 23) & 0x01ff; - d->version = (headerData >> 11) & 0x03ff; + d->bitrate = (headerData >> 23) & 0x01ff; + d->version = (headerData >> 11) & 0x03ff; d->sampleRate = 44100; - d->channels = 2; + d->channels = 2; if(d->version >= 5) d->totalFrames = data.toUInt(4, false); @@ -308,9 +359,11 @@ void MPC::Properties::readSV7(const ByteVector &data) d->sampleFrames = d->totalFrames * 1152 - 576; } - d->length = d->sampleRate > 0 ? (d->sampleFrames + (d->sampleRate / 2)) / d->sampleRate : 0; + if(d->sampleFrames > 0 && d->sampleRate > 0) { + const double length = d->sampleFrames * 1000.0 / d->sampleRate; + d->length = static_cast(length + 0.5); - if(!d->bitrate) - d->bitrate = d->length > 0 ? ((d->streamLength * 8L) / d->length) / 1000 : 0; + if(d->bitrate == 0) + d->bitrate = static_cast(streamLength * 8.0 / length + 0.5); + } } - diff --git a/3rdparty/taglib/mpc/mpcproperties.h b/3rdparty/taglib/mpc/mpcproperties.h index adf40d839..d6b066919 100644 --- a/3rdparty/taglib/mpc/mpcproperties.h +++ b/3rdparty/taglib/mpc/mpcproperties.h @@ -66,17 +66,53 @@ namespace TagLib { */ virtual ~Properties(); - // Reimplementations. - + /*! + * Returns the length of the file in seconds. The length is rounded down to + * the nearest whole second. + * + * \note This method is just an alias of lengthInSeconds(). + * + * \deprecated + */ virtual int length() const; + + /*! + * Returns the length of the file in seconds. The length is rounded down to + * the nearest whole second. + * + * \see lengthInMilliseconds() + */ + // BIC: make virtual + int lengthInSeconds() const; + + /*! + * Returns the length of the file in milliseconds. + * + * \see lengthInSeconds() + */ + // BIC: make virtual + int lengthInMilliseconds() const; + + /*! + * Returns the average bit rate of the file in kb/s. + */ virtual int bitrate() const; + + /*! + * Returns the sample rate in Hz. + */ virtual int sampleRate() const; + + /*! + * Returns the number of audio channels. + */ virtual int channels() const; /*! * Returns the version of the bitstream (SV4-SV8) */ int mpcVersion() const; + uint totalFrames() const; uint sampleFrames() const; @@ -110,8 +146,8 @@ namespace TagLib { Properties(const Properties &); Properties &operator=(const Properties &); - void readSV7(const ByteVector &data); - void readSV8(File *file); + void readSV7(const ByteVector &data, long streamLength); + void readSV8(File *file, long streamLength); class PropertiesPrivate; PropertiesPrivate *d; diff --git a/3rdparty/taglib/mpeg/id3v1/id3v1genres.cpp b/3rdparty/taglib/mpeg/id3v1/id3v1genres.cpp index eba4c526e..074c8bff3 100644 --- a/3rdparty/taglib/mpeg/id3v1/id3v1genres.cpp +++ b/3rdparty/taglib/mpeg/id3v1/id3v1genres.cpp @@ -30,7 +30,7 @@ using namespace TagLib; namespace TagLib { namespace ID3v1 { - static const int genresSize = 148; + static const int genresSize = 192; static const String genres[] = { "Blues", "Classic Rock", @@ -179,7 +179,51 @@ namespace TagLib { "Thrash Metal", "Anime", "Jpop", - "Synthpop" + "Synthpop", + "Abstract", + "Art Rock", + "Baroque", + "Bhangra", + "Big Beat", + "Breakbeat", + "Chillout", + "Downtempo", + "Dub", + "EBM", + "Eclectic", + "Electro", + "Electroclash", + "Emo", + "Experimental", + "Garage", + "Global", + "IDM", + "Illbient", + "Industro-Goth", + "Jam Band", + "Krautrock", + "Leftfield", + "Lounge", + "Math Rock", + "New Romantic", + "Nu-Breakz", + "Post-Punk", + "Post-Rock", + "Psytrance", + "Shoegaze", + "Space Rock", + "Trop Rock", + "World Music", + "Neoclassical", + "Audiobook", + "Audio Theatre", + "Neue Deutsche Welle", + "Podcast", + "Indie Rock", + "G-Funk", + "Dubstep", + "Garage Rock", + "Psybient" }; } } diff --git a/3rdparty/taglib/mpeg/id3v1/id3v1genres.h b/3rdparty/taglib/mpeg/id3v1/id3v1genres.h index 271f72590..0a0dd9700 100644 --- a/3rdparty/taglib/mpeg/id3v1/id3v1genres.h +++ b/3rdparty/taglib/mpeg/id3v1/id3v1genres.h @@ -49,7 +49,7 @@ namespace TagLib { /*! * Returns the name of the genre at \a index in the ID3v1 genre list. If - * \a index is out of range -- less than zero or greater than 146 -- a null + * \a index is out of range -- less than zero or greater than 191 -- a null * string will be returned. */ String TAGLIB_EXPORT genre(int index); diff --git a/3rdparty/taglib/mpeg/id3v1/id3v1tag.h b/3rdparty/taglib/mpeg/id3v1/id3v1tag.h index 7b26d023e..4cba00dbf 100644 --- a/3rdparty/taglib/mpeg/id3v1/id3v1tag.h +++ b/3rdparty/taglib/mpeg/id3v1/id3v1tag.h @@ -85,7 +85,7 @@ namespace TagLib { //! The main class in the ID3v1 implementation /*! - * This is an implementation of the ID3v1 format. ID3v1 is both the simplist + * This is an implementation of the ID3v1 format. ID3v1 is both the simplest * and most common of tag formats but is rather limited. Because of its * pervasiveness and the way that applications have been written around the * fields that it provides, the generic TagLib::Tag API is a mirror of what is @@ -154,14 +154,14 @@ namespace TagLib { /*! * Returns the genre in number. * - * /note Normally 255 indicates that this tag contains no genre. + * \note Normally 255 indicates that this tag contains no genre. */ TagLib::uint genreNumber() const; /*! * Sets the genre in number to \a i. * - * /note Valid value is from 0 up to 255. Normally 255 indicates that + * \note Valid value is from 0 up to 255. Normally 255 indicates that * this tag contains no genre. */ void setGenreNumber(TagLib::uint i); diff --git a/3rdparty/taglib/mpeg/id3v2/frames/chapterframe.cpp b/3rdparty/taglib/mpeg/id3v2/frames/chapterframe.cpp new file mode 100644 index 000000000..e11e2928b --- /dev/null +++ b/3rdparty/taglib/mpeg/id3v2/frames/chapterframe.cpp @@ -0,0 +1,306 @@ +/*************************************************************************** + copyright : (C) 2013 by Lukas Krejci + email : krejclu6@fel.cvut.cz + ***************************************************************************/ + +/*************************************************************************** + * 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 +#include +#include +#include + +#include "chapterframe.h" + +using namespace TagLib; +using namespace ID3v2; + +class ChapterFrame::ChapterFramePrivate +{ +public: + ChapterFramePrivate() : + tagHeader(0) + { + embeddedFrameList.setAutoDelete(true); + } + + const ID3v2::Header *tagHeader; + ByteVector elementID; + TagLib::uint startTime; + TagLib::uint endTime; + TagLib::uint startOffset; + TagLib::uint endOffset; + FrameListMap embeddedFrameListMap; + FrameList embeddedFrameList; +}; + +//////////////////////////////////////////////////////////////////////////////// +// public methods +//////////////////////////////////////////////////////////////////////////////// + +ChapterFrame::ChapterFrame(const ID3v2::Header *tagHeader, const ByteVector &data) : + ID3v2::Frame(data) +{ + d = new ChapterFramePrivate; + d->tagHeader = tagHeader; + setData(data); +} + +ChapterFrame::ChapterFrame(const ByteVector &elementID, + TagLib::uint startTime, TagLib::uint endTime, + TagLib::uint startOffset, TagLib::uint endOffset, + const FrameList &embeddedFrames) : + ID3v2::Frame("CHAP") +{ + d = new ChapterFramePrivate; + + // setElementID has a workaround for a previously silly API where you had to + // specifically include the null byte. + + setElementID(elementID); + + d->startTime = startTime; + d->endTime = endTime; + d->startOffset = startOffset; + d->endOffset = endOffset; + + for(FrameList::ConstIterator it = embeddedFrames.begin(); + it != embeddedFrames.end(); ++it) + addEmbeddedFrame(*it); +} + +ChapterFrame::~ChapterFrame() +{ + delete d; +} + +ByteVector ChapterFrame::elementID() const +{ + return d->elementID; +} + +TagLib::uint ChapterFrame::startTime() const +{ + return d->startTime; +} + +TagLib::uint ChapterFrame::endTime() const +{ + return d->endTime; +} + +TagLib::uint ChapterFrame::startOffset() const +{ + return d->startOffset; +} + +TagLib::uint ChapterFrame::endOffset() const +{ + return d->endOffset; +} + +void ChapterFrame::setElementID(const ByteVector &eID) +{ + d->elementID = eID; + + if(d->elementID.endsWith(char(0))) + d->elementID = d->elementID.mid(0, d->elementID.size() - 1); +} + +void ChapterFrame::setStartTime(const TagLib::uint &sT) +{ + d->startTime = sT; +} + +void ChapterFrame::setEndTime(const TagLib::uint &eT) +{ + d->endTime = eT; +} + +void ChapterFrame::setStartOffset(const TagLib::uint &sO) +{ + d->startOffset = sO; +} + +void ChapterFrame::setEndOffset(const TagLib::uint &eO) +{ + d->endOffset = eO; +} + +const FrameListMap &ChapterFrame::embeddedFrameListMap() const +{ + return d->embeddedFrameListMap; +} + +const FrameList &ChapterFrame::embeddedFrameList() const +{ + return d->embeddedFrameList; +} + +const FrameList &ChapterFrame::embeddedFrameList(const ByteVector &frameID) const +{ + return d->embeddedFrameListMap[frameID]; +} + +void ChapterFrame::addEmbeddedFrame(Frame *frame) +{ + d->embeddedFrameList.append(frame); + d->embeddedFrameListMap[frame->frameID()].append(frame); +} + +void ChapterFrame::removeEmbeddedFrame(Frame *frame, bool del) +{ + // remove the frame from the frame list + FrameList::Iterator it = d->embeddedFrameList.find(frame); + d->embeddedFrameList.erase(it); + + // ...and from the frame list map + it = d->embeddedFrameListMap[frame->frameID()].find(frame); + d->embeddedFrameListMap[frame->frameID()].erase(it); + + // ...and delete as desired + if(del) + delete frame; +} + +void ChapterFrame::removeEmbeddedFrames(const ByteVector &id) +{ + FrameList l = d->embeddedFrameListMap[id]; + for(FrameList::ConstIterator it = l.begin(); it != l.end(); ++it) + removeEmbeddedFrame(*it, true); +} + +String ChapterFrame::toString() const +{ + String s = String(d->elementID) + + ": start time: " + String::number(d->startTime) + + ", end time: " + String::number(d->endTime); + + if(d->startOffset != 0xFFFFFFFF) + s += ", start offset: " + String::number(d->startOffset); + + if(d->endOffset != 0xFFFFFFFF) + s += ", start offset: " + String::number(d->endOffset); + + if(!d->embeddedFrameList.isEmpty()) { + StringList frameIDs; + for(FrameList::ConstIterator it = d->embeddedFrameList.begin(); + it != d->embeddedFrameList.end(); ++it) + frameIDs.append((*it)->frameID()); + s += ", sub-frames: [ " + frameIDs.toString(", ") + " ]"; + } + + return s; +} + +PropertyMap ChapterFrame::asProperties() const +{ + PropertyMap map; + + map.unsupportedData().append(frameID() + String("/") + d->elementID); + + return map; +} + +ChapterFrame *ChapterFrame::findByElementID(const ID3v2::Tag *tag, const ByteVector &eID) // static +{ + ID3v2::FrameList comments = tag->frameList("CHAP"); + + for(ID3v2::FrameList::ConstIterator it = comments.begin(); + it != comments.end(); + ++it) + { + ChapterFrame *frame = dynamic_cast(*it); + if(frame && frame->elementID() == eID) + return frame; + } + + return 0; +} + +void ChapterFrame::parseFields(const ByteVector &data) +{ + TagLib::uint size = data.size(); + if(size < 18) { + debug("A CHAP frame must contain at least 18 bytes (1 byte element ID " + "terminated by null and 4x4 bytes for start and end time and offset)."); + return; + } + + int pos = 0; + TagLib::uint embPos = 0; + d->elementID = readStringField(data, String::Latin1, &pos).data(String::Latin1); + d->startTime = data.toUInt(pos, true); + pos += 4; + d->endTime = data.toUInt(pos, true); + pos += 4; + d->startOffset = data.toUInt(pos, true); + pos += 4; + d->endOffset = data.toUInt(pos, true); + pos += 4; + size -= pos; + + // Embedded frames are optional + + if(size < header()->size()) + return; + + while(embPos < size - header()->size()) { + Frame *frame = FrameFactory::instance()->createFrame(data.mid(pos + embPos), (d->tagHeader != 0)); + + if(!frame) + return; + + // Checks to make sure that frame parsed correctly. + if(frame->size() <= 0) { + delete frame; + return; + } + + embPos += frame->size() + header()->size(); + addEmbeddedFrame(frame); + } +} + +ByteVector ChapterFrame::renderFields() const +{ + ByteVector data; + + data.append(d->elementID); + data.append('\0'); + data.append(ByteVector::fromUInt(d->startTime, true)); + data.append(ByteVector::fromUInt(d->endTime, true)); + data.append(ByteVector::fromUInt(d->startOffset, true)); + data.append(ByteVector::fromUInt(d->endOffset, true)); + FrameList l = d->embeddedFrameList; + for(FrameList::ConstIterator it = l.begin(); it != l.end(); ++it) + data.append((*it)->render()); + + return data; +} + +ChapterFrame::ChapterFrame(const ID3v2::Header *tagHeader, const ByteVector &data, Header *h) : + Frame(h) +{ + d = new ChapterFramePrivate; + d->tagHeader = tagHeader; + parseFields(fieldData(data)); +} diff --git a/3rdparty/taglib/mpeg/id3v2/frames/chapterframe.h b/3rdparty/taglib/mpeg/id3v2/frames/chapterframe.h new file mode 100644 index 000000000..368530621 --- /dev/null +++ b/3rdparty/taglib/mpeg/id3v2/frames/chapterframe.h @@ -0,0 +1,249 @@ +/*************************************************************************** + copyright : (C) 2013 by Lukas Krejci + email : krejclu6@fel.cvut.cz + ***************************************************************************/ + +/*************************************************************************** + * 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_CHAPTERFRAME +#define TAGLIB_CHAPTERFRAME + +#include "id3v2tag.h" +#include "id3v2frame.h" +#include "taglib_export.h" + +namespace TagLib { + + namespace ID3v2 { + + /*! + * This is an implementation of ID3v2 chapter frames. The purpose of this + * frame is to describe a single chapter within an audio file. + */ + + //! An implementation of ID3v2 chapter frames + + class TAGLIB_EXPORT ChapterFrame : public ID3v2::Frame + { + friend class FrameFactory; + + public: + /*! + * Creates a chapter frame based on \a data. \a tagHeader is required as + * the internal frames are parsed based on the tag version. + */ + ChapterFrame(const ID3v2::Header *tagHeader, const ByteVector &data); + + /*! + * Creates a chapter frame with the element ID \a elementID, start time + * \a startTime, end time \a endTime, start offset \a startOffset, + * end offset \a endOffset and optionally a list of embedded frames, + * whose ownership will then be taken over by this Frame, in + * \a embeededFrames; + * + * All times are in milliseconds. + */ + ChapterFrame(const ByteVector &elementID, + uint startTime, uint endTime, + uint startOffset, uint endOffset, + const FrameList &embeddedFrames = FrameList()); + + /*! + * Destroys the frame. + */ + virtual ~ChapterFrame(); + + /*! + * Returns the element ID of the frame. Element ID + * is a null terminated string, however it's not human-readable. + * + * \see setElementID() + */ + ByteVector elementID() const; + + /*! + * Returns time of chapter's start (in milliseconds). + * + * \see setStartTime() + */ + uint startTime() const; + + /*! + * Returns time of chapter's end (in milliseconds). + * + * \see setEndTime() + */ + uint endTime() const; + + /*! + * Returns zero based byte offset (count of bytes from the beginning + * of the audio file) of chapter's start. + * + * \note If returned value is 0xFFFFFFFF, start time should be used instead. + * \see setStartOffset() + */ + uint startOffset() const; + + /*! + * Returns zero based byte offset (count of bytes from the beginning + * of the audio file) of chapter's end. + * + * \note If returned value is 0xFFFFFFFF, end time should be used instead. + * \see setEndOffset() + */ + uint endOffset() const; + + /*! + * Sets the element ID of the frame to \a eID. If \a eID isn't + * null terminated, a null char is appended automatically. + * + * \see elementID() + */ + void setElementID(const ByteVector &eID); + + /*! + * Sets time of chapter's start (in milliseconds) to \a sT. + * + * \see startTime() + */ + void setStartTime(const uint &sT); + + /*! + * Sets time of chapter's end (in milliseconds) to \a eT. + * + * \see endTime() + */ + void setEndTime(const uint &eT); + + /*! + * Sets zero based byte offset (count of bytes from the beginning + * of the audio file) of chapter's start to \a sO. + * + * \see startOffset() + */ + void setStartOffset(const uint &sO); + + /*! + * Sets zero based byte offset (count of bytes from the beginning + * of the audio file) of chapter's end to \a eO. + * + * \see endOffset() + */ + void setEndOffset(const uint &eO); + + /*! + * Returns a reference to the frame list map. This is an FrameListMap of + * all of the frames embedded in the CHAP frame. + * + * This is the most convenient structure for accessing the CHAP frame's + * embedded frames. Many frame types allow multiple instances of the same + * frame type so this is a map of lists. In most cases however there will + * only be a single frame of a certain type. + * + * \warning You should not modify this data structure directly, instead + * use addEmbeddedFrame() and removeEmbeddedFrame(). + * + * \see embeddedFrameList() + */ + const FrameListMap &embeddedFrameListMap() const; + + /*! + * Returns a reference to the embedded frame list. This is an FrameList + * of all of the frames embedded in the CHAP frame in the order that they + * were parsed. + * + * This can be useful if for example you want iterate over the CHAP frame's + * embedded frames in the order that they occur in the CHAP frame. + * + * \warning You should not modify this data structure directly, instead + * use addEmbeddedFrame() and removeEmbeddedFrame(). + */ + const FrameList &embeddedFrameList() const; + + /*! + * Returns the embedded frame list for frames with the id \a frameID + * or an empty list if there are no embedded frames of that type. This + * is just a convenience and is equivalent to: + * + * \code + * embeddedFrameListMap()[frameID]; + * \endcode + * + * \see embeddedFrameListMap() + */ + const FrameList &embeddedFrameList(const ByteVector &frameID) const; + + /*! + * Add an embedded frame to the CHAP frame. At this point the CHAP frame + * takes ownership of the embedded frame and will handle freeing its memory. + * + * \note Using this method will invalidate any pointers on the list + * returned by embeddedFrameList() + */ + void addEmbeddedFrame(Frame *frame); + + /*! + * Remove an embedded frame from the CHAP frame. If \a del is true the frame's + * memory will be freed; if it is false, it must be deleted by the user. + * + * \note Using this method will invalidate any pointers on the list + * returned by embeddedFrameList() + */ + void removeEmbeddedFrame(Frame *frame, bool del = true); + + /*! + * Remove all embedded frames of type \a id from the CHAP frame and free their + * memory. + * + * \note Using this method will invalidate any pointers on the list + * returned by embeddedFrameList() + */ + void removeEmbeddedFrames(const ByteVector &id); + + virtual String toString() const; + + PropertyMap asProperties() const; + + /*! + * CHAP frames each have a unique element ID. This searches for a CHAP + * frame with the element ID \a eID and returns a pointer to it. This + * can be used to link CTOC and CHAP frames together. + * + * \see elementID() + */ + static ChapterFrame *findByElementID(const Tag *tag, const ByteVector &eID); + + protected: + virtual void parseFields(const ByteVector &data); + virtual ByteVector renderFields() const; + + private: + ChapterFrame(const ID3v2::Header *tagHeader, const ByteVector &data, Header *h); + ChapterFrame(const ChapterFrame &); + ChapterFrame &operator=(const ChapterFrame &); + + class ChapterFramePrivate; + ChapterFramePrivate *d; + }; + } +} + +#endif diff --git a/3rdparty/taglib/mpeg/id3v2/frames/commentsframe.h b/3rdparty/taglib/mpeg/id3v2/frames/commentsframe.h index f65f6f01c..4da9d5357 100644 --- a/3rdparty/taglib/mpeg/id3v2/frames/commentsframe.h +++ b/3rdparty/taglib/mpeg/id3v2/frames/commentsframe.h @@ -36,7 +36,7 @@ namespace TagLib { //! An implementation of ID3v2 comments /*! - * This implements the ID3v2 comment format. An ID3v2 comment concists of + * This implements the ID3v2 comment format. An ID3v2 comment consists of * a language encoding, a description and a single text field. */ @@ -106,7 +106,7 @@ namespace TagLib { /*! * Sets the description of the comment to \a s. * - * \see decription() + * \see description() */ void setDescription(const String &s); @@ -149,7 +149,7 @@ namespace TagLib { /*! * Comments each have a unique description. This searches for a comment - * frame with the decription \a d and returns a pointer to it. If no + * frame with the description \a d and returns a pointer to it. If no * frame is found that matches the given description null is returned. * * \see description() diff --git a/3rdparty/taglib/mpeg/id3v2/frames/eventtimingcodesframe.cpp b/3rdparty/taglib/mpeg/id3v2/frames/eventtimingcodesframe.cpp new file mode 100644 index 000000000..70214badb --- /dev/null +++ b/3rdparty/taglib/mpeg/id3v2/frames/eventtimingcodesframe.cpp @@ -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 +#include +#include +#include + +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/3rdparty/taglib/mpeg/id3v2/frames/eventtimingcodesframe.h b/3rdparty/taglib/mpeg/id3v2/frames/eventtimingcodesframe.h new file mode 100644 index 000000000..0719f51f3 --- /dev/null +++ b/3rdparty/taglib/mpeg/id3v2/frames/eventtimingcodesframe.h @@ -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 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 diff --git a/3rdparty/taglib/mpeg/id3v2/frames/ownershipframe.h b/3rdparty/taglib/mpeg/id3v2/frames/ownershipframe.h index 34fc91296..06a1e2336 100644 --- a/3rdparty/taglib/mpeg/id3v2/frames/ownershipframe.h +++ b/3rdparty/taglib/mpeg/id3v2/frames/ownershipframe.h @@ -94,21 +94,21 @@ namespace TagLib { * \see pricePaid() */ void setPricePaid(const String &pricePaid); - + /*! * Returns the seller. * * \see setSeller() */ String seller() const; - + /*! * Set the seller. * * \see seller() */ void setSeller(const String &seller); - + /*! * Returns the text encoding that will be used in rendering this frame. * This defaults to the type that was either specified in the constructor @@ -118,7 +118,7 @@ namespace TagLib { * \see render() */ String::Type textEncoding() const; - + /*! * Sets the text encoding to be used when rendering this frame to * \a encoding. diff --git a/3rdparty/taglib/mpeg/id3v2/frames/popularimeterframe.h b/3rdparty/taglib/mpeg/id3v2/frames/popularimeterframe.h index d39f1aa8f..79b88cbf0 100644 --- a/3rdparty/taglib/mpeg/id3v2/frames/popularimeterframe.h +++ b/3rdparty/taglib/mpeg/id3v2/frames/popularimeterframe.h @@ -36,7 +36,7 @@ namespace TagLib { //! An implementation of ID3v2 "popularimeter" /*! - * This implements the ID3v2 popularimeter (POPM frame). It concists of + * This implements the ID3v2 popularimeter (POPM frame). It consists of * an email, a rating and an optional counter. */ diff --git a/3rdparty/taglib/mpeg/id3v2/frames/relativevolumeframe.h b/3rdparty/taglib/mpeg/id3v2/frames/relativevolumeframe.h index dad4e7d4e..695e26f50 100644 --- a/3rdparty/taglib/mpeg/id3v2/frames/relativevolumeframe.h +++ b/3rdparty/taglib/mpeg/id3v2/frames/relativevolumeframe.h @@ -140,7 +140,7 @@ namespace TagLib { /* * There was a terrible API goof here, and while this can't be changed to - * the way it appears below for binary compaibility reasons, let's at + * the way it appears below for binary compatibility reasons, let's at * least pretend that it looks clean. */ @@ -149,7 +149,7 @@ namespace TagLib { /*! * Returns the relative volume adjustment "index". As indicated by the * ID3v2 standard this is a 16-bit signed integer that reflects the - * decibils of adjustment when divided by 512. + * decibels of adjustment when divided by 512. * * This defaults to returning the value for the master volume channel if * available and returns 0 if the specified channel does not exist. @@ -161,7 +161,7 @@ namespace TagLib { /*! * Set the volume adjustment to \a index. As indicated by the ID3v2 - * standard this is a 16-bit signed integer that reflects the decibils of + * standard this is a 16-bit signed integer that reflects the decibels of * adjustment when divided by 512. * * By default this sets the value for the master volume. diff --git a/3rdparty/taglib/mpeg/id3v2/frames/synchronizedlyricsframe.cpp b/3rdparty/taglib/mpeg/id3v2/frames/synchronizedlyricsframe.cpp new file mode 100644 index 000000000..86c11f7a4 --- /dev/null +++ b/3rdparty/taglib/mpeg/id3v2/frames/synchronizedlyricsframe.cpp @@ -0,0 +1,243 @@ +/*************************************************************************** + 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 +#include +#include +#include + +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.toUInt(pos, 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)); +} diff --git a/3rdparty/taglib/mpeg/id3v2/frames/synchronizedlyricsframe.h b/3rdparty/taglib/mpeg/id3v2/frames/synchronizedlyricsframe.h new file mode 100644 index 000000000..704ac4b5b --- /dev/null +++ b/3rdparty/taglib/mpeg/id3v2/frames/synchronizedlyricsframe.h @@ -0,0 +1,231 @@ +/*************************************************************************** + 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 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 + * ISO-639-2. + * + * \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 + * ISO-639-2 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 description() + */ + 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 diff --git a/3rdparty/taglib/mpeg/id3v2/frames/tableofcontentsframe.cpp b/3rdparty/taglib/mpeg/id3v2/frames/tableofcontentsframe.cpp new file mode 100644 index 000000000..bdcc11830 --- /dev/null +++ b/3rdparty/taglib/mpeg/id3v2/frames/tableofcontentsframe.cpp @@ -0,0 +1,338 @@ +/*************************************************************************** + copyright : (C) 2013 by Lukas Krejci + email : krejclu6@fel.cvut.cz + ***************************************************************************/ + +/*************************************************************************** + * 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 +#include +#include + +#include "tableofcontentsframe.h" + +using namespace TagLib; +using namespace ID3v2; + +class TableOfContentsFrame::TableOfContentsFramePrivate +{ +public: + TableOfContentsFramePrivate() : + tagHeader(0) + { + embeddedFrameList.setAutoDelete(true); + } + + const ID3v2::Header *tagHeader; + ByteVector elementID; + bool isTopLevel; + bool isOrdered; + ByteVectorList childElements; + FrameListMap embeddedFrameListMap; + FrameList embeddedFrameList; +}; + +namespace { + + // These functions are needed to try to aim for backward compatibility with + // an API that previously (unreasonably) required null bytes to be appeneded + // at the end of identifiers explicitly by the API user. + + // BIC: remove these + + ByteVector &strip(ByteVector &b) + { + if(b.endsWith('\0')) + b.resize(b.size() - 1); + return b; + } + + ByteVectorList &strip(ByteVectorList &l) + { + for(ByteVectorList::Iterator it = l.begin(); it != l.end(); ++it) + { + strip(*it); + } + return l; + } +} + +//////////////////////////////////////////////////////////////////////////////// +// public methods +//////////////////////////////////////////////////////////////////////////////// + +TableOfContentsFrame::TableOfContentsFrame(const ID3v2::Header *tagHeader, const ByteVector &data) : + ID3v2::Frame(data) +{ + d = new TableOfContentsFramePrivate; + d->tagHeader = tagHeader; + setData(data); +} + +TableOfContentsFrame::TableOfContentsFrame(const ByteVector &elementID, + const ByteVectorList &children, + const FrameList &embeddedFrames) : + ID3v2::Frame("CTOC") +{ + d = new TableOfContentsFramePrivate; + d->elementID = elementID; + strip(d->elementID); + d->childElements = children; + + for(FrameList::ConstIterator it = embeddedFrames.begin(); it != embeddedFrames.end(); ++it) + addEmbeddedFrame(*it); +} + +TableOfContentsFrame::~TableOfContentsFrame() +{ + delete d; +} + +ByteVector TableOfContentsFrame::elementID() const +{ + return d->elementID; +} + +bool TableOfContentsFrame::isTopLevel() const +{ + return d->isTopLevel; +} + +bool TableOfContentsFrame::isOrdered() const +{ + return d->isOrdered; +} + +TagLib::uint TableOfContentsFrame::entryCount() const +{ + return d->childElements.size(); +} + +ByteVectorList TableOfContentsFrame::childElements() const +{ + return d->childElements; +} + +void TableOfContentsFrame::setElementID(const ByteVector &eID) +{ + d->elementID = eID; + strip(d->elementID); +} + +void TableOfContentsFrame::setIsTopLevel(const bool &t) +{ + d->isTopLevel = t; +} + +void TableOfContentsFrame::setIsOrdered(const bool &o) +{ + d->isOrdered = o; +} + +void TableOfContentsFrame::setChildElements(const ByteVectorList &l) +{ + d->childElements = l; + strip(d->childElements); +} + +void TableOfContentsFrame::addChildElement(const ByteVector &cE) +{ + d->childElements.append(cE); + strip(d->childElements); +} + +void TableOfContentsFrame::removeChildElement(const ByteVector &cE) +{ + ByteVectorList::Iterator it = d->childElements.find(cE); + + if(it == d->childElements.end()) + it = d->childElements.find(cE + ByteVector("\0")); + + d->childElements.erase(it); +} + +const FrameListMap &TableOfContentsFrame::embeddedFrameListMap() const +{ + return d->embeddedFrameListMap; +} + +const FrameList &TableOfContentsFrame::embeddedFrameList() const +{ + return d->embeddedFrameList; +} + +const FrameList &TableOfContentsFrame::embeddedFrameList(const ByteVector &frameID) const +{ + return d->embeddedFrameListMap[frameID]; +} + +void TableOfContentsFrame::addEmbeddedFrame(Frame *frame) +{ + d->embeddedFrameList.append(frame); + d->embeddedFrameListMap[frame->frameID()].append(frame); +} + +void TableOfContentsFrame::removeEmbeddedFrame(Frame *frame, bool del) +{ + // remove the frame from the frame list + FrameList::Iterator it = d->embeddedFrameList.find(frame); + d->embeddedFrameList.erase(it); + + // ...and from the frame list map + it = d->embeddedFrameListMap[frame->frameID()].find(frame); + d->embeddedFrameListMap[frame->frameID()].erase(it); + + // ...and delete as desired + if(del) + delete frame; +} + +void TableOfContentsFrame::removeEmbeddedFrames(const ByteVector &id) +{ + FrameList l = d->embeddedFrameListMap[id]; + for(FrameList::ConstIterator it = l.begin(); it != l.end(); ++it) + removeEmbeddedFrame(*it, true); +} + +String TableOfContentsFrame::toString() const +{ + return String::null; +} + +PropertyMap TableOfContentsFrame::asProperties() const +{ + PropertyMap map; + + map.unsupportedData().append(frameID() + String("/") + d->elementID); + + return map; +} + +TableOfContentsFrame *TableOfContentsFrame::findByElementID(const ID3v2::Tag *tag, + const ByteVector &eID) // static +{ + ID3v2::FrameList tablesOfContents = tag->frameList("CTOC"); + + for(ID3v2::FrameList::ConstIterator it = tablesOfContents.begin(); + it != tablesOfContents.end(); + ++it) + { + TableOfContentsFrame *frame = dynamic_cast(*it); + if(frame && frame->elementID() == eID) + return frame; + } + + return 0; +} + +TableOfContentsFrame *TableOfContentsFrame::findTopLevel(const ID3v2::Tag *tag) // static +{ + ID3v2::FrameList tablesOfContents = tag->frameList("CTOC"); + + for(ID3v2::FrameList::ConstIterator it = tablesOfContents.begin(); + it != tablesOfContents.end(); + ++it) + { + TableOfContentsFrame *frame = dynamic_cast(*it); + if(frame && frame->isTopLevel() == true) + return frame; + } + + return 0; +} + +void TableOfContentsFrame::parseFields(const ByteVector &data) +{ + TagLib::uint size = data.size(); + if(size < 6) { + debug("A CTOC frame must contain at least 6 bytes (1 byte element ID terminated by " + "null, 1 byte flags, 1 byte entry count and 1 byte child element ID terminated " + "by null."); + return; + } + + int pos = 0; + TagLib::uint embPos = 0; + d->elementID = readStringField(data, String::Latin1, &pos).data(String::Latin1); + d->isTopLevel = (data.at(pos) & 2) > 0; + d->isOrdered = (data.at(pos++) & 1) > 0; + TagLib::uint entryCount = data.at(pos++); + for(TagLib::uint i = 0; i < entryCount; i++) { + ByteVector childElementID = readStringField(data, String::Latin1, &pos).data(String::Latin1); + d->childElements.append(childElementID); + } + + size -= pos; + + if(size < header()->size()) + return; + + while(embPos < size - header()->size()) { + Frame *frame = FrameFactory::instance()->createFrame(data.mid(pos + embPos), (d->tagHeader != 0)); + + if(!frame) + return; + + // Checks to make sure that frame parsed correctly. + if(frame->size() <= 0) { + delete frame; + return; + } + + embPos += frame->size() + header()->size(); + addEmbeddedFrame(frame); + } +} + +ByteVector TableOfContentsFrame::renderFields() const +{ + ByteVector data; + + data.append(d->elementID); + data.append('\0'); + char flags = 0; + if(d->isTopLevel) + flags += 2; + if(d->isOrdered) + flags += 1; + data.append(flags); + data.append((char)(entryCount())); + ByteVectorList::ConstIterator it = d->childElements.begin(); + while(it != d->childElements.end()) { + data.append(*it); + data.append('\0'); + it++; + } + FrameList l = d->embeddedFrameList; + for(FrameList::ConstIterator it = l.begin(); it != l.end(); ++it) + data.append((*it)->render()); + + return data; +} + +TableOfContentsFrame::TableOfContentsFrame(const ID3v2::Header *tagHeader, + const ByteVector &data, Header *h) : + Frame(h) +{ + d = new TableOfContentsFramePrivate; + d->tagHeader = tagHeader; + parseFields(fieldData(data)); +} diff --git a/3rdparty/taglib/mpeg/id3v2/frames/tableofcontentsframe.h b/3rdparty/taglib/mpeg/id3v2/frames/tableofcontentsframe.h new file mode 100644 index 000000000..66facf80a --- /dev/null +++ b/3rdparty/taglib/mpeg/id3v2/frames/tableofcontentsframe.h @@ -0,0 +1,258 @@ +/*************************************************************************** + copyright : (C) 2013 by Lukas Krejci + email : krejclu6@fel.cvut.cz + ***************************************************************************/ + +/*************************************************************************** + * 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_TABLEOFCONTENTSFRAME +#define TAGLIB_TABLEOFCONTENTSFRAME + +#include "id3v2tag.h" +#include "id3v2frame.h" + +namespace TagLib { + + namespace ID3v2 { + + /*! + * This is an implementation of ID3v2 table of contents frames. Purpose + * of this frame is to allow a table of contents to be defined. + */ + + //! An implementation of ID3v2 table of contents frames + + class TAGLIB_EXPORT TableOfContentsFrame : public ID3v2::Frame + { + friend class FrameFactory; + + public: + /*! + * Creates a table of contents frame based on \a data. \a tagHeader is + * required as the internal frames are parsed based on the tag version. + */ + TableOfContentsFrame(const ID3v2::Header *tagHeader, const ByteVector &data); + + /*! + * Creates a table of contents frame with the element ID \a elementID, + * the child elements \a children and embedded frames, which become owned + * by this frame, in \a embeddedFrames. + */ + TableOfContentsFrame(const ByteVector &elementID, + const ByteVectorList &children = ByteVectorList(), + const FrameList &embeddedFrames = FrameList()); + + /*! + * Destroys the frame. + */ + ~TableOfContentsFrame(); + + /*! + * Returns the elementID of the frame. Element ID + * is a null terminated string, however it's not human-readable. + * + * \see setElementID() + */ + ByteVector elementID() const; + + /*! + * Returns true, if the frame is top-level (doesn't have + * any parent CTOC frame). + * + * \see setIsTopLevel() + */ + bool isTopLevel() const; + + /*! + * Returns true, if the child elements list entries + * are ordered. + * + * \see setIsOrdered() + */ + bool isOrdered() const; + + /*! + * Returns count of child elements of the frame. It always + * corresponds to size of child elements list. + * + * \see childElements() + */ + uint entryCount() const; + + /*! + * Returns list of child elements of the frame. + * + * \see setChildElements() + */ + ByteVectorList childElements() const; + + /*! + * Sets the elementID of the frame to \a eID. If \a eID isn't + * null terminated, a null char is appended automatically. + * + * \see elementID() + */ + void setElementID(const ByteVector &eID); + + /*! + * Sets, if the frame is top-level (doesn't have + * any parent CTOC frame). + * + * \see isTopLevel() + */ + void setIsTopLevel(const bool &t); + + /*! + * Sets, if the child elements list entries + * are ordered. + * + * \see isOrdered() + */ + void setIsOrdered(const bool &o); + + /*! + * Sets list of child elements of the frame to \a l. + * + * \see childElements() + */ + void setChildElements(const ByteVectorList &l); + + /*! + * Adds \a cE to list of child elements of the frame. + * + * \see childElements() + */ + void addChildElement(const ByteVector &cE); + + /*! + * Removes \a cE to list of child elements of the frame. + * + * \see childElements() + */ + void removeChildElement(const ByteVector &cE); + + /*! + * Returns a reference to the frame list map. This is an FrameListMap of + * all of the frames embedded in the CTOC frame. + * + * This is the most convenient structure for accessing the CTOC frame's + * embedded frames. Many frame types allow multiple instances of the same + * frame type so this is a map of lists. In most cases however there will + * only be a single frame of a certain type. + * + * \warning You should not modify this data structure directly, instead + * use addEmbeddedFrame() and removeEmbeddedFrame(). + * + * \see embeddedFrameList() + */ + const FrameListMap &embeddedFrameListMap() const; + + /*! + * Returns a reference to the embedded frame list. This is an FrameList + * of all of the frames embedded in the CTOC frame in the order that they + * were parsed. + * + * This can be useful if for example you want iterate over the CTOC frame's + * embedded frames in the order that they occur in the CTOC frame. + * + * \warning You should not modify this data structure directly, instead + * use addEmbeddedFrame() and removeEmbeddedFrame(). + */ + const FrameList &embeddedFrameList() const; + + /*! + * Returns the embedded frame list for frames with the id \a frameID + * or an empty list if there are no embedded frames of that type. This + * is just a convenience and is equivalent to: + * + * \code + * embeddedFrameListMap()[frameID]; + * \endcode + * + * \see embeddedFrameListMap() + */ + const FrameList &embeddedFrameList(const ByteVector &frameID) const; + + /*! + * Add an embedded frame to the CTOC frame. At this point the CTOC frame + * takes ownership of the embedded frame and will handle freeing its memory. + * + * \note Using this method will invalidate any pointers on the list + * returned by embeddedFrameList() + */ + void addEmbeddedFrame(Frame *frame); + + /*! + * Remove an embedded frame from the CTOC frame. If \a del is true the frame's + * memory will be freed; if it is false, it must be deleted by the user. + * + * \note Using this method will invalidate any pointers on the list + * returned by embeddedFrameList() + */ + void removeEmbeddedFrame(Frame *frame, bool del = true); + + /*! + * Remove all embedded frames of type \a id from the CTOC frame and free their + * memory. + * + * \note Using this method will invalidate any pointers on the list + * returned by embeddedFrameList() + */ + void removeEmbeddedFrames(const ByteVector &id); + + virtual String toString() const; + + PropertyMap asProperties() const; + + /*! + * CTOC frames each have a unique element ID. This searches for a CTOC + * frame with the element ID \a eID and returns a pointer to it. This + * can be used to link together parent and child CTOC frames. + * + * \see elementID() + */ + static TableOfContentsFrame *findByElementID(const Tag *tag, const ByteVector &eID); + + /*! + * CTOC frames each contain a flag that indicates, if CTOC frame is top-level (there isn't + * any frame, which contains this frame in its child elements list). Only a single frame + * within tag can be top-level. This searches for a top-level CTOC frame. + * + * \see isTopLevel() + */ + static TableOfContentsFrame *findTopLevel(const Tag *tag); + + protected: + virtual void parseFields(const ByteVector &data); + virtual ByteVector renderFields() const; + + private: + TableOfContentsFrame(const ID3v2::Header *tagHeader, const ByteVector &data, Header *h); + TableOfContentsFrame(const TableOfContentsFrame &); + TableOfContentsFrame &operator=(const TableOfContentsFrame &); + + class TableOfContentsFramePrivate; + TableOfContentsFramePrivate *d; + }; + } +} + +#endif diff --git a/3rdparty/taglib/mpeg/id3v2/frames/textidentificationframe.cpp b/3rdparty/taglib/mpeg/id3v2/frames/textidentificationframe.cpp index 70ea50f8f..b77dd5475 100644 --- a/3rdparty/taglib/mpeg/id3v2/frames/textidentificationframe.cpp +++ b/3rdparty/taglib/mpeg/id3v2/frames/textidentificationframe.cpp @@ -211,12 +211,12 @@ void TextIdentificationFrame::parseFields(const ByteVector &data) // append those split values to the list and make sure that the new string's // type is the same specified for this frame - for(ByteVectorList::Iterator it = l.begin(); it != l.end(); it++) { + for(ByteVectorList::ConstIterator it = l.begin(); it != l.end(); it++) { if(!(*it).isEmpty()) { if(d->textEncoding == String::Latin1) d->fieldList.append(Tag::latin1StringHandler()->parse(*it)); else - d->fieldList.append(String(*it, d->textEncoding)); + d->fieldList.append(String(*it, d->textEncoding)); } } } @@ -394,7 +394,7 @@ UserTextIdentificationFrame *UserTextIdentificationFrame::find( ID3v2::Tag *tag, const String &description) // static { FrameList l = tag->frameList("TXXX"); - for(FrameList::Iterator it = l.begin(); it != l.end(); ++it) { + for(FrameList::ConstIterator it = l.begin(); it != l.end(); ++it) { UserTextIdentificationFrame *f = dynamic_cast(*it); if(f && f->description() == description) return f; diff --git a/3rdparty/taglib/mpeg/id3v2/frames/textidentificationframe.h b/3rdparty/taglib/mpeg/id3v2/frames/textidentificationframe.h index 58ed63e69..e49aa897b 100644 --- a/3rdparty/taglib/mpeg/id3v2/frames/textidentificationframe.h +++ b/3rdparty/taglib/mpeg/id3v2/frames/textidentificationframe.h @@ -234,7 +234,7 @@ namespace TagLib { * This description identifies the frame and must be unique. */ - //! An ID3v2 custom text identification frame implementationx + //! An ID3v2 custom text identification frame implementation class TAGLIB_EXPORT UserTextIdentificationFrame : public TextIdentificationFrame { diff --git a/3rdparty/taglib/mpeg/id3v2/frames/uniquefileidentifierframe.h b/3rdparty/taglib/mpeg/id3v2/frames/uniquefileidentifierframe.h index add5a2e0c..decf1b0d9 100644 --- a/3rdparty/taglib/mpeg/id3v2/frames/uniquefileidentifierframe.h +++ b/3rdparty/taglib/mpeg/id3v2/frames/uniquefileidentifierframe.h @@ -46,7 +46,7 @@ namespace TagLib { public: /*! - * Creates a uniqe file identifier frame based on \a data. + * Creates a unique file identifier frame based on \a data. */ UniqueFileIdentifierFrame(const ByteVector &data); diff --git a/3rdparty/taglib/mpeg/id3v2/frames/unsynchronizedlyricsframe.h b/3rdparty/taglib/mpeg/id3v2/frames/unsynchronizedlyricsframe.h index 3af354fcd..dad67c7a3 100644 --- a/3rdparty/taglib/mpeg/id3v2/frames/unsynchronizedlyricsframe.h +++ b/3rdparty/taglib/mpeg/id3v2/frames/unsynchronizedlyricsframe.h @@ -104,7 +104,7 @@ namespace TagLib { /*! * Sets the description of the unsynchronized lyrics frame to \a s. * - * \see decription() + * \see description() */ void setDescription(const String &s); @@ -149,7 +149,7 @@ namespace TagLib { /*! * LyricsFrames each have a unique description. This searches for a lyrics - * frame with the decription \a d and returns a pointer to it. If no + * frame with the description \a d and returns a pointer to it. If no * frame is found that matches the given description null is returned. * * \see description() diff --git a/3rdparty/taglib/mpeg/id3v2/frames/urllinkframe.cpp b/3rdparty/taglib/mpeg/id3v2/frames/urllinkframe.cpp index 6bcbbda45..1225b5247 100644 --- a/3rdparty/taglib/mpeg/id3v2/frames/urllinkframe.cpp +++ b/3rdparty/taglib/mpeg/id3v2/frames/urllinkframe.cpp @@ -169,7 +169,7 @@ PropertyMap UserUrlLinkFrame::asProperties() const UserUrlLinkFrame *UserUrlLinkFrame::find(ID3v2::Tag *tag, const String &description) // static { FrameList l = tag->frameList("WXXX"); - for(FrameList::Iterator it = l.begin(); it != l.end(); ++it) { + for(FrameList::ConstIterator it = l.begin(); it != l.end(); ++it) { UserUrlLinkFrame *f = dynamic_cast(*it); if(f && f->description() == description) return f; diff --git a/3rdparty/taglib/mpeg/id3v2/id3v2extendedheader.h b/3rdparty/taglib/mpeg/id3v2/id3v2extendedheader.h index d7227e9d1..d60200e73 100644 --- a/3rdparty/taglib/mpeg/id3v2/id3v2extendedheader.h +++ b/3rdparty/taglib/mpeg/id3v2/id3v2extendedheader.h @@ -38,7 +38,7 @@ namespace TagLib { /*! * This class implements ID3v2 extended headers. It attempts to follow, - * both semantically and programatically, the structure specified in + * both semantically and programatically, the structure specified in * the ID3v2 standard. The API is based on the properties of ID3v2 extended * headers specified there. If any of the terms used in this documentation * are unclear please check the specification in the linked section. diff --git a/3rdparty/taglib/mpeg/id3v2/id3v2frame.cpp b/3rdparty/taglib/mpeg/id3v2/id3v2frame.cpp index 3cafcff9e..bee5375ad 100644 --- a/3rdparty/taglib/mpeg/id3v2/id3v2frame.cpp +++ b/3rdparty/taglib/mpeg/id3v2/id3v2frame.cpp @@ -254,12 +254,49 @@ ByteVector Frame::fieldData(const ByteVector &frameData) const if(d->header->compression() && !d->header->encryption()) { - ByteVector data(frameDataLength); - uLongf uLongTmp = frameDataLength; - ::uncompress((Bytef *) data.data(), - (uLongf *) &uLongTmp, - (Bytef *) frameData.data() + frameDataOffset, - size()); + if(frameData.size() <= frameDataOffset) { + debug("Compressed frame doesn't have enough data to decode"); + return ByteVector(); + } + + z_stream stream = {}; + + if(inflateInit(&stream) != Z_OK) + return ByteVector(); + + stream.avail_in = (uLongf) frameData.size() - frameDataOffset; + stream.next_in = (Bytef *) frameData.data() + frameDataOffset; + + static const uint chunkSize = 1024; + + ByteVector data; + ByteVector chunk(chunkSize); + + do { + stream.avail_out = (uLongf) chunk.size(); + stream.next_out = (Bytef *) chunk.data(); + + int result = inflate(&stream, Z_NO_FLUSH); + + if(result == Z_STREAM_ERROR || + result == Z_NEED_DICT || + result == Z_DATA_ERROR || + result == Z_MEM_ERROR) + { + if(result != Z_STREAM_ERROR) + inflateEnd(&stream); + debug("Error reading compressed stream"); + return ByteVector(); + } + + data.append(stream.avail_out == 0 ? chunk : chunk.mid(0, chunk.size() - stream.avail_out)); + } while(stream.avail_out == 0); + + inflateEnd(&stream); + + if(frameDataLength != data.size()) + debug("frameDataLength does not match the data length returned by zlib"); + return data; } else diff --git a/3rdparty/taglib/mpeg/id3v2/id3v2frame.h b/3rdparty/taglib/mpeg/id3v2/id3v2frame.h index 3e257d4f5..3771f4f03 100644 --- a/3rdparty/taglib/mpeg/id3v2/id3v2frame.h +++ b/3rdparty/taglib/mpeg/id3v2/id3v2frame.h @@ -165,7 +165,7 @@ namespace TagLib { * All other processing of \a data should be handled in a subclass. * * \note This need not contain anything more than a frame ID, but - * \e must constain at least that. + * \e must contain at least that. */ explicit Frame(const ByteVector &data); @@ -218,9 +218,9 @@ namespace TagLib { ByteVector fieldData(const ByteVector &frameData) const; /*! - * Reads a String of type \a encodiong from the ByteVector \a data. If \a + * Reads a String of type \a encoding from the ByteVector \a data. If \a * position is passed in it is used both as the starting point and is - * updated to replect the position just after the string that has been read. + * updated to return the position just after the string that has been read. * This is useful for reading strings sequentially. */ String readStringField(const ByteVector &data, String::Type encoding, @@ -460,7 +460,7 @@ namespace TagLib { bool readOnly() const; /*! - * Returns true if the flag for the grouping identifity is set. + * Returns true if the flag for the grouping identity is set. * * \note This flag is currently ignored internally in TagLib. */ diff --git a/3rdparty/taglib/mpeg/id3v2/id3v2framefactory.cpp b/3rdparty/taglib/mpeg/id3v2/id3v2framefactory.cpp index 3371ca7dd..f6a4aac99 100644 --- a/3rdparty/taglib/mpeg/id3v2/id3v2framefactory.cpp +++ b/3rdparty/taglib/mpeg/id3v2/id3v2framefactory.cpp @@ -45,6 +45,10 @@ #include "frames/popularimeterframe.h" #include "frames/privateframe.h" #include "frames/ownershipframe.h" +#include "frames/synchronizedlyricsframe.h" +#include "frames/eventtimingcodesframe.h" +#include "frames/chapterframe.h" +#include "frames/tableofcontentsframe.h" using namespace TagLib; using namespace ID3v2; @@ -241,6 +245,20 @@ Frame *FrameFactory::createFrame(const ByteVector &origData, Header *tagHeader) 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; + } + + // Event timing codes (frames 4.5) + + if(frameID == "ETCO") + return new EventTimingCodesFrame(data, header); + // Popularimeter (frames 4.17) if(frameID == "POPM") @@ -250,18 +268,50 @@ Frame *FrameFactory::createFrame(const ByteVector &origData, Header *tagHeader) if(frameID == "PRIV") return new PrivateFrame(data, header); - + // Ownership (frames 4.22) - + if(frameID == "OWNE") { OwnershipFrame *f = new OwnershipFrame(data, header); d->setTextEncoding(f); return f; } + // Chapter (ID3v2 chapters 1.0) + + if(frameID == "CHAP") + return new ChapterFrame(tagHeader, data, header); + + // Table of contents (ID3v2 chapters 1.0) + + if(frameID == "CTOC") + return new TableOfContentsFrame(tagHeader, data, header); + return new UnknownFrame(data, header); } +void FrameFactory::rebuildAggregateFrames(ID3v2::Tag *tag) const +{ + if(tag->header()->majorVersion() < 4 && + tag->frameList("TDRC").size() == 1 && + tag->frameList("TDAT").size() == 1) + { + TextIdentificationFrame *trdc = + static_cast(tag->frameList("TDRC").front()); + UnknownFrame *tdat = + static_cast(tag->frameList("TDAT").front()); + + if(trdc->fieldList().size() == 1 && + trdc->fieldList().front().size() == 4 && + tdat->data().size() >= 5) + { + String date(tdat->data().mid(1), String::Type(tdat->data()[0])); + if(date.length() == 4) + trdc->setText(trdc->toString() + '-' + date.substr(2, 2) + '-' + date.substr(0, 2)); + } + } +} + String::Type FrameFactory::defaultTextEncoding() const { return d->defaultEncoding; @@ -430,7 +480,7 @@ void FrameFactory::updateGenre(TextIdentificationFrame *frame) const StringList fields = frame->fieldList(); StringList newfields; - for(StringList::Iterator it = fields.begin(); it != fields.end(); ++it) { + for(StringList::ConstIterator it = fields.begin(); it != fields.end(); ++it) { String s = *it; int end = s.find(")"); diff --git a/3rdparty/taglib/mpeg/id3v2/id3v2framefactory.h b/3rdparty/taglib/mpeg/id3v2/id3v2framefactory.h index 762d7eb3e..3de59ac7b 100644 --- a/3rdparty/taglib/mpeg/id3v2/id3v2framefactory.h +++ b/3rdparty/taglib/mpeg/id3v2/id3v2framefactory.h @@ -93,11 +93,19 @@ namespace TagLib { // BIC: make virtual Frame *createFrame(const ByteVector &data, Header *tagHeader) const; + /*! + * After a tag has been read, this tries to rebuild some of them + * information, most notably the recording date, from frames that + * have been deprecated and can't be upgraded directly. + */ + // BIC: Make virtual + void rebuildAggregateFrames(ID3v2::Tag *tag) const; + /*! * Returns the default text encoding for text frames. If setTextEncoding() * has not been explicitly called this will only be used for new text * frames. However, if this value has been set explicitly all frames will be - * converted to this type (unless it's explitly set differently for the + * converted to this type (unless it's explicitly set differently for the * individual frame) when being rendered. * * \see setDefaultTextEncoding() diff --git a/3rdparty/taglib/mpeg/id3v2/id3v2header.cpp b/3rdparty/taglib/mpeg/id3v2/id3v2header.cpp index ad6b3aa78..10381053d 100644 --- a/3rdparty/taglib/mpeg/id3v2/id3v2header.cpp +++ b/3rdparty/taglib/mpeg/id3v2/id3v2header.cpp @@ -215,7 +215,7 @@ void Header::parse(const ByteVector &data) return; } - for(ByteVector::Iterator it = sizeData.begin(); it != sizeData.end(); it++) { + for(ByteVector::ConstIterator it = sizeData.begin(); it != sizeData.end(); it++) { if(uchar(*it) >= 128) { d->tagSize = 0; debug("TagLib::ID3v2::Header::parse() - One of the size bytes in the id3v2 header was greater than the allowed 128."); diff --git a/3rdparty/taglib/mpeg/id3v2/id3v2header.h b/3rdparty/taglib/mpeg/id3v2/id3v2header.h index 307ba96c8..52294ddda 100644 --- a/3rdparty/taglib/mpeg/id3v2/id3v2header.h +++ b/3rdparty/taglib/mpeg/id3v2/id3v2header.h @@ -37,7 +37,7 @@ namespace TagLib { /*! * This class implements ID3v2 headers. It attempts to follow, both - * semantically and programatically, the structure specified in + * semantically and programmatically, the structure specified in * the ID3v2 standard. The API is based on the properties of ID3v2 headers * specified there. If any of the terms used in this documentation are * unclear please check the specification in the linked section. diff --git a/3rdparty/taglib/mpeg/id3v2/id3v2tag.cpp b/3rdparty/taglib/mpeg/id3v2/id3v2tag.cpp index 57637a36e..e31d1247a 100644 --- a/3rdparty/taglib/mpeg/id3v2/id3v2tag.cpp +++ b/3rdparty/taglib/mpeg/id3v2/id3v2tag.cpp @@ -24,10 +24,10 @@ ***************************************************************************/ #ifdef HAVE_CONFIG_H -#include +#include "config.h" #endif -#include +#include "tfile.h" #include "id3v2tag.h" #include "id3v2header.h" @@ -37,7 +37,7 @@ #include "tbytevector.h" #include "id3v1genres.h" #include "tpropertymap.h" -#include +#include "tdebug.h" #include "frames/textidentificationframe.h" #include "frames/commentsframe.h" @@ -81,6 +81,11 @@ public: static const Latin1StringHandler defaultStringHandler; const ID3v2::Latin1StringHandler *ID3v2::Tag::TagPrivate::stringHandler = &defaultStringHandler; +namespace +{ + const TagLib::uint DefaultPaddingSize = 1024; +} + //////////////////////////////////////////////////////////////////////////////// // StringHandler implementation //////////////////////////////////////////////////////////////////////////////// @@ -356,7 +361,7 @@ void ID3v2::Tag::removeFrame(Frame *frame, bool del) void ID3v2::Tag::removeFrames(const ByteVector &id) { FrameList l = d->frameListMap[id]; - for(FrameList::Iterator it = l.begin(); it != l.end(); ++it) + for(FrameList::ConstIterator it = l.begin(); it != l.end(); ++it) removeFrame(*it, true); } @@ -469,7 +474,7 @@ void ID3v2::Tag::downgradeFrames(FrameList *frames, FrameList *newFrames) const ID3v2::TextIdentificationFrame *frameTDRC = 0; ID3v2::TextIdentificationFrame *frameTIPL = 0; ID3v2::TextIdentificationFrame *frameTMCL = 0; - for(FrameList::Iterator it = d->frameList.begin(); it != d->frameList.end(); it++) { + for(FrameList::ConstIterator it = d->frameList.begin(); it != d->frameList.end(); it++) { ID3v2::Frame *frame = *it; ByteVector frameID = frame->header()->frameID(); for(int i = 0; unsupportedFrames[i]; i++) { @@ -583,28 +588,41 @@ ByteVector ID3v2::Tag::render(int version) const downgradeFrames(&frameList, &newFrames); } - for(FrameList::Iterator it = frameList.begin(); it != frameList.end(); it++) { + for(FrameList::ConstIterator it = frameList.begin(); it != frameList.end(); it++) { (*it)->header()->setVersion(version); if((*it)->header()->frameID().size() != 4) { - debug("A frame of unsupported or unknown type \'" + debug("An ID3v2 frame of unsupported or unknown type \'" + String((*it)->header()->frameID()) + "\' has been discarded"); continue; } - if(!(*it)->header()->tagAlterPreservation()) - tagData.append((*it)->render()); + if(!(*it)->header()->tagAlterPreservation()) { + const ByteVector frameData = (*it)->render(); + if(frameData.size() == Frame::headerSize((*it)->header()->version())) { + debug("An empty ID3v2 frame \'" + + String((*it)->header()->frameID()) + "\' has been discarded"); + continue; + } + tagData.append(frameData); + } } // Compute the amount of padding, and append that to tagData. - uint paddingSize = 0; - uint originalSize = d->header.tagSize(); + uint paddingSize = DefaultPaddingSize; - if(tagData.size() < originalSize) - paddingSize = originalSize - tagData.size(); - else - paddingSize = 1024; + if(d->file && tagData.size() < d->header.tagSize()) { + paddingSize = d->header.tagSize() - tagData.size(); - tagData.append(ByteVector(paddingSize, char(0))); + // Padding won't increase beyond 1% of the file size. + + if(paddingSize > DefaultPaddingSize) { + const uint threshold = d->file->length() / 100; // should be ulonglong in taglib2. + if(paddingSize > threshold) + paddingSize = DefaultPaddingSize; + } + } + + tagData.append(ByteVector(paddingSize, '\0')); // Set the version and data size. d->header.setMajorVersion(version); @@ -693,7 +711,7 @@ void ID3v2::Tag::parse(const ByteVector &origData) } d->paddingSize = frameDataLength - frameDataPosition; - return; + break; } Frame *frame = d->factory->createFrame(data.mid(frameDataPosition), @@ -712,6 +730,8 @@ void ID3v2::Tag::parse(const ByteVector &origData) frameDataPosition += frame->size() + Frame::headerSize(d->header.majorVersion()); addFrame(frame); } + + d->factory->rebuildAggregateFrames(this); } void ID3v2::Tag::setTextFrame(const ByteVector &id, const String &value) diff --git a/3rdparty/taglib/mpeg/id3v2/id3v2tag.h b/3rdparty/taglib/mpeg/id3v2/id3v2tag.h index 5fd5c1f12..76ca73f12 100644 --- a/3rdparty/taglib/mpeg/id3v2/id3v2tag.h +++ b/3rdparty/taglib/mpeg/id3v2/id3v2tag.h @@ -60,13 +60,13 @@ namespace TagLib { //! An abstraction for the ISO-8859-1 string to data encoding in ID3v2 tags. /*! - * ID3v2 tag can store strings in ISO-8859-1 (Latin1), and TagLib only - * supports genuine ISO-8859-1 by default. However, in practice, non - * ISO-8859-1 encodings are often used instead of ISO-8859-1, such as + * ID3v2 tag can store strings in ISO-8859-1 (Latin1), and TagLib only + * supports genuine ISO-8859-1 by default. However, in practice, non + * ISO-8859-1 encodings are often used instead of ISO-8859-1, such as * Windows-1252 for western languages, Shift_JIS for Japanese and so on. * * Here is an option to read such tags by subclassing this class, - * reimplementing parse() and setting your reimplementation as the default + * reimplementing parse() and setting your reimplementation as the default * with ID3v2::Tag::setStringHandler(). * * \note Writing non-ISO-8859-1 tags is not implemented intentionally. @@ -98,7 +98,7 @@ namespace TagLib { * split into data components. * * ID3v2 tags have several parts, TagLib attempts to provide an interface - * for them all. header(), footer() and extendedHeader() corespond to those + * for them all. header(), footer() and extendedHeader() correspond to those * data structures in the ID3v2 standard and the APIs for the classes that * they return attempt to reflect this. * @@ -115,7 +115,7 @@ namespace TagLib { * class. * * read() and parse() pass binary data to the other ID3v2 class structures, - * they do not handle parsing of flags or fields, for instace. Those are + * they do not handle parsing of flags or fields, for instance. Those are * handled by similar functions within those classes. * * \note All pointers to data structures within the tag will become invalid @@ -126,7 +126,7 @@ namespace TagLib { * rather long, but if you're planning on messing with this class and others * that deal with the details of ID3v2 (rather than the nice, safe, abstract * TagLib::Tag and friends), it's worth your time to familiarize yourself - * with said spec (which is distrubuted with the TagLib sources). TagLib + * with said spec (which is distributed with the TagLib sources). TagLib * tries to do most of the work, but with a little luck, you can still * convince it to generate invalid ID3v2 tags. The APIs for ID3v2 assume a * working knowledge of ID3v2 structure. You're been warned. @@ -150,7 +150,7 @@ namespace TagLib { * \note You should be able to ignore the \a factory parameter in almost * all situations. You would want to specify your own FrameFactory * subclass in the case that you are extending TagLib to support additional - * frame types, which would be incorperated into your factory. + * frame types, which would be incorporated into your factory. * * \see FrameFactory */ @@ -353,9 +353,9 @@ namespace TagLib { */ // BIC: combine with the above method ByteVector render(int version) const; - + /*! - * Gets the current string handler that decides how the "Latin-1" data + * Gets the current string handler that decides how the "Latin-1" data * will be converted to and from binary data. * * \see Latin1StringHandler @@ -369,7 +369,7 @@ namespace TagLib { * released and default ISO-8859-1 handler is restored. * * \note The caller is responsible for deleting the previous handler - * as needed after it is released. + * as needed after it is released. * * \see Latin1StringHandler */ diff --git a/3rdparty/taglib/mpeg/mpegfile.cpp b/3rdparty/taglib/mpeg/mpegfile.cpp index 0ac87d10f..43075cfc0 100644 --- a/3rdparty/taglib/mpeg/mpegfile.cpp +++ b/3rdparty/taglib/mpeg/mpegfile.cpp @@ -31,14 +31,31 @@ #include #include -#include - #include "mpegfile.h" #include "mpegheader.h" #include "tpropertymap.h" using namespace TagLib; +namespace +{ + /*! + * MPEG frames can be recognized by the bit pattern 11111111 111, so the + * first byte is easy to check for, however checking to see if the second byte + * starts with \e 111 is a bit more tricky, hence these functions. + */ + + inline bool firstSyncByte(uchar byte) + { + return (byte == 0xFF); + } + + inline bool secondSynchByte(uchar byte) + { + return ((byte & 0xE0) == 0xE0); + } +} + namespace { enum { ID3v2Index = 0, APEIndex = 1, ID3v1Index = 2 }; @@ -60,7 +77,6 @@ public: hasAPE(false), properties(0) { - } ~FilePrivate() @@ -95,33 +111,30 @@ public: // public members //////////////////////////////////////////////////////////////////////////////// -MPEG::File::File(FileName file, bool readProperties, - Properties::ReadStyle propertiesStyle) : TagLib::File(file) +MPEG::File::File(FileName file, bool readProperties, Properties::ReadStyle) : + TagLib::File(file), + d(new FilePrivate()) { - d = new FilePrivate; - if(isOpen()) - read(readProperties, propertiesStyle); + read(readProperties); } MPEG::File::File(FileName file, ID3v2::FrameFactory *frameFactory, - bool readProperties, Properties::ReadStyle propertiesStyle) : - TagLib::File(file) + bool readProperties, Properties::ReadStyle) : + TagLib::File(file), + d(new FilePrivate(frameFactory)) { - d = new FilePrivate(frameFactory); - if(isOpen()) - read(readProperties, propertiesStyle); + read(readProperties); } MPEG::File::File(IOStream *stream, ID3v2::FrameFactory *frameFactory, - bool readProperties, Properties::ReadStyle propertiesStyle) : - TagLib::File(stream) + bool readProperties, Properties::ReadStyle) : + TagLib::File(stream), + d(new FilePrivate(frameFactory)) { - d = new FilePrivate(frameFactory); - if(isOpen()) - read(readProperties, propertiesStyle); + read(readProperties); } MPEG::File::~File() @@ -392,11 +405,11 @@ long MPEG::File::nextFrameOffset(long position) return position - 1; for(uint i = 0; i < buffer.size() - 1; i++) { - if(uchar(buffer[i]) == 0xff && secondSynchByte(buffer[i + 1])) + if(firstSyncByte(buffer[i]) && secondSynchByte(buffer[i + 1])) return position + i; } - foundLastSyncPattern = uchar(buffer[buffer.size() - 1]) == 0xff; + foundLastSyncPattern = firstSyncByte(buffer[buffer.size() - 1]); position += buffer.size(); } } @@ -416,11 +429,11 @@ long MPEG::File::previousFrameOffset(long position) if(buffer.size() <= 0) break; - if(foundFirstSyncPattern && uchar(buffer[buffer.size() - 1]) == 0xff) + if(foundFirstSyncPattern && firstSyncByte(buffer[buffer.size() - 1])) return position + buffer.size() - 1; for(int i = buffer.size() - 2; i >= 0; i--) { - if(uchar(buffer[i]) == 0xff && secondSynchByte(buffer[i + 1])) + if(firstSyncByte(buffer[i]) && secondSynchByte(buffer[i + 1])) return position + i; } @@ -433,7 +446,7 @@ long MPEG::File::firstFrameOffset() { long position = 0; - if(ID3v2Tag()) { + if(hasID3v2Tag()) { position = d->ID3v2Location + ID3v2Tag()->header()->completeTagSize(); // Skip duplicate ID3v2 tags. @@ -456,7 +469,7 @@ long MPEG::File::firstFrameOffset() long MPEG::File::lastFrameOffset() { - return previousFrameOffset(ID3v1Tag() ? d->ID3v1Location - 1 : length()); + return previousFrameOffset(hasID3v1Tag() ? d->ID3v1Location - 1 : length()); } bool MPEG::File::hasID3v1Tag() const @@ -478,7 +491,7 @@ bool MPEG::File::hasAPETag() const // private members //////////////////////////////////////////////////////////////////////////////// -void MPEG::File::read(bool readProperties, Properties::ReadStyle propertiesStyle) +void MPEG::File::read(bool readProperties) { // Look for an ID3v2 tag @@ -517,7 +530,7 @@ void MPEG::File::read(bool readProperties, Properties::ReadStyle propertiesStyle } if(readProperties) - d->properties = new Properties(this, propertiesStyle); + d->properties = new Properties(this); // Make sure that we have our default tag types available. @@ -528,7 +541,7 @@ void MPEG::File::read(bool readProperties, Properties::ReadStyle propertiesStyle long MPEG::File::findID3v2(long offset) { // This method is based on the contents of TagLib::File::find(), but because - // of some subtlteies -- specifically the need to look for the bit pattern of + // of some subtleties -- specifically the need to look for the bit pattern of // an MPEG sync, it has been modified for use here. if(isValid() && ID3v2::Header::fileIdentifier().size() <= bufferSize()) { @@ -668,11 +681,3 @@ void MPEG::File::findAPE() d->APELocation = -1; d->APEFooterLocation = -1; } - -bool MPEG::File::secondSynchByte(char byte) -{ - std::bitset<8> b(byte); - - // check to see if the byte matches 111xxxxx - return b.test(7) && b.test(6) && b.test(5); -} diff --git a/3rdparty/taglib/mpeg/mpegfile.h b/3rdparty/taglib/mpeg/mpegfile.h index a03887a3e..858a6a5ca 100644 --- a/3rdparty/taglib/mpeg/mpegfile.h +++ b/3rdparty/taglib/mpeg/mpegfile.h @@ -173,7 +173,10 @@ namespace TagLib { * This is the same as calling save(AllTags); * * If you would like more granular control over the content of the tags, - * with the concession of generality, use paramaterized save call below. + * with the concession of generality, use parameterized save call below. + * + * \warning In the current implementation, it's dangerous to call save() + * repeatedly. At worst it will corrupt the file. * * \see save(int tags) */ @@ -187,6 +190,9 @@ namespace TagLib { * This strips all tags not included in the mask, but does not modify them * in memory, so later calls to save() which make use of these tags will * remain valid. This also strips empty tags. + * + * \warning In the current implementation, it's dangerous to call save() + * repeatedly. At worst it will corrupt the file. */ bool save(int tags); @@ -198,6 +204,9 @@ namespace TagLib { * If \a stripOthers is true this strips all tags not included in the mask, * but does not modify them in memory, so later calls to save() which make * use of these tags will remain valid. This also strips empty tags. + * + * \warning In the current implementation, it's dangerous to call save() + * repeatedly. At worst it will corrupt the file. */ // BIC: combine with the above method bool save(int tags, bool stripOthers); @@ -213,6 +222,9 @@ namespace TagLib { * * The \a id3v2Version parameter specifies the version of the saved * ID3v2 tag. It can be either 4 or 3. + * + * \warning In the current implementation, it's dangerous to call save() + * repeatedly. At worst it will corrupt the file. */ // BIC: combine with the above method bool save(int tags, bool stripOthers, int id3v2Version); @@ -231,6 +243,9 @@ namespace TagLib { * * If \a duplicateTags is true and at least one tag -- ID3v1 or ID3v2 -- * exists this will duplicate its content into the other tag. + * + * \warning In the current implementation, it's dangerous to call save() + * repeatedly. At worst it will corrupt the file. */ // BIC: combine with the above method bool save(int tags, bool stripOthers, int id3v2Version, bool duplicateTags); @@ -301,6 +316,8 @@ namespace TagLib { * * \note This will also invalidate pointers to the ID3 and APE tags * as their memory will be freed. + * + * \note This will update the file immediately. */ bool strip(int tags = AllTags); @@ -311,6 +328,8 @@ namespace TagLib { * * If \a freeMemory is true the ID3 and APE tags will be deleted and * pointers to them will be invalidated. + * + * \note This will update the file immediately. */ // BIC: merge with the method above bool strip(int tags, bool freeMemory); @@ -319,6 +338,7 @@ namespace TagLib { * Set the ID3v2::FrameFactory to something other than the default. * * \see ID3v2FrameFactory + * \deprecated This value should be passed in via the constructor */ void setID3v2FrameFactory(const ID3v2::FrameFactory *factory); @@ -369,18 +389,11 @@ namespace TagLib { File(const File &); File &operator=(const File &); - void read(bool readProperties, Properties::ReadStyle propertiesStyle); + void read(bool readProperties); long findID3v2(long offset); long findID3v1(); void findAPE(); - /*! - * MPEG frames can be recognized by the bit pattern 11111111 111, so the - * first byte is easy to check for, however checking to see if the second byte - * starts with \e 111 is a bit more tricky, hence this member function. - */ - static bool secondSynchByte(char byte); - class FilePrivate; FilePrivate *d; }; diff --git a/3rdparty/taglib/mpeg/mpegheader.cpp b/3rdparty/taglib/mpeg/mpegheader.cpp index a582f7589..0c9a0f1c9 100644 --- a/3rdparty/taglib/mpeg/mpegheader.cpp +++ b/3rdparty/taglib/mpeg/mpegheader.cpp @@ -213,8 +213,8 @@ void MPEG::Header::parse(const ByteVector &data) } }; - const int versionIndex = d->version == Version1 ? 0 : 1; - const int layerIndex = d->layer > 0 ? d->layer - 1 : 0; + const int versionIndex = (d->version == Version1) ? 0 : 1; + const int layerIndex = (d->layer > 0) ? d->layer - 1 : 0; // The bitrate index is encoded as the first 4 bits of the 3rd byte, // i.e. 1111xxxx @@ -253,13 +253,6 @@ void MPEG::Header::parse(const ByteVector &data) d->isCopyrighted = flags[3]; d->isPadded = flags[9]; - // Calculate the frame length - - if(d->layer == 1) - d->frameLength = 24000 * 2 * d->bitrate / d->sampleRate + int(d->isPadded); - else - d->frameLength = 72000 * d->bitrate / d->sampleRate + int(d->isPadded); - // Samples per frame static const int samplesPerFrame[3][2] = { @@ -271,6 +264,15 @@ void MPEG::Header::parse(const ByteVector &data) d->samplesPerFrame = samplesPerFrame[layerIndex][versionIndex]; + // Calculate the frame length + + static const int paddingSize[3] = { 4, 1, 1 }; + + d->frameLength = d->samplesPerFrame * d->bitrate * 125 / d->sampleRate; + + if(d->isPadded) + d->frameLength += paddingSize[layerIndex]; + // Now that we're done parsing, set this to be a valid frame. d->isValid = true; diff --git a/3rdparty/taglib/mpeg/mpegheader.h b/3rdparty/taglib/mpeg/mpegheader.h index 020ebd060..a55cac099 100644 --- a/3rdparty/taglib/mpeg/mpegheader.h +++ b/3rdparty/taglib/mpeg/mpegheader.h @@ -140,7 +140,7 @@ namespace TagLib { bool isOriginal() const; /*! - * Returns the frame length. + * Returns the frame length in bytes. */ int frameLength() const; diff --git a/3rdparty/taglib/mpeg/mpegproperties.cpp b/3rdparty/taglib/mpeg/mpegproperties.cpp index 3af956643..9fece404d 100644 --- a/3rdparty/taglib/mpeg/mpegproperties.cpp +++ b/3rdparty/taglib/mpeg/mpegproperties.cpp @@ -29,16 +29,16 @@ #include "mpegproperties.h" #include "mpegfile.h" #include "xingheader.h" +#include "apetag.h" +#include "apefooter.h" using namespace TagLib; class MPEG::Properties::PropertiesPrivate { public: - PropertiesPrivate(File *f, ReadStyle s) : - file(f), + PropertiesPrivate() : xingHeader(0), - style(s), length(0), bitrate(0), sampleRate(0), @@ -55,9 +55,7 @@ public: delete xingHeader; } - File *file; XingHeader *xingHeader; - ReadStyle style; int length; int bitrate; int sampleRate; @@ -74,12 +72,11 @@ public: // public members //////////////////////////////////////////////////////////////////////////////// -MPEG::Properties::Properties(File *file, ReadStyle style) : AudioProperties(style) +MPEG::Properties::Properties(File *file, ReadStyle style) : + AudioProperties(style), + d(new PropertiesPrivate()) { - d = new PropertiesPrivate(file, style); - - if(file && file->isOpen()) - read(); + read(file); } MPEG::Properties::~Properties() @@ -88,6 +85,16 @@ MPEG::Properties::~Properties() } int MPEG::Properties::length() const +{ + return lengthInSeconds(); +} + +int MPEG::Properties::lengthInSeconds() const +{ + return d->length / 1000; +} + +int MPEG::Properties::lengthInMilliseconds() const { return d->length; } @@ -146,109 +153,72 @@ bool MPEG::Properties::isOriginal() const // private members //////////////////////////////////////////////////////////////////////////////// -void MPEG::Properties::read() +void MPEG::Properties::read(File *file) { - // Since we've likely just looked for the ID3v1 tag, start at the end of the - // file where we're least likely to have to have to move the disk head. - - long last = d->file->lastFrameOffset(); - - if(last < 0) { - debug("MPEG::Properties::read() -- Could not find a valid last MPEG frame in the stream."); - return; - } - - d->file->seek(last); - Header lastHeader(d->file->readBlock(4)); - - long first = d->file->firstFrameOffset(); + // Only the first frame is required if we have a VBR header. + const long first = file->firstFrameOffset(); if(first < 0) { debug("MPEG::Properties::read() -- Could not find a valid first MPEG frame in the stream."); return; } - if(!lastHeader.isValid()) { + file->seek(first); + const Header firstHeader(file->readBlock(4)); - long pos = last; - - while(pos > first) { - - pos = d->file->previousFrameOffset(pos); - - if(pos < 0) - break; - - d->file->seek(pos); - Header header(d->file->readBlock(4)); - - if(header.isValid()) { - lastHeader = header; - last = pos; - break; - } - } - } - - // Now jump back to the front of the file and read what we need from there. - - d->file->seek(first); - Header firstHeader(d->file->readBlock(4)); - - if(!firstHeader.isValid() || !lastHeader.isValid()) { - debug("MPEG::Properties::read() -- Page headers were invalid."); + if(!firstHeader.isValid()) { + debug("MPEG::Properties::read() -- The first page header is invalid."); return; } - // Check for a Xing header that will help us in gathering information about a + // Check for a VBR header that will help us in gathering information about a // VBR stream. - int xingHeaderOffset = MPEG::XingHeader::xingHeaderOffset(firstHeader.version(), - firstHeader.channelMode()); - - d->file->seek(first + xingHeaderOffset); - d->xingHeader = new XingHeader(d->file->readBlock(16)); - - // Read the length and the bitrate from the Xing header. - - if(d->xingHeader->isValid() && - firstHeader.sampleRate() > 0 && - d->xingHeader->totalFrames() > 0) - { - double timePerFrame = - double(firstHeader.samplesPerFrame()) / firstHeader.sampleRate(); - - double length = timePerFrame * d->xingHeader->totalFrames(); - - d->length = int(length); - d->bitrate = d->length > 0 ? (int)(d->xingHeader->totalSize() * 8 / length / 1000) : 0; - } - else { - // Since there was no valid Xing header found, we hope that we're in a constant - // bitrate file. - + file->seek(first + 4); + d->xingHeader = new XingHeader(file->readBlock(firstHeader.frameLength() - 4)); + if(!d->xingHeader->isValid()) { delete d->xingHeader; d->xingHeader = 0; + } + + if(d->xingHeader && firstHeader.samplesPerFrame() > 0 && firstHeader.sampleRate() > 0) { + + // Read the length and the bitrate from the VBR header. + + const double timePerFrame = firstHeader.samplesPerFrame() * 1000.0 / firstHeader.sampleRate(); + const double length = timePerFrame * d->xingHeader->totalFrames(); + + d->length = static_cast(length + 0.5); + d->bitrate = static_cast(d->xingHeader->totalSize() * 8.0 / length + 0.5); + } + else if(firstHeader.bitrate() > 0) { + + // Since there was no valid VBR header found, we hope that we're in a constant + // bitrate file. // TODO: Make this more robust with audio property detection for VBR without a // Xing header. - if(firstHeader.frameLength() > 0 && firstHeader.bitrate() > 0) { - int frames = (last - first) / firstHeader.frameLength() + 1; + d->bitrate = firstHeader.bitrate(); - d->length = int(float(firstHeader.frameLength() * frames) / - float(firstHeader.bitrate() * 125) + 0.5); - d->bitrate = firstHeader.bitrate(); - } + long streamLength = file->length() - first; + + if(file->hasID3v1Tag()) + streamLength -= 128; + + if(file->hasAPETag()) + streamLength -= file->APETag()->footer()->completeTagSize(); + + if(streamLength > 0) + d->length = static_cast(streamLength * 8.0 / d->bitrate + 0.5); } - - d->sampleRate = firstHeader.sampleRate(); - d->channels = firstHeader.channelMode() == Header::SingleChannel ? 1 : 2; - d->version = firstHeader.version(); - d->layer = firstHeader.layer(); + d->sampleRate = firstHeader.sampleRate(); + d->channels = firstHeader.channelMode() == Header::SingleChannel ? 1 : 2; + d->version = firstHeader.version(); + d->layer = firstHeader.layer(); d->protectionEnabled = firstHeader.protectionEnabled(); - d->channelMode = firstHeader.channelMode(); - d->isCopyrighted = firstHeader.isCopyrighted(); - d->isOriginal = firstHeader.isOriginal(); + d->channelMode = firstHeader.channelMode(); + d->isCopyrighted = firstHeader.isCopyrighted(); + d->isOriginal = firstHeader.isOriginal(); } diff --git a/3rdparty/taglib/mpeg/mpegproperties.h b/3rdparty/taglib/mpeg/mpegproperties.h index 72e594ff5..f11fad112 100644 --- a/3rdparty/taglib/mpeg/mpegproperties.h +++ b/3rdparty/taglib/mpeg/mpegproperties.h @@ -59,18 +59,52 @@ namespace TagLib { */ virtual ~Properties(); - // Reimplementations. - + /*! + * Returns the length of the file in seconds. The length is rounded down to + * the nearest whole second. + * + * \note This method is just an alias of lengthInSeconds(). + * + * \deprecated + */ virtual int length() const; + + /*! + * Returns the length of the file in seconds. The length is rounded down to + * the nearest whole second. + * + * \see lengthInMilliseconds() + */ + // BIC: make virtual + int lengthInSeconds() const; + + /*! + * Returns the length of the file in milliseconds. + * + * \see lengthInSeconds() + */ + // BIC: make virtual + int lengthInMilliseconds() const; + + /*! + * Returns the average bit rate of the file in kb/s. + */ virtual int bitrate() const; + + /*! + * Returns the sample rate in Hz. + */ virtual int sampleRate() const; + + /*! + * Returns the number of audio channels. + */ virtual int channels() const; /*! - * Returns a pointer to the XingHeader if one exists or null if no - * XingHeader was found. + * Returns a pointer to the Xing/VBRI header if one exists or null if no + * Xing/VBRI header was found. */ - const XingHeader *xingHeader() const; /*! @@ -107,7 +141,7 @@ namespace TagLib { Properties(const Properties &); Properties &operator=(const Properties &); - void read(); + void read(File *file); class PropertiesPrivate; PropertiesPrivate *d; diff --git a/3rdparty/taglib/mpeg/xingheader.cpp b/3rdparty/taglib/mpeg/xingheader.cpp index 9e20127ee..5a4961845 100644 --- a/3rdparty/taglib/mpeg/xingheader.cpp +++ b/3rdparty/taglib/mpeg/xingheader.cpp @@ -28,6 +28,7 @@ #include #include "xingheader.h" +#include "mpegfile.h" using namespace TagLib; @@ -37,17 +38,21 @@ public: XingHeaderPrivate() : frames(0), size(0), - valid(false) - {} + type(MPEG::XingHeader::Invalid) {} uint frames; uint size; - bool valid; + + MPEG::XingHeader::HeaderType type; }; -MPEG::XingHeader::XingHeader(const ByteVector &data) +//////////////////////////////////////////////////////////////////////////////// +// public members +//////////////////////////////////////////////////////////////////////////////// + +MPEG::XingHeader::XingHeader(const ByteVector &data) : + d(new XingHeaderPrivate()) { - d = new XingHeaderPrivate; parse(data); } @@ -58,7 +63,7 @@ MPEG::XingHeader::~XingHeader() bool MPEG::XingHeader::isValid() const { - return d->valid; + return (d->type != Invalid && d->frames > 0 && d->size > 0); } TagLib::uint MPEG::XingHeader::totalFrames() const @@ -71,45 +76,65 @@ TagLib::uint MPEG::XingHeader::totalSize() const return d->size; } -int MPEG::XingHeader::xingHeaderOffset(TagLib::MPEG::Header::Version v, - TagLib::MPEG::Header::ChannelMode c) +MPEG::XingHeader::HeaderType MPEG::XingHeader::type() const { - if(v == MPEG::Header::Version1) { - if(c == MPEG::Header::SingleChannel) - return 0x15; - else - return 0x24; - } - else { - if(c == MPEG::Header::SingleChannel) - return 0x0D; - else - return 0x15; - } + return d->type; } +int MPEG::XingHeader::xingHeaderOffset(TagLib::MPEG::Header::Version /*v*/, + TagLib::MPEG::Header::ChannelMode /*c*/) +{ + return 0; +} + +//////////////////////////////////////////////////////////////////////////////// +// private members +//////////////////////////////////////////////////////////////////////////////// + void MPEG::XingHeader::parse(const ByteVector &data) { - // Check to see if a valid Xing header is available. + // Look for a Xing header. - if(!data.startsWith("Xing") && !data.startsWith("Info")) - return; + long offset = data.find("Xing"); + if(offset < 0) + offset = data.find("Info"); - // If the XingHeader doesn't contain the number of frames and the total stream - // info it's invalid. + if(offset >= 0) { - if(!(data[7] & 0x01)) { - debug("MPEG::XingHeader::parse() -- Xing header doesn't contain the total number of frames."); - return; + // Xing header found. + + if(data.size() < static_cast(offset + 16)) { + debug("MPEG::XingHeader::parse() -- Xing header found but too short."); + return; + } + + if((data[offset + 7] & 0x03) != 0x03) { + debug("MPEG::XingHeader::parse() -- Xing header doesn't contain the required information."); + return; + } + + d->frames = data.toUInt(offset + 8, true); + d->size = data.toUInt(offset + 12, true); + d->type = Xing; } + else { - if(!(data[7] & 0x02)) { - debug("MPEG::XingHeader::parse() -- Xing header doesn't contain the total stream size."); - return; + // Xing header not found. Then look for a VBRI header. + + offset = data.find("VBRI"); + + if(offset >= 0) { + + // VBRI header found. + + if(data.size() < static_cast(offset + 32)) { + debug("MPEG::XingHeader::parse() -- VBRI header found but too short."); + return; + } + + d->frames = data.toUInt(offset + 14, true); + d->size = data.toUInt(offset + 10, true); + d->type = VBRI; + } } - - d->frames = data.toUInt(8U); - d->size = data.toUInt(12U); - - d->valid = true; } diff --git a/3rdparty/taglib/mpeg/xingheader.h b/3rdparty/taglib/mpeg/xingheader.h index ffe7494d5..cd417157b 100644 --- a/3rdparty/taglib/mpeg/xingheader.h +++ b/3rdparty/taglib/mpeg/xingheader.h @@ -35,24 +35,47 @@ namespace TagLib { namespace MPEG { - //! An implementation of the Xing VBR headers + class File; + + //! An implementation of the Xing/VBRI headers /*! - * This is a minimalistic implementation of the Xing VBR headers. Xing - * headers are often added to VBR (variable bit rate) MP3 streams to make it - * easy to compute the length and quality of a VBR stream. Our implementation - * is only concerned with the total size of the stream (so that we can - * calculate the total playing time and the average bitrate). It uses - * this text - * and the XMMS sources as references. + * This is a minimalistic implementation of the Xing/VBRI VBR headers. + * Xing/VBRI headers are often added to VBR (variable bit rate) MP3 streams + * to make it easy to compute the length and quality of a VBR stream. Our + * implementation is only concerned with the total size of the stream (so + * that we can calculate the total playing time and the average bitrate). + * It uses + * this text and the XMMS sources as references. */ class TAGLIB_EXPORT XingHeader { public: /*! - * Parses a Xing header based on \a data. The data must be at least 16 - * bytes long (anything longer than this is discarded). + * The type of the VBR header. + */ + enum HeaderType + { + /*! + * Invalid header or no VBR header found. + */ + Invalid = 0, + + /*! + * Xing header. + */ + Xing = 1, + + /*! + * VBRI header. + */ + VBRI = 2, + }; + + /*! + * Parses an Xing/VBRI header based on \a data which contains the entire + * first MPEG frame. */ XingHeader(const ByteVector &data); @@ -63,7 +86,7 @@ namespace TagLib { /*! * Returns true if the data was parsed properly and if there is a valid - * Xing header present. + * Xing/VBRI header present. */ bool isValid() const; @@ -77,11 +100,17 @@ namespace TagLib { */ uint totalSize() const; + /*! + * Returns the type of the VBR header. + */ + HeaderType type() const; + /*! * Returns the offset for the start of this Xing header, given the * version and channels of the frame + * + * \deprecated Always returns 0. */ - // BIC: rename to offset() static int xingHeaderOffset(TagLib::MPEG::Header::Version v, TagLib::MPEG::Header::ChannelMode c); diff --git a/3rdparty/taglib/ogg/flac/oggflacfile.cpp b/3rdparty/taglib/ogg/flac/oggflacfile.cpp index bdf824595..1258e7051 100644 --- a/3rdparty/taglib/ogg/flac/oggflacfile.cpp +++ b/3rdparty/taglib/ogg/flac/oggflacfile.cpp @@ -103,7 +103,7 @@ PropertyMap Ogg::FLAC::File::properties() const PropertyMap Ogg::FLAC::File::setProperties(const PropertyMap &properties) { return d->comment->setProperties(properties); -} +} Properties *Ogg::FLAC::File::audioProperties() const { @@ -211,29 +211,30 @@ void Ogg::FLAC::File::scan() long overhead = 0; ByteVector metadataHeader = packet(ipacket); - if(metadataHeader.isNull()) + if(metadataHeader.isEmpty()) return; - ByteVector header; - - if (!metadataHeader.startsWith("fLaC")) { + if(!metadataHeader.startsWith("fLaC")) { // FLAC 1.1.2+ - if (metadataHeader.mid(1,4) != "FLAC") return; + if(metadataHeader.mid(1, 4) != "FLAC") + return; - if (metadataHeader[5] != 1) return; // not version 1 + if(metadataHeader[5] != 1) + return; // not version 1 metadataHeader = metadataHeader.mid(13); } else { // FLAC 1.1.0 & 1.1.1 metadataHeader = packet(++ipacket); - - if(metadataHeader.isNull()) - return; - } - header = metadataHeader.mid(0,4); + ByteVector header = metadataHeader.mid(0, 4); + if(header.size() != 4) { + debug("Ogg::FLAC::File::scan() -- Invalid Ogg/FLAC metadata header"); + return; + } + // Header format (from spec): // <1> Last-metadata-block flag // <7> BLOCK_TYPE @@ -262,11 +263,12 @@ void Ogg::FLAC::File::scan() while(!lastBlock) { metadataHeader = packet(++ipacket); - - if(metadataHeader.isNull()) - return; - header = metadataHeader.mid(0, 4); + if(header.size() != 4) { + debug("Ogg::FLAC::File::scan() -- Invalid Ogg/FLAC metadata header"); + return; + } + blockType = header[0] & 0x7f; lastBlock = (header[0] & 0x80) != 0; length = header.toUInt(1, 3, true); diff --git a/3rdparty/taglib/ogg/flac/oggflacfile.h b/3rdparty/taglib/ogg/flac/oggflacfile.h index 8d87e486b..28b3f67f8 100644 --- a/3rdparty/taglib/ogg/flac/oggflacfile.h +++ b/3rdparty/taglib/ogg/flac/oggflacfile.h @@ -42,7 +42,7 @@ namespace TagLib { /*! * This is implementation of FLAC metadata for Ogg FLAC files. For "pure" - * FLAC files look under the FLAC hiearchy. + * FLAC files look under the FLAC hierarchy. * * Unlike "pure" FLAC-files, Ogg FLAC only supports Xiph-comments, * while the audio-properties are the same. @@ -64,7 +64,7 @@ namespace TagLib { { public: /*! - * Constructs an Ogg/FLAC file from \a file. If \a readProperties is true + * Constructs an Ogg/FLAC file from \a file. If \a readProperties is true * the file's audio properties will also be read. * * \note In the current implementation, \a propertiesStyle is ignored. @@ -73,7 +73,7 @@ namespace TagLib { Properties::ReadStyle propertiesStyle = Properties::Average); /*! - * Constructs an Ogg/FLAC file from \a stream. If \a readProperties is true + * Constructs an Ogg/FLAC file from \a stream. If \a readProperties is true * the file's audio properties will also be read. * * \note TagLib will *not* take ownership of the stream, the caller is @@ -92,10 +92,10 @@ namespace TagLib { /*! * Returns the Tag for this file. This will always be a XiphComment. * - * \note This always returns a valid pointer regardless of whether or not - * the file on disk has a XiphComment. Use hasXiphComment() to check if + * \note This always returns a valid pointer regardless of whether or not + * the file on disk has a XiphComment. Use hasXiphComment() to check if * the file on disk actually has a XiphComment. - * + * * \note The Tag is still owned by the FLAC::File and should not be * deleted by the user. It will be deleted when the file (object) is * destroyed. @@ -111,22 +111,25 @@ namespace TagLib { virtual Properties *audioProperties() const; - /*! + /*! * Implements the unified property interface -- export function. * This forwards directly to XiphComment::properties(). */ PropertyMap properties() const; - /*! + /*! * Implements the unified tag dictionary interface -- import function. * Like properties(), this is a forwarder to the file's XiphComment. */ - PropertyMap setProperties(const PropertyMap &); + PropertyMap setProperties(const PropertyMap &); /*! * Save the file. This will primarily save and update the XiphComment. * Returns true if the save is successful. + * + * \warning In the current implementation, it's dangerous to call save() + * repeatedly. It leads to a segfault. */ virtual bool save(); diff --git a/3rdparty/taglib/ogg/oggfile.cpp b/3rdparty/taglib/ogg/oggfile.cpp index cc30f9ce1..dfd81ec64 100644 --- a/3rdparty/taglib/ogg/oggfile.cpp +++ b/3rdparty/taglib/ogg/oggfile.cpp @@ -354,7 +354,7 @@ void Ogg::File::writePageGroup(const List &thePageGroup) // create a gap for the new pages int numberOfNewPages = pages.back()->header()->pageSequenceNumber() - pageGroup.back(); - List::Iterator pageIter = d->pages.begin(); + List::ConstIterator pageIter = d->pages.begin(); for(int i = 0; i < pageGroup.back(); i++) { if(pageIter != d->pages.end()) { ++pageIter; diff --git a/3rdparty/taglib/ogg/oggpage.h b/3rdparty/taglib/ogg/oggpage.h index e9f4840c5..9ec794077 100644 --- a/3rdparty/taglib/ogg/oggpage.h +++ b/3rdparty/taglib/ogg/oggpage.h @@ -41,7 +41,7 @@ namespace TagLib { /*! * This is an implementation of the pages that make up an Ogg stream. * This handles parsing pages and breaking them down into packets and handles - * the details of packets spanning multiple pages and pages that contiain + * the details of packets spanning multiple pages and pages that contain * multiple packets. * * In most Xiph.org formats the comments are found in the first few packets, @@ -162,7 +162,7 @@ namespace TagLib { /*! * Pack \a packets into Ogg pages using the \a strategy for pagination. - * The page number indicater inside of the rendered packets will start + * The page number indicator inside of the rendered packets will start * with \a firstPage and be incremented for each page rendered. * \a containsLastPacket should be set to true if \a packets contains the * last page in the stream and will set the appropriate flag in the last diff --git a/3rdparty/taglib/ogg/opus/opusfile.cpp b/3rdparty/taglib/ogg/opus/opusfile.cpp index cb81a32b0..43c102d07 100644 --- a/3rdparty/taglib/ogg/opus/opusfile.cpp +++ b/3rdparty/taglib/ogg/opus/opusfile.cpp @@ -27,8 +27,6 @@ * http://www.mozilla.org/MPL/ * ***************************************************************************/ -#include - #include #include #include @@ -59,20 +57,20 @@ public: // public members //////////////////////////////////////////////////////////////////////////////// -Opus::File::File(FileName file, bool readProperties, Properties::ReadStyle propertiesStyle) : +Opus::File::File(FileName file, bool readProperties, Properties::ReadStyle) : Ogg::File(file), d(new FilePrivate()) { if(isOpen()) - read(readProperties, propertiesStyle); + read(readProperties); } -Opus::File::File(IOStream *stream, bool readProperties, Properties::ReadStyle propertiesStyle) : +Opus::File::File(IOStream *stream, bool readProperties, Properties::ReadStyle) : Ogg::File(stream), d(new FilePrivate()) { if(isOpen()) - read(readProperties, propertiesStyle); + read(readProperties); } Opus::File::~File() @@ -114,7 +112,7 @@ bool Opus::File::save() // private members //////////////////////////////////////////////////////////////////////////////// -void Opus::File::read(bool readProperties, Properties::ReadStyle propertiesStyle) +void Opus::File::read(bool readProperties) { ByteVector opusHeaderData = packet(0); @@ -135,5 +133,5 @@ void Opus::File::read(bool readProperties, Properties::ReadStyle propertiesStyle d->comment = new Ogg::XiphComment(commentHeaderData.mid(8)); if(readProperties) - d->properties = new Properties(this, propertiesStyle); + d->properties = new Properties(this); } diff --git a/3rdparty/taglib/ogg/opus/opusfile.h b/3rdparty/taglib/ogg/opus/opusfile.h index 275167e40..0363b5846 100644 --- a/3rdparty/taglib/ogg/opus/opusfile.h +++ b/3rdparty/taglib/ogg/opus/opusfile.h @@ -106,13 +106,21 @@ namespace TagLib { */ virtual Properties *audioProperties() const; + /*! + * Save the file. + * + * This returns true if the save was successful. + * + * \warning In the current implementation, it's dangerous to call save() + * repeatedly. It leads to a segfault. + */ virtual bool save(); private: File(const File &); File &operator=(const File &); - void read(bool readProperties, Properties::ReadStyle propertiesStyle); + void read(bool readProperties); class FilePrivate; FilePrivate *d; diff --git a/3rdparty/taglib/ogg/opus/opusproperties.cpp b/3rdparty/taglib/ogg/opus/opusproperties.cpp index 7bdcd39df..c8aff6246 100644 --- a/3rdparty/taglib/ogg/opus/opusproperties.cpp +++ b/3rdparty/taglib/ogg/opus/opusproperties.cpp @@ -41,17 +41,15 @@ using namespace TagLib::Ogg; class Opus::Properties::PropertiesPrivate { public: - PropertiesPrivate(File *f, ReadStyle s) : - file(f), - style(s), + PropertiesPrivate() : length(0), + bitrate(0), inputSampleRate(0), channels(0), opusVersion(0) {} - File *file; - ReadStyle style; int length; + int bitrate; int inputSampleRate; int channels; int opusVersion; @@ -61,10 +59,11 @@ public: // public members //////////////////////////////////////////////////////////////////////////////// -Opus::Properties::Properties(File *file, ReadStyle style) : AudioProperties(style) +Opus::Properties::Properties(File *file, ReadStyle style) : + AudioProperties(style), + d(new PropertiesPrivate()) { - d = new PropertiesPrivate(file, style); - read(); + read(file); } Opus::Properties::~Properties() @@ -73,13 +72,23 @@ Opus::Properties::~Properties() } int Opus::Properties::length() const +{ + return lengthInSeconds(); +} + +int Ogg::Opus::Properties::lengthInSeconds() const +{ + return d->length / 1000; +} + +int Ogg::Opus::Properties::lengthInMilliseconds() const { return d->length; } int Opus::Properties::bitrate() const { - return 0; + return d->bitrate; } int Opus::Properties::sampleRate() const @@ -109,13 +118,13 @@ int Opus::Properties::opusVersion() const // private members //////////////////////////////////////////////////////////////////////////////// -void Opus::Properties::read() +void Opus::Properties::read(File *file) { // Get the identification header from the Ogg implementation. // http://tools.ietf.org/html/draft-terriberry-oggopus-01#section-5.1 - ByteVector data = d->file->packet(0); + const ByteVector data = file->packet(0); // *Magic Signature* uint pos = 8; @@ -142,15 +151,22 @@ void Opus::Properties::read() // *Channel Mapping Family* (8 bits, unsigned) pos += 1; - const Ogg::PageHeader *first = d->file->firstPageHeader(); - const Ogg::PageHeader *last = d->file->lastPageHeader(); + const Ogg::PageHeader *first = file->firstPageHeader(); + const Ogg::PageHeader *last = file->lastPageHeader(); if(first && last) { - long long start = first->absoluteGranularPosition(); - long long end = last->absoluteGranularPosition(); + const long long start = first->absoluteGranularPosition(); + const long long end = last->absoluteGranularPosition(); - if(start >= 0 && end >= 0) - d->length = (int) ((end - start - preSkip) / 48000); + if(start >= 0 && end >= 0) { + const long long frameCount = (end - start - preSkip); + + if(frameCount > 0) { + const double length = frameCount * 1000.0 / 48000.0; + d->length = static_cast(length + 0.5); + d->bitrate = static_cast(file->length() * 8.0 / length + 0.5); + } + } else { debug("Opus::Properties::read() -- The PCM values for the start or " "end of this file was incorrect."); diff --git a/3rdparty/taglib/ogg/opus/opusproperties.h b/3rdparty/taglib/ogg/opus/opusproperties.h index 946f16757..be88b1461 100644 --- a/3rdparty/taglib/ogg/opus/opusproperties.h +++ b/3rdparty/taglib/ogg/opus/opusproperties.h @@ -61,11 +61,49 @@ namespace TagLib { */ virtual ~Properties(); - // Reimplementations. - + /*! + * Returns the length of the file in seconds. The length is rounded down to + * the nearest whole second. + * + * \note This method is just an alias of lengthInSeconds(). + * + * \deprecated + */ virtual int length() const; + + /*! + * Returns the length of the file in seconds. The length is rounded down to + * the nearest whole second. + * + * \see lengthInMilliseconds() + */ + // BIC: make virtual + int lengthInSeconds() const; + + /*! + * Returns the length of the file in milliseconds. + * + * \see lengthInSeconds() + */ + // BIC: make virtual + int lengthInMilliseconds() const; + + /*! + * Returns the average bit rate of the file in kb/s. + */ virtual int bitrate() const; + + /*! + * Returns the sample rate in Hz. + * + * \note Always returns 48000, because Opus can decode any stream at a + * sample rate of 8, 12, 16, 24, or 48 kHz, + */ virtual int sampleRate() const; + + /*! + * Returns the number of audio channels. + */ virtual int channels() const; /*! @@ -76,7 +114,7 @@ namespace TagLib { int inputSampleRate() const; /*! - * Returns the Opus version, currently "0" (as specified by the spec). + * Returns the Opus version, in the range 0...255. */ int opusVersion() const; @@ -84,7 +122,7 @@ namespace TagLib { Properties(const Properties &); Properties &operator=(const Properties &); - void read(); + void read(File *file); class PropertiesPrivate; PropertiesPrivate *d; diff --git a/3rdparty/taglib/ogg/speex/speexfile.cpp b/3rdparty/taglib/ogg/speex/speexfile.cpp index e83f0ad9b..2d374aedc 100644 --- a/3rdparty/taglib/ogg/speex/speexfile.cpp +++ b/3rdparty/taglib/ogg/speex/speexfile.cpp @@ -27,8 +27,6 @@ * http://www.mozilla.org/MPL/ * ***************************************************************************/ -#include - #include #include #include @@ -59,20 +57,20 @@ public: // public members //////////////////////////////////////////////////////////////////////////////// -Speex::File::File(FileName file, bool readProperties, - Properties::ReadStyle propertiesStyle) : Ogg::File(file) +Speex::File::File(FileName file, bool readProperties, Properties::ReadStyle) : + Ogg::File(file), + d(new FilePrivate()) { - d = new FilePrivate; if(isOpen()) - read(readProperties, propertiesStyle); + read(readProperties); } -Speex::File::File(IOStream *stream, bool readProperties, - Properties::ReadStyle propertiesStyle) : Ogg::File(stream) +Speex::File::File(IOStream *stream, bool readProperties, Properties::ReadStyle) : + Ogg::File(stream), + d(new FilePrivate()) { - d = new FilePrivate; if(isOpen()) - read(readProperties, propertiesStyle); + read(readProperties); } Speex::File::~File() @@ -114,7 +112,7 @@ bool Speex::File::save() // private members //////////////////////////////////////////////////////////////////////////////// -void Speex::File::read(bool readProperties, Properties::ReadStyle propertiesStyle) +void Speex::File::read(bool readProperties) { ByteVector speexHeaderData = packet(0); @@ -128,5 +126,5 @@ void Speex::File::read(bool readProperties, Properties::ReadStyle propertiesStyl d->comment = new Ogg::XiphComment(commentHeaderData); if(readProperties) - d->properties = new Properties(this, propertiesStyle); + d->properties = new Properties(this); } diff --git a/3rdparty/taglib/ogg/speex/speexfile.h b/3rdparty/taglib/ogg/speex/speexfile.h index 880e68904..de38bfbfc 100644 --- a/3rdparty/taglib/ogg/speex/speexfile.h +++ b/3rdparty/taglib/ogg/speex/speexfile.h @@ -106,15 +106,21 @@ namespace TagLib { */ virtual Properties *audioProperties() const; - - + /*! + * Save the file. + * + * This returns true if the save was successful. + * + * \warning In the current implementation, it's dangerous to call save() + * repeatedly. It leads to a segfault. + */ virtual bool save(); private: File(const File &); File &operator=(const File &); - void read(bool readProperties, Properties::ReadStyle propertiesStyle); + void read(bool readProperties); class FilePrivate; FilePrivate *d; diff --git a/3rdparty/taglib/ogg/speex/speexproperties.cpp b/3rdparty/taglib/ogg/speex/speexproperties.cpp index 5aaa91530..e486e4499 100644 --- a/3rdparty/taglib/ogg/speex/speexproperties.cpp +++ b/3rdparty/taglib/ogg/speex/speexproperties.cpp @@ -41,21 +41,19 @@ using namespace TagLib::Ogg; class Speex::Properties::PropertiesPrivate { public: - PropertiesPrivate(File *f, ReadStyle s) : - file(f), - style(s), + PropertiesPrivate() : length(0), bitrate(0), + bitrateNominal(0), sampleRate(0), channels(0), speexVersion(0), vbr(false), mode(0) {} - File *file; - ReadStyle style; int length; int bitrate; + int bitrateNominal; int sampleRate; int channels; int speexVersion; @@ -67,10 +65,11 @@ public: // public members //////////////////////////////////////////////////////////////////////////////// -Speex::Properties::Properties(File *file, ReadStyle style) : AudioProperties(style) +Speex::Properties::Properties(File *file, ReadStyle style) : + AudioProperties(style), + d(new PropertiesPrivate()) { - d = new PropertiesPrivate(file, style); - read(); + read(file); } Speex::Properties::~Properties() @@ -79,13 +78,28 @@ Speex::Properties::~Properties() } int Speex::Properties::length() const +{ + return lengthInSeconds(); +} + +int Speex::Properties::lengthInSeconds() const +{ + return d->length / 1000; +} + +int Speex::Properties::lengthInMilliseconds() const { return d->length; } int Speex::Properties::bitrate() const { - return int(float(d->bitrate) / float(1000) + 0.5); + return d->bitrate; +} + +int Speex::Properties::bitrateNominal() const +{ + return d->bitrateNominal; } int Speex::Properties::sampleRate() const @@ -107,11 +121,15 @@ int Speex::Properties::speexVersion() const // private members //////////////////////////////////////////////////////////////////////////////// -void Speex::Properties::read() +void Speex::Properties::read(File *file) { // Get the identification header from the Ogg implementation. - ByteVector data = d->file->packet(0); + const ByteVector data = file->packet(0); + if(data.size() < 64) { + debug("Speex::Properties::read() -- data is too short."); + return; + } uint pos = 28; @@ -138,7 +156,7 @@ void Speex::Properties::read() pos += 4; // bitrate; /**< Bit-rate used */ - d->bitrate = data.toUInt(pos, false); + d->bitrateNominal = data.toUInt(pos, false); pos += 4; // frame_size; /**< Size of frames */ @@ -152,19 +170,32 @@ void Speex::Properties::read() // frames_per_packet; /**< Number of frames stored per Ogg packet */ // unsigned int framesPerPacket = data.mid(pos, 4).toUInt(false); - const Ogg::PageHeader *first = d->file->firstPageHeader(); - const Ogg::PageHeader *last = d->file->lastPageHeader(); + const Ogg::PageHeader *first = file->firstPageHeader(); + const Ogg::PageHeader *last = file->lastPageHeader(); if(first && last) { - long long start = first->absoluteGranularPosition(); - long long end = last->absoluteGranularPosition(); + const long long start = first->absoluteGranularPosition(); + const long long end = last->absoluteGranularPosition(); - if(start >= 0 && end >= 0 && d->sampleRate > 0) - d->length = (int) ((end - start) / (long long) d->sampleRate); - else + if(start >= 0 && end >= 0 && d->sampleRate > 0) { + const long long frameCount = end - start; + + if(frameCount > 0) { + const double length = frameCount * 1000.0 / d->sampleRate; + d->length = static_cast(length + 0.5); + d->bitrate = static_cast(file->length() * 8.0 / length + 0.5); + } + } + else { debug("Speex::Properties::read() -- Either the PCM values for the start or " "end of this file was incorrect or the sample rate is zero."); + } } else debug("Speex::Properties::read() -- Could not find valid first and last Ogg pages."); + + // Alternative to the actual average bitrate. + + if(d->bitrate == 0 && d->bitrateNominal > 0) + d->bitrate = static_cast(d->bitrateNominal / 1000.0 + 0.5); } diff --git a/3rdparty/taglib/ogg/speex/speexproperties.h b/3rdparty/taglib/ogg/speex/speexproperties.h index 4720bd881..64e6fac37 100644 --- a/3rdparty/taglib/ogg/speex/speexproperties.h +++ b/3rdparty/taglib/ogg/speex/speexproperties.h @@ -61,11 +61,51 @@ namespace TagLib { */ virtual ~Properties(); - // Reimplementations. - + /*! + * Returns the length of the file in seconds. The length is rounded down to + * the nearest whole second. + * + * \note This method is just an alias of lengthInSeconds(). + * + * \deprecated + */ virtual int length() const; + + /*! + * Returns the length of the file in seconds. The length is rounded down to + * the nearest whole second. + * + * \see lengthInMilliseconds() + */ + // BIC: make virtual + int lengthInSeconds() const; + + /*! + * Returns the length of the file in milliseconds. + * + * \see lengthInSeconds() + */ + // BIC: make virtual + int lengthInMilliseconds() const; + + /*! + * Returns the average bit rate of the file in kb/s. + */ virtual int bitrate() const; + + /*! + * Returns the nominal bit rate as read from the Speex header in kb/s. + */ + int bitrateNominal() const; + + /*! + * Returns the sample rate in Hz. + */ virtual int sampleRate() const; + + /*! + * Returns the number of audio channels. + */ virtual int channels() const; /*! @@ -77,7 +117,7 @@ namespace TagLib { Properties(const Properties &); Properties &operator=(const Properties &); - void read(); + void read(File *file); class PropertiesPrivate; PropertiesPrivate *d; diff --git a/3rdparty/taglib/ogg/vorbis/vorbisfile.cpp b/3rdparty/taglib/ogg/vorbis/vorbisfile.cpp index 82984536b..1e19e8629 100644 --- a/3rdparty/taglib/ogg/vorbis/vorbisfile.cpp +++ b/3rdparty/taglib/ogg/vorbis/vorbisfile.cpp @@ -63,20 +63,20 @@ namespace TagLib { // public members //////////////////////////////////////////////////////////////////////////////// -Vorbis::File::File(FileName file, bool readProperties, - Properties::ReadStyle propertiesStyle) : Ogg::File(file) +Vorbis::File::File(FileName file, bool readProperties, Properties::ReadStyle) : + Ogg::File(file), + d(new FilePrivate()) { - d = new FilePrivate; if(isOpen()) - read(readProperties, propertiesStyle); + read(readProperties); } -Vorbis::File::File(IOStream *stream, bool readProperties, - Properties::ReadStyle propertiesStyle) : Ogg::File(stream) +Vorbis::File::File(IOStream *stream, bool readProperties, Properties::ReadStyle) : + Ogg::File(stream), + d(new FilePrivate()) { - d = new FilePrivate; if(isOpen()) - read(readProperties, propertiesStyle); + read(readProperties); } Vorbis::File::~File() @@ -121,7 +121,7 @@ bool Vorbis::File::save() // private members //////////////////////////////////////////////////////////////////////////////// -void Vorbis::File::read(bool readProperties, Properties::ReadStyle propertiesStyle) +void Vorbis::File::read(bool readProperties) { ByteVector commentHeaderData = packet(1); @@ -134,5 +134,5 @@ void Vorbis::File::read(bool readProperties, Properties::ReadStyle propertiesSty d->comment = new Ogg::XiphComment(commentHeaderData.mid(7)); if(readProperties) - d->properties = new Properties(this, propertiesStyle); + d->properties = new Properties(this); } diff --git a/3rdparty/taglib/ogg/vorbis/vorbisfile.h b/3rdparty/taglib/ogg/vorbis/vorbisfile.h index 7735a11be..48d9d7ca2 100644 --- a/3rdparty/taglib/ogg/vorbis/vorbisfile.h +++ b/3rdparty/taglib/ogg/vorbis/vorbisfile.h @@ -114,13 +114,21 @@ namespace TagLib { */ virtual Properties *audioProperties() const; + /*! + * Save the file. + * + * This returns true if the save was successful. + * + * \warning In the current implementation, it's dangerous to call save() + * repeatedly. It leads to a segfault. + */ virtual bool save(); private: File(const File &); File &operator=(const File &); - void read(bool readProperties, Properties::ReadStyle propertiesStyle); + void read(bool readProperties); class FilePrivate; FilePrivate *d; diff --git a/3rdparty/taglib/ogg/vorbis/vorbisproperties.cpp b/3rdparty/taglib/ogg/vorbis/vorbisproperties.cpp index b5e88bfd0..d9dfc0a89 100644 --- a/3rdparty/taglib/ogg/vorbis/vorbisproperties.cpp +++ b/3rdparty/taglib/ogg/vorbis/vorbisproperties.cpp @@ -36,9 +36,7 @@ using namespace TagLib; class Vorbis::Properties::PropertiesPrivate { public: - PropertiesPrivate(File *f, ReadStyle s) : - file(f), - style(s), + PropertiesPrivate() : length(0), bitrate(0), sampleRate(0), @@ -48,8 +46,6 @@ public: bitrateNominal(0), bitrateMinimum(0) {} - File *file; - ReadStyle style; int length; int bitrate; int sampleRate; @@ -72,10 +68,11 @@ namespace TagLib { // public members //////////////////////////////////////////////////////////////////////////////// -Vorbis::Properties::Properties(File *file, ReadStyle style) : AudioProperties(style) +Vorbis::Properties::Properties(File *file, ReadStyle style) : + AudioProperties(style), + d(new PropertiesPrivate()) { - d = new PropertiesPrivate(file, style); - read(); + read(file); } Vorbis::Properties::~Properties() @@ -84,13 +81,23 @@ Vorbis::Properties::~Properties() } int Vorbis::Properties::length() const +{ + return lengthInSeconds(); +} + +int Vorbis::Properties::lengthInSeconds() const +{ + return d->length / 1000; +} + +int Vorbis::Properties::lengthInMilliseconds() const { return d->length; } int Vorbis::Properties::bitrate() const { - return int(float(d->bitrate) / float(1000) + 0.5); + return d->bitrate; } int Vorbis::Properties::sampleRate() const @@ -127,11 +134,15 @@ int Vorbis::Properties::bitrateMinimum() const // private members //////////////////////////////////////////////////////////////////////////////// -void Vorbis::Properties::read() +void Vorbis::Properties::read(File *file) { // Get the identification header from the Ogg implementation. - ByteVector data = d->file->packet(0); + const ByteVector data = file->packet(0); + if(data.size() < 28) { + debug("Vorbis::Properties::read() -- data is too short."); + return; + } uint pos = 0; @@ -158,26 +169,38 @@ void Vorbis::Properties::read() pos += 4; d->bitrateMinimum = data.toUInt(pos, false); - - // TODO: Later this should be only the "fast" mode. - d->bitrate = d->bitrateNominal; + pos += 4; // Find the length of the file. See http://wiki.xiph.org/VorbisStreamLength/ // for my notes on the topic. - const Ogg::PageHeader *first = d->file->firstPageHeader(); - const Ogg::PageHeader *last = d->file->lastPageHeader(); + const Ogg::PageHeader *first = file->firstPageHeader(); + const Ogg::PageHeader *last = file->lastPageHeader(); if(first && last) { - long long start = first->absoluteGranularPosition(); - long long end = last->absoluteGranularPosition(); + const long long start = first->absoluteGranularPosition(); + const long long end = last->absoluteGranularPosition(); - if(start >= 0 && end >= 0 && d->sampleRate > 0) - d->length = (int)((end - start) / (long long) d->sampleRate); - else + if(start >= 0 && end >= 0 && d->sampleRate > 0) { + const long long frameCount = end - start; + + if(frameCount > 0) { + const double length = frameCount * 1000.0 / d->sampleRate; + + d->length = static_cast(length + 0.5); + d->bitrate = static_cast(file->length() * 8.0 / length + 0.5); + } + } + else { debug("Vorbis::Properties::read() -- Either the PCM values for the start or " "end of this file was incorrect or the sample rate is zero."); + } } else debug("Vorbis::Properties::read() -- Could not find valid first and last Ogg pages."); + + // Alternative to the actual average bitrate. + + if(d->bitrate == 0 && d->bitrateNominal > 0) + d->bitrate = static_cast(d->bitrateNominal / 1000.0 + 0.5); } diff --git a/3rdparty/taglib/ogg/vorbis/vorbisproperties.h b/3rdparty/taglib/ogg/vorbis/vorbisproperties.h index de46985b6..9da0ac9d7 100644 --- a/3rdparty/taglib/ogg/vorbis/vorbisproperties.h +++ b/3rdparty/taglib/ogg/vorbis/vorbisproperties.h @@ -67,11 +67,46 @@ namespace TagLib { */ virtual ~Properties(); - // Reimplementations. - + /*! + * Returns the length of the file in seconds. The length is rounded down to + * the nearest whole second. + * + * \note This method is just an alias of lengthInSeconds(). + * + * \deprecated + */ virtual int length() const; + + /*! + * Returns the length of the file in seconds. The length is rounded down to + * the nearest whole second. + * + * \see lengthInMilliseconds() + */ + // BIC: make virtual + int lengthInSeconds() const; + + /*! + * Returns the length of the file in milliseconds. + * + * \see lengthInSeconds() + */ + // BIC: make virtual + int lengthInMilliseconds() const; + + /*! + * Returns the average bit rate of the file in kb/s. + */ virtual int bitrate() const; + + /*! + * Returns the sample rate in Hz. + */ virtual int sampleRate() const; + + /*! + * Returns the number of audio channels. + */ virtual int channels() const; /*! @@ -101,7 +136,7 @@ namespace TagLib { Properties(const Properties &); Properties &operator=(const Properties &); - void read(); + void read(File *file); class PropertiesPrivate; PropertiesPrivate *d; diff --git a/3rdparty/taglib/ogg/xiphcomment.cpp b/3rdparty/taglib/ogg/xiphcomment.cpp index 51c2f9f04..9462607f4 100644 --- a/3rdparty/taglib/ogg/xiphcomment.cpp +++ b/3rdparty/taglib/ogg/xiphcomment.cpp @@ -63,33 +63,33 @@ String Ogg::XiphComment::title() const { if(d->fieldListMap["TITLE"].isEmpty()) return String::null; - return d->fieldListMap["TITLE"].front(); + return d->fieldListMap["TITLE"].toString(); } String Ogg::XiphComment::artist() const { if(d->fieldListMap["ARTIST"].isEmpty()) return String::null; - return d->fieldListMap["ARTIST"].front(); + return d->fieldListMap["ARTIST"].toString(); } String Ogg::XiphComment::album() const { if(d->fieldListMap["ALBUM"].isEmpty()) return String::null; - return d->fieldListMap["ALBUM"].front(); + return d->fieldListMap["ALBUM"].toString(); } String Ogg::XiphComment::comment() const { if(!d->fieldListMap["DESCRIPTION"].isEmpty()) { d->commentField = "DESCRIPTION"; - return d->fieldListMap["DESCRIPTION"].front(); + return d->fieldListMap["DESCRIPTION"].toString(); } if(!d->fieldListMap["COMMENT"].isEmpty()) { d->commentField = "COMMENT"; - return d->fieldListMap["COMMENT"].front(); + return d->fieldListMap["COMMENT"].toString(); } return String::null; @@ -99,7 +99,7 @@ String Ogg::XiphComment::genre() const { if(d->fieldListMap["GENRE"].isEmpty()) return String::null; - return d->fieldListMap["GENRE"].front(); + return d->fieldListMap["GENRE"].toString(); } TagLib::uint Ogg::XiphComment::year() const diff --git a/3rdparty/taglib/patches/01-too-many-flac-metadata-blocks.patch b/3rdparty/taglib/patches/01-too-many-flac-metadata-blocks.patch deleted file mode 100644 index 79d8a7c6e..000000000 --- a/3rdparty/taglib/patches/01-too-many-flac-metadata-blocks.patch +++ /dev/null @@ -1,29 +0,0 @@ -commit 0b55f03fa092241dd4533ef0fd4a18b2d97a1b25 -Author: David Sansome -Date: Sun Feb 17 18:39:49 2013 +1100 - - Stop loading a FLAC file if it has too many metadata blocks. - -diff --git a/3rdparty/taglib/flac/flacfile.cpp b/3rdparty/taglib/flac/flacfile.cpp -index a02770a..d308256 100644 ---- a/3rdparty/taglib/flac/flacfile.cpp -+++ b/3rdparty/taglib/flac/flacfile.cpp -@@ -416,9 +416,18 @@ void FLAC::File::scan() - d->blocks.append(new UnknownMetadataBlock(blockType, d->streamInfoData)); - nextBlockOffset += length + 4; - -+ int blockCount = 0; -+ - // Search through the remaining metadata - while(!isLastBlock) { - -+ if (++blockCount > 1024) { -+ debug("FLAC::File::scan() -- FLAC stream has more than 1024 metadata " -+ "blocks, probably corrupt."); -+ setValid(false); -+ return; -+ } -+ - header = readBlock(4); - blockType = header[0] & 0x7f; - isLastBlock = (header[0] & 0x80) != 0; diff --git a/3rdparty/taglib/patches/02-swap-big-endian.patch b/3rdparty/taglib/patches/02-swap-big-endian.patch deleted file mode 100644 index e2a9e472f..000000000 --- a/3rdparty/taglib/patches/02-swap-big-endian.patch +++ /dev/null @@ -1,22 +0,0 @@ -diff --git a/3rdparty/taglib/toolkit/tbytevector.cpp b/3rdparty/taglib/toolkit/tbytevector.cpp -index ddc3d4f..f36758f 100644 ---- a/3rdparty/taglib/toolkit/tbytevector.cpp -+++ b/3rdparty/taglib/toolkit/tbytevector.cpp -@@ -218,7 +218,7 @@ T toNumber(const ByteVector &v, size_t offset, bool mostSignificantByteFirst) - #if SYSTEM_BYTEORDER == 1 - const bool swap = mostSignificantByteFirst; - #else -- const bool swap != mostSignificantByteFirst; -+ const bool swap = !mostSignificantByteFirst; - #endif - if(swap) - return byteSwap(tmp); -@@ -234,7 +234,7 @@ ByteVector fromNumber(T value, bool mostSignificantByteFirst) - #if SYSTEM_BYTEORDER == 1 - const bool swap = mostSignificantByteFirst; - #else -- const bool swap != mostSignificantByteFirst; -+ const bool swap = !mostSignificantByteFirst; - #endif - if(swap) - value = byteSwap(value); diff --git a/3rdparty/taglib/riff/aiff/aifffile.cpp b/3rdparty/taglib/riff/aiff/aifffile.cpp index d20c148be..35eedd8e3 100644 --- a/3rdparty/taglib/riff/aiff/aifffile.cpp +++ b/3rdparty/taglib/riff/aiff/aifffile.cpp @@ -39,10 +39,8 @@ public: FilePrivate() : properties(0), tag(0), - tagChunkID("ID3 ") - { - - } + tagChunkID("ID3 "), + hasID3v2(false) {} ~FilePrivate() { @@ -53,26 +51,28 @@ public: Properties *properties; ID3v2::Tag *tag; ByteVector tagChunkID; + + bool hasID3v2; }; //////////////////////////////////////////////////////////////////////////////// // public members //////////////////////////////////////////////////////////////////////////////// -RIFF::AIFF::File::File(FileName file, bool readProperties, - Properties::ReadStyle propertiesStyle) : RIFF::File(file, BigEndian) +RIFF::AIFF::File::File(FileName file, bool readProperties, Properties::ReadStyle) : + RIFF::File(file, BigEndian), + d(new FilePrivate()) { - d = new FilePrivate; if(isOpen()) - read(readProperties, propertiesStyle); + read(readProperties); } -RIFF::AIFF::File::File(IOStream *stream, bool readProperties, - Properties::ReadStyle propertiesStyle) : RIFF::File(stream, BigEndian) +RIFF::AIFF::File::File(IOStream *stream, bool readProperties, Properties::ReadStyle) : + RIFF::File(stream, BigEndian), + d(new FilePrivate()) { - d = new FilePrivate; if(isOpen()) - read(readProperties, propertiesStyle); + read(readProperties); } RIFF::AIFF::File::~File() @@ -100,7 +100,6 @@ PropertyMap RIFF::AIFF::File::setProperties(const PropertyMap &properties) return d->tag->setProperties(properties); } - RIFF::AIFF::Properties *RIFF::AIFF::File::audioProperties() const { return d->properties; @@ -119,26 +118,39 @@ bool RIFF::AIFF::File::save() } setChunkData(d->tagChunkID, d->tag->render()); + d->hasID3v2 = true; return true; } +bool RIFF::AIFF::File::hasID3v2Tag() const +{ + return d->hasID3v2; +} //////////////////////////////////////////////////////////////////////////////// // private members //////////////////////////////////////////////////////////////////////////////// -void RIFF::AIFF::File::read(bool readProperties, Properties::ReadStyle propertiesStyle) +void RIFF::AIFF::File::read(bool readProperties) { - for(uint i = 0; i < chunkCount(); i++) { - if(chunkName(i) == "ID3 " || chunkName(i) == "id3 ") { - d->tagChunkID = chunkName(i); - d->tag = new ID3v2::Tag(this, chunkOffset(i)); + for(uint i = 0; i < chunkCount(); ++i) { + const ByteVector name = chunkName(i); + if(name == "ID3 " || name == "id3 ") { + if(!d->tag) { + d->tag = new ID3v2::Tag(this, chunkOffset(i)); + d->tagChunkID = name; + d->hasID3v2 = true; + } + else { + debug("RIFF::AIFF::File::read() - Duplicate ID3v2 tag found."); + } } - else if(chunkName(i) == "COMM" && readProperties) - d->properties = new Properties(chunkData(i), propertiesStyle); } if(!d->tag) - d->tag = new ID3v2::Tag; + d->tag = new ID3v2::Tag(); + + if(readProperties) + d->properties = new Properties(this, Properties::Average); } diff --git a/3rdparty/taglib/riff/aiff/aifffile.h b/3rdparty/taglib/riff/aiff/aifffile.h index c75b94b4c..a79d76b2b 100644 --- a/3rdparty/taglib/riff/aiff/aifffile.h +++ b/3rdparty/taglib/riff/aiff/aifffile.h @@ -86,8 +86,8 @@ namespace TagLib { /*! * Returns the Tag for this file. * - * \note This always returns a valid pointer regardless of whether or not - * the file on disk has an ID3v2 tag. Use hasID3v2Tag() to check if the file + * \note This always returns a valid pointer regardless of whether or not + * the file on disk has an ID3v2 tag. Use hasID3v2Tag() to check if the file * on disk actually has an ID3v2 tag. * * \see hasID3v2Tag() @@ -130,7 +130,9 @@ namespace TagLib { File(const File &); File &operator=(const File &); - void read(bool readProperties, Properties::ReadStyle propertiesStyle); + void read(bool readProperties); + + friend class Properties; class FilePrivate; FilePrivate *d; diff --git a/3rdparty/taglib/riff/aiff/aiffproperties.cpp b/3rdparty/taglib/riff/aiff/aiffproperties.cpp index 18b973eb0..e345fb0c0 100644 --- a/3rdparty/taglib/riff/aiff/aiffproperties.cpp +++ b/3rdparty/taglib/riff/aiff/aiffproperties.cpp @@ -25,56 +25,9 @@ #include #include -#include -// ldexp is a c99 function, which might not be defined in -// so we pull in math.h too and hope it does the right (wrong) thing -// wrt. c99 functions in C++ -#include - +#include "aifffile.h" #include "aiffproperties.h" -//////////////////////////////////////////////////////////////////////////////// -// nasty 80-bit float helpers -//////////////////////////////////////////////////////////////////////////////// - -#define UnsignedToFloat(u) (((double)((long)(u - 2147483647L - 1))) + 2147483648.0) - -static double ConvertFromIeeeExtended(const TagLib::uchar *bytes) -{ - double f; - int expon; - unsigned long hiMant, loMant; - - expon = ((bytes[0] & 0x7F) << 8) | (bytes[1] & 0xFF); - - hiMant = ((unsigned long)(bytes[2] & 0xFF) << 24) | - ((unsigned long)(bytes[3] & 0xFF) << 16) | - ((unsigned long)(bytes[4] & 0xFF) << 8) | - ((unsigned long)(bytes[5] & 0xFF)); - - loMant = ((unsigned long)(bytes[6] & 0xFF) << 24) | - ((unsigned long)(bytes[7] & 0xFF) << 16) | - ((unsigned long)(bytes[8] & 0xFF) << 8) | - ((unsigned long)(bytes[9] & 0xFF)); - - if (expon == 0 && hiMant == 0 && loMant == 0) - f = 0; - else { - if(expon == 0x7FFF) /* Infinity or NaN */ - f = HUGE_VAL; - else { - expon -= 16383; - f = ldexp(UnsignedToFloat(hiMant), expon -= 31); - f += ldexp(UnsignedToFloat(loMant), expon -= 32); - } - } - - if(bytes[0] & 0x80) - return -f; - else - return f; -} - using namespace TagLib; class RIFF::AIFF::Properties::PropertiesPrivate @@ -85,17 +38,18 @@ public: bitrate(0), sampleRate(0), channels(0), - sampleWidth(0), - sampleFrames(0) - { - - } + bitsPerSample(0), + sampleFrames(0) {} int length; int bitrate; int sampleRate; int channels; - int sampleWidth; + int bitsPerSample; + + ByteVector compressionType; + String compressionName; + uint sampleFrames; }; @@ -103,10 +57,18 @@ public: // public members //////////////////////////////////////////////////////////////////////////////// -RIFF::AIFF::Properties::Properties(const ByteVector &data, ReadStyle style) : AudioProperties(style) +RIFF::AIFF::Properties::Properties(const ByteVector &, ReadStyle style) : + AudioProperties(style), + d(new PropertiesPrivate()) { - d = new PropertiesPrivate; - read(data); + debug("RIFF::AIFF::Properties::Properties() - This constructor is no longer used."); +} + +RIFF::AIFF::Properties::Properties(File *file, ReadStyle style) : + AudioProperties(style), + d(new PropertiesPrivate()) +{ + read(file); } RIFF::AIFF::Properties::~Properties() @@ -115,6 +77,16 @@ RIFF::AIFF::Properties::~Properties() } int RIFF::AIFF::Properties::length() const +{ + return lengthInSeconds(); +} + +int RIFF::AIFF::Properties::lengthInSeconds() const +{ + return d->length / 1000; +} + +int RIFF::AIFF::Properties::lengthInMilliseconds() const { return d->length; } @@ -134,9 +106,14 @@ int RIFF::AIFF::Properties::channels() const return d->channels; } +int RIFF::AIFF::Properties::bitsPerSample() const +{ + return d->bitsPerSample; +} + int RIFF::AIFF::Properties::sampleWidth() const { - return d->sampleWidth; + return bitsPerSample(); } TagLib::uint RIFF::AIFF::Properties::sampleFrames() const @@ -144,17 +121,71 @@ TagLib::uint RIFF::AIFF::Properties::sampleFrames() const return d->sampleFrames; } +bool RIFF::AIFF::Properties::isAiffC() const +{ + return (!d->compressionType.isEmpty()); +} + +ByteVector RIFF::AIFF::Properties::compressionType() const +{ + return d->compressionType; +} + +String RIFF::AIFF::Properties::compressionName() const +{ + return d->compressionName; +} + //////////////////////////////////////////////////////////////////////////////// // private members //////////////////////////////////////////////////////////////////////////////// -void RIFF::AIFF::Properties::read(const ByteVector &data) +void RIFF::AIFF::Properties::read(File *file) { - d->channels = data.toShort(0U); - d->sampleFrames = data.toUInt(2U); - d->sampleWidth = data.toShort(6U); - double sampleRate = ConvertFromIeeeExtended(reinterpret_cast(data.data() + 8)); - d->sampleRate = (int)sampleRate; - d->bitrate = (int)((sampleRate * d->sampleWidth * d->channels) / 1000.0); - d->length = d->sampleRate > 0 ? d->sampleFrames / d->sampleRate : 0; + ByteVector data; + uint streamLength = 0; + for(uint i = 0; i < file->chunkCount(); i++) { + const ByteVector name = file->chunkName(i); + if(name == "COMM") { + if(data.isEmpty()) + data = file->chunkData(i); + else + debug("RIFF::AIFF::Properties::read() - Duplicate 'COMM' chunk found."); + } + else if(name == "SSND") { + if(streamLength == 0) + streamLength = file->chunkDataSize(i) + file->chunkPadding(i); + else + debug("RIFF::AIFF::Properties::read() - Duplicate 'SSND' chunk found."); + } + } + + if(data.size() < 18) { + debug("RIFF::AIFF::Properties::read() - 'COMM' chunk not found or too short."); + return; + } + + if(streamLength == 0) { + debug("RIFF::AIFF::Properties::read() - 'SSND' chunk not found."); + return; + } + + d->channels = data.toShort(0U); + d->sampleFrames = data.toUInt(2U); + d->bitsPerSample = data.toShort(6U); + + const long double sampleRate = data.toFloat80BE(8); + if(sampleRate >= 1.0) + d->sampleRate = static_cast(sampleRate + 0.5); + + if(d->sampleFrames > 0 && d->sampleRate > 0) { + const double length = d->sampleFrames * 1000.0 / d->sampleRate; + d->length = static_cast(length + 0.5); + d->bitrate = static_cast(streamLength * 8.0 / length + 0.5); + } + + if(data.size() >= 23) { + d->compressionType = data.mid(18, 4); + d->compressionName = String(data.mid(23, static_cast(data[22])), String::Latin1); + } } diff --git a/3rdparty/taglib/riff/aiff/aiffproperties.h b/3rdparty/taglib/riff/aiff/aiffproperties.h index 68e90b792..1d92ac874 100644 --- a/3rdparty/taglib/riff/aiff/aiffproperties.h +++ b/3rdparty/taglib/riff/aiff/aiffproperties.h @@ -49,29 +49,112 @@ namespace TagLib { /*! * Create an instance of AIFF::Properties with the data read from the * ByteVector \a data. + * + * \deprecated */ Properties(const ByteVector &data, ReadStyle style); + /*! + * Create an instance of AIFF::Properties with the data read from the + * AIFF::File \a file. + */ + Properties(File *file, ReadStyle style); + /*! * Destroys this AIFF::Properties instance. */ virtual ~Properties(); - // Reimplementations. - + /*! + * Returns the length of the file in seconds. The length is rounded down to + * the nearest whole second. + * + * \note This method is just an alias of lengthInSeconds(). + * + * \deprecated + */ virtual int length() const; + + /*! + * Returns the length of the file in seconds. The length is rounded down to + * the nearest whole second. + * + * \see lengthInMilliseconds() + */ + // BIC: make virtual + int lengthInSeconds() const; + + /*! + * Returns the length of the file in milliseconds. + * + * \see lengthInSeconds() + */ + // BIC: make virtual + int lengthInMilliseconds() const; + + /*! + * Returns the average bit rate of the file in kb/s. + */ virtual int bitrate() const; + + /*! + * Returns the sample rate in Hz. + */ virtual int sampleRate() const; + + /*! + * Returns the number of audio channels. + */ virtual int channels() const; + /*! + * Returns the number of bits per audio sample. + */ + int bitsPerSample() const; + + /*! + * Returns the number of bits per audio sample. + * + * \note This method is just an alias of bitsPerSample(). + * + * \deprecated + */ int sampleWidth() const; + + /*! + * Returns the number of sample frames + */ uint sampleFrames() const; + /*! + * Returns true if the file is in AIFF-C format, false if AIFF format. + */ + bool isAiffC() const; + + /*! + * Returns the compression type of the AIFF-C file. For example, "NONE" for + * not compressed, "ACE2" for ACE 2-to-1. + * + * If the file is in AIFF format, always returns an empty vector. + * + * \see isAiffC() + */ + ByteVector compressionType() const; + + /*! + * Returns the concrete compression name of the AIFF-C file. + * + * If the file is in AIFF format, always returns an empty string. + * + * \see isAiffC() + */ + String compressionName() const; + private: Properties(const Properties &); Properties &operator=(const Properties &); - void read(const ByteVector &data); + void read(File *file); class PropertiesPrivate; PropertiesPrivate *d; diff --git a/3rdparty/taglib/riff/rifffile.cpp b/3rdparty/taglib/riff/rifffile.cpp index a39fce614..efd4f6426 100644 --- a/3rdparty/taglib/riff/rifffile.cpp +++ b/3rdparty/taglib/riff/rifffile.cpp @@ -127,15 +127,7 @@ ByteVector RIFF::File::chunkData(uint i) if(i >= chunkCount()) return ByteVector::null; - // Offset for the first subchunk's data - - long begin = 12 + 8; - - for(uint it = 0; it < i; it++) - begin += 8 + d->chunks[it].size + d->chunks[it].padding; - - seek(begin); - + seek(d->chunks[i].offset); return readBlock(d->chunks[i].size); } @@ -219,22 +211,24 @@ void RIFF::File::removeChunk(uint i) { if(i >= d->chunks.size()) return; - - removeBlock(d->chunks[i].offset - 8, d->chunks[i].size + 8); - d->chunks.erase(d->chunks.begin() + i); + + std::vector::iterator it = d->chunks.begin(); + std::advance(it, i); + + const uint removeSize = it->size + it->padding + 8; + removeBlock(it->offset - 8, removeSize); + it = d->chunks.erase(it); + + for(; it != d->chunks.end(); ++it) + it->offset -= removeSize; } void RIFF::File::removeChunk(const ByteVector &name) { - std::vector newChunks; - for(size_t i = 0; i < d->chunks.size(); ++i) { + for(int i = d->chunks.size() - 1; i >= 0; --i) { if(d->chunks[i].name == name) - removeBlock(d->chunks[i].offset - 8, d->chunks[i].size + 8); - else - newChunks.push_back(d->chunks[i]); + removeChunk(i); } - - d->chunks.swap(newChunks); } //////////////////////////////////////////////////////////////////////////////// diff --git a/3rdparty/taglib/riff/rifffile.h b/3rdparty/taglib/riff/rifffile.h index 6b8fe197f..11d2b71a0 100644 --- a/3rdparty/taglib/riff/rifffile.h +++ b/3rdparty/taglib/riff/rifffile.h @@ -96,7 +96,7 @@ namespace TagLib { ByteVector chunkData(uint i); /*! - * Sets the data for the the specified chunk to \a data. + * Sets the data for the specified chunk to \a data. * * \warning This will update the file immediately. */ @@ -116,9 +116,9 @@ namespace TagLib { * given name already exists it will be overwritten, otherwise it will be * created after the existing chunks. * - * \note If \a alwaysCreate is true, a new chunk is created regardless of - * whether or not the chunk \a name exists. It should only be used for - * "LIST" chunks. + * \note If \a alwaysCreate is true, a new chunk is created regardless of + * whether or not the chunk \a name exists. It should only be used for + * "LIST" chunks. * * \warning This will update the file immediately. */ diff --git a/3rdparty/taglib/riff/wav/infotag.cpp b/3rdparty/taglib/riff/wav/infotag.cpp index 09edfa53c..050ff37ca 100644 --- a/3rdparty/taglib/riff/wav/infotag.cpp +++ b/3rdparty/taglib/riff/wav/infotag.cpp @@ -1,6 +1,6 @@ /*************************************************************************** copyright : (C) 2012 by Tsuda Kageyu - email : wheeler@kde.org + email : tsuda.kageyu@gmail.com ***************************************************************************/ /*************************************************************************** @@ -49,7 +49,7 @@ namespace { class RIFF::Info::Tag::TagPrivate { public: - TagPrivate() + TagPrivate() {} FieldListMap fieldListMap; @@ -86,14 +86,14 @@ ByteVector RIFF::Info::StringHandler::render(const String &s) const static const StringHandler defaultStringHandler; const RIFF::Info::StringHandler *RIFF::Info::Tag::TagPrivate::stringHandler = &defaultStringHandler; -RIFF::Info::Tag::Tag(const ByteVector &data) +RIFF::Info::Tag::Tag(const ByteVector &data) : TagLib::Tag() , d(new TagPrivate()) { parse(data); } -RIFF::Info::Tag::Tag() +RIFF::Info::Tag::Tag() : TagLib::Tag() , d(new TagPrivate()) { @@ -229,7 +229,7 @@ ByteVector RIFF::Info::Tag::render() const data.append(it->first); data.append(ByteVector::fromUInt(text.size() + 1, false)); data.append(text); - + do { data.append('\0'); } while(data.size() & 1); @@ -258,9 +258,15 @@ void RIFF::Info::Tag::parse(const ByteVector &data) uint p = 4; while(p < data.size()) { const uint size = data.toUInt(p + 4, false); - d->fieldListMap[data.mid(p, 4)] = TagPrivate::stringHandler->parse(data.mid(p + 8, size)); + if(size > data.size() - p - 8) + break; + + const ByteVector id = data.mid(p, 4); + if(isValidChunkID(id)) { + const String text = TagPrivate::stringHandler->parse(data.mid(p + 8, size)); + d->fieldListMap[id] = text; + } p += ((size + 1) & ~1) + 8; } } - diff --git a/3rdparty/taglib/riff/wav/infotag.h b/3rdparty/taglib/riff/wav/infotag.h index 4007ae661..440e7b934 100644 --- a/3rdparty/taglib/riff/wav/infotag.h +++ b/3rdparty/taglib/riff/wav/infotag.h @@ -37,7 +37,7 @@ namespace TagLib { class File; - //! A RIFF Info tag implementation. + //! A RIFF INFO tag implementation. namespace RIFF { namespace Info { @@ -46,12 +46,12 @@ namespace TagLib { //! A abstraction for the string to data encoding in Info tags. /*! - * RIFF Info tag has no clear definitions about character encodings. + * RIFF INFO tag has no clear definitions about character encodings. * In practice, local encoding of each system is largely used and UTF-8 is * popular too. * - * Here is an option to read and write tags in your preferrd encoding - * by subclassing this class, reimplementing parse() and render() and setting + * Here is an option to read and write tags in your preferred encoding + * by subclassing this class, reimplementing parse() and render() and setting * your reimplementation as the default with Info::Tag::setStringHandler(). * * \see ID3v1::Tag::setStringHandler() @@ -71,7 +71,7 @@ namespace TagLib { /*! * Encode a ByteVector with the data from \a s. The default implementation - * assumes that \a s is an UTF-8 string. + * assumes that \a s is an UTF-8 string. */ virtual ByteVector render(const String &s) const; }; @@ -79,22 +79,22 @@ namespace TagLib { //! The main class in the ID3v2 implementation /*! - * This is the main class in the INFO tag implementation. RIFF INFO tag is a - * metadata format found in WAV audio and AVI video files. Though it is a part - * of Microsoft/IBM's RIFF specification, the author could not find the official - * documents about it. So, this implementation is referring to unofficial documents + * This is the main class in the INFO tag implementation. RIFF INFO tag is a + * metadata format found in WAV audio and AVI video files. Though it is a part + * of Microsoft/IBM's RIFF specification, the author could not find the official + * documents about it. So, this implementation is referring to unofficial documents * online and some applications' behaviors especially Windows Explorer. */ class TAGLIB_EXPORT Tag : public TagLib::Tag { public: /*! - * Constructs an empty Info tag. + * Constructs an empty INFO tag. */ Tag(); /*! - * Constructs an Info tag read from \a data which is contents of "LIST" chunk. + * Constructs an INFO tag read from \a data which is contents of "LIST" chunk. */ Tag(const ByteVector &data); @@ -121,7 +121,7 @@ namespace TagLib { virtual bool isEmpty() const; /*! - * Returns a copy of the internal fields of the tag. The returned map directly + * Returns a copy of the internal fields of the tag. The returned map directly * reflects the contents of the "INFO" chunk. * * \note Modifying this map does not affect the tag's internal data. @@ -136,13 +136,13 @@ namespace TagLib { * Gets the value of the field with the ID \a id. */ String fieldText(const ByteVector &id) const; - + /* * Sets the value of the field with the ID \a id to \a s. * If the field does not exist, it is created. * If \s is empty, the field is removed. * - * \note fieldId must be four-byte long pure ASCII string. This function + * \note fieldId must be four-byte long pure ASCII string. This function * performs nothing if fieldId is invalid. */ void setFieldText(const ByteVector &id, const String &s); @@ -155,7 +155,7 @@ namespace TagLib { /*! * Render the tag back to binary data, suitable to be written to disk. * - * \note Returns empty ByteVector is the tag contains no fields. + * \note Returns empty ByteVector is the tag contains no fields. */ ByteVector render() const; @@ -171,7 +171,7 @@ namespace TagLib { * \see StringHandler */ static void setStringHandler(const StringHandler *handler); - + protected: /*! * Pareses the body of the tag in \a data. diff --git a/3rdparty/taglib/riff/wav/wavfile.cpp b/3rdparty/taglib/riff/wav/wavfile.cpp index abecea351..e7b8dc8a8 100644 --- a/3rdparty/taglib/riff/wav/wavfile.cpp +++ b/3rdparty/taglib/riff/wav/wavfile.cpp @@ -57,7 +57,7 @@ public: } Properties *properties; - + ByteVector tagChunkID; TagUnion tag; @@ -70,20 +70,20 @@ public: // public members //////////////////////////////////////////////////////////////////////////////// -RIFF::WAV::File::File(FileName file, bool readProperties, - Properties::ReadStyle propertiesStyle) : RIFF::File(file, LittleEndian) +RIFF::WAV::File::File(FileName file, bool readProperties, Properties::ReadStyle) : + RIFF::File(file, LittleEndian), + d(new FilePrivate()) { - d = new FilePrivate; if(isOpen()) - read(readProperties, propertiesStyle); + read(readProperties); } -RIFF::WAV::File::File(IOStream *stream, bool readProperties, - Properties::ReadStyle propertiesStyle) : RIFF::File(stream, LittleEndian) +RIFF::WAV::File::File(IOStream *stream, bool readProperties, Properties::ReadStyle) : + RIFF::File(stream, LittleEndian), + d(new FilePrivate()) { - d = new FilePrivate; if(isOpen()) - read(readProperties, propertiesStyle); + read(readProperties); } RIFF::WAV::File::~File() @@ -146,23 +146,28 @@ bool RIFF::WAV::File::save(TagTypes tags, bool stripOthers, int id3v2Version) if(stripOthers) strip(static_cast(AllTags & ~tags)); - ID3v2::Tag *id3v2tag = d->tag.access(ID3v2Index, false); - if(!id3v2tag->isEmpty()) { - if(tags & ID3v2) { + const ID3v2::Tag *id3v2tag = d->tag.access(ID3v2Index, false); + if(tags & ID3v2) { + if(d->hasID3v2) { + removeChunk(d->tagChunkID); + d->hasID3v2 = false; + } + + if(!id3v2tag->isEmpty()) { setChunkData(d->tagChunkID, id3v2tag->render(id3v2Version)); d->hasID3v2 = true; } } - Info::Tag *infotag = d->tag.access(InfoIndex, false); - if(!infotag->isEmpty()) { - if(tags & Info) { - int chunkId = findInfoTagChunk(); - if(chunkId != -1) - setChunkData(chunkId, infotag->render()); - else - setChunkData("LIST", infotag->render(), true); + const Info::Tag *infotag = d->tag.access(InfoIndex, false); + if(tags & Info) { + if(d->hasInfo) { + removeChunk(findInfoTagChunk()); + d->hasInfo = false; + } + if(!infotag->isEmpty()) { + setChunkData("LIST", infotag->render(), true); d->hasInfo = true; } } @@ -184,61 +189,67 @@ bool RIFF::WAV::File::hasInfoTag() const // private members //////////////////////////////////////////////////////////////////////////////// -void RIFF::WAV::File::read(bool readProperties, Properties::ReadStyle propertiesStyle) +void RIFF::WAV::File::read(bool readProperties) { - ByteVector formatData; - uint streamLength = 0; - for(uint i = 0; i < chunkCount(); i++) { - String name = chunkName(i); + for(uint i = 0; i < chunkCount(); ++i) { + const ByteVector name = chunkName(i); if(name == "ID3 " || name == "id3 ") { - d->tagChunkID = chunkName(i); - d->tag.set(ID3v2Index, new ID3v2::Tag(this, chunkOffset(i))); - d->hasID3v2 = true; + if(!d->tag[ID3v2Index]) { + d->tagChunkID = name; + d->tag.set(ID3v2Index, new ID3v2::Tag(this, chunkOffset(i))); + d->hasID3v2 = true; + } + else { + debug("RIFF::WAV::File::read() - Duplicate ID3v2 tag found."); + } } - else if(name == "fmt " && readProperties) - formatData = chunkData(i); - else if(name == "data" && readProperties) - streamLength = chunkDataSize(i); else if(name == "LIST") { - ByteVector data = chunkData(i); - ByteVector type = data.mid(0, 4); - - if(type == "INFO") { - d->tag.set(InfoIndex, new RIFF::Info::Tag(data)); - d->hasInfo = true; + const ByteVector data = chunkData(i); + if(data.startsWith("INFO")) { + if(!d->tag[InfoIndex]) { + d->tag.set(InfoIndex, new RIFF::Info::Tag(data)); + d->hasInfo = true; + } + else { + debug("RIFF::WAV::File::read() - Duplicate INFO tag found."); + } } } } - if (!d->tag[ID3v2Index]) - d->tag.set(ID3v2Index, new ID3v2::Tag); + if(!d->tag[ID3v2Index]) + d->tag.set(ID3v2Index, new ID3v2::Tag()); - if (!d->tag[InfoIndex]) - d->tag.set(InfoIndex, new RIFF::Info::Tag); + if(!d->tag[InfoIndex]) + d->tag.set(InfoIndex, new RIFF::Info::Tag()); - if(!formatData.isEmpty()) - d->properties = new Properties(formatData, streamLength, propertiesStyle); + if(readProperties) + d->properties = new Properties(this, Properties::Average); } void RIFF::WAV::File::strip(TagTypes tags) { - if(tags & ID3v2) + if(tags & ID3v2) { removeChunk(d->tagChunkID); + d->hasID3v2 = false; + } if(tags & Info){ TagLib::uint chunkId = findInfoTagChunk(); - if(chunkId != TagLib::uint(-1)) + if(chunkId != TagLib::uint(-1)) { removeChunk(chunkId); + d->hasInfo = false; + } } } TagLib::uint RIFF::WAV::File::findInfoTagChunk() { for(uint i = 0; i < chunkCount(); ++i) { - if(chunkName(i) == "LIST" && chunkData(i).mid(0, 4) == "INFO") { + if(chunkName(i) == "LIST" && chunkData(i).startsWith("INFO")) { return i; } } - + return TagLib::uint(-1); } diff --git a/3rdparty/taglib/riff/wav/wavfile.h b/3rdparty/taglib/riff/wav/wavfile.h index 2f83332a2..129d9537a 100644 --- a/3rdparty/taglib/riff/wav/wavfile.h +++ b/3rdparty/taglib/riff/wav/wavfile.h @@ -63,7 +63,7 @@ namespace TagLib { NoTags = 0x0000, //! Matches ID3v2 tags. ID3v2 = 0x0001, - //! Matches Info tags. + //! Matches INFO tags. Info = 0x0002, //! Matches all tag types. AllTags = 0xffff @@ -97,8 +97,8 @@ namespace TagLib { /*! * Returns the ID3v2 Tag for this file. - * - * \note This method does not return all the tags for this file for + * + * \note This method does not return all the tags for this file for * backward compatibility. Will be fixed in TagLib 2.0. */ ID3v2::Tag *tag() const; @@ -106,8 +106,8 @@ namespace TagLib { /*! * Returns the ID3v2 Tag for this file. * - * \note This always returns a valid pointer regardless of whether or not - * the file on disk has an ID3v2 tag. Use hasID3v2Tag() to check if the + * \note This always returns a valid pointer regardless of whether or not + * the file on disk has an ID3v2 tag. Use hasID3v2Tag() to check if the * file on disk actually has an ID3v2 tag. * * \see hasID3v2Tag() @@ -117,8 +117,8 @@ namespace TagLib { /*! * Returns the RIFF INFO Tag for this file. * - * \note This always returns a valid pointer regardless of whether or not - * the file on disk has a RIFF INFO tag. Use hasInfoTag() to check if the + * \note This always returns a valid pointer regardless of whether or not + * the file on disk has a RIFF INFO tag. Use hasInfoTag() to check if the * file on disk actually has a RIFF INFO tag. * * \see hasInfoTag() @@ -151,7 +151,7 @@ namespace TagLib { virtual bool save(); bool save(TagTypes tags, bool stripOthers = true, int id3v2Version = 4); - + /*! * Returns whether or not the file on disk actually has an ID3v2 tag. * @@ -170,7 +170,7 @@ namespace TagLib { File(const File &); File &operator=(const File &); - void read(bool readProperties, Properties::ReadStyle propertiesStyle); + void read(bool readProperties); void strip(TagTypes tags); @@ -179,6 +179,8 @@ namespace TagLib { */ uint findInfoTagChunk(); + friend class Properties; + class FilePrivate; FilePrivate *d; }; diff --git a/3rdparty/taglib/riff/wav/wavproperties.cpp b/3rdparty/taglib/riff/wav/wavproperties.cpp index 8062df5f7..39f0dddca 100644 --- a/3rdparty/taglib/riff/wav/wavproperties.cpp +++ b/3rdparty/taglib/riff/wav/wavproperties.cpp @@ -23,55 +23,66 @@ * http://www.mozilla.org/MPL/ * ***************************************************************************/ +#include +#include "wavfile.h" #include "wavproperties.h" -#include -#include -#include -#include - using namespace TagLib; +namespace +{ + // Quoted from RFC 2361. + enum WaveFormat + { + FORMAT_UNKNOWN = 0x0000, + FORMAT_PCM = 0x0001 + }; +} + class RIFF::WAV::Properties::PropertiesPrivate { public: - PropertiesPrivate(uint streamLength = 0) : + PropertiesPrivate() : format(0), length(0), bitrate(0), sampleRate(0), channels(0), - sampleWidth(0), - sampleFrames(0), - streamLength(streamLength) - { + bitsPerSample(0), + sampleFrames(0) {} - } - - short format; + int format; int length; int bitrate; int sampleRate; int channels; - int sampleWidth; + int bitsPerSample; uint sampleFrames; - uint streamLength; }; //////////////////////////////////////////////////////////////////////////////// // public members //////////////////////////////////////////////////////////////////////////////// -RIFF::WAV::Properties::Properties(const ByteVector &data, ReadStyle style) : AudioProperties(style) +RIFF::WAV::Properties::Properties(const ByteVector & /*data*/, ReadStyle style) : + AudioProperties(style), + d(new PropertiesPrivate()) { - d = new PropertiesPrivate(); - read(data); + debug("RIFF::WAV::Properties::Properties() -- This constructor is no longer used."); } -RIFF::WAV::Properties::Properties(const ByteVector &data, uint streamLength, ReadStyle style) : AudioProperties(style) +RIFF::WAV::Properties::Properties(const ByteVector & /*data*/, uint /*streamLength*/, ReadStyle style) : + AudioProperties(style), + d(new PropertiesPrivate()) { - d = new PropertiesPrivate(streamLength); - read(data); + debug("RIFF::WAV::Properties::Properties() -- This constructor is no longer used."); +} + +TagLib::RIFF::WAV::Properties::Properties(File *file, ReadStyle style) : + AudioProperties(style), + d(new PropertiesPrivate()) +{ + read(file); } RIFF::WAV::Properties::~Properties() @@ -80,6 +91,16 @@ RIFF::WAV::Properties::~Properties() } int RIFF::WAV::Properties::length() const +{ + return lengthInSeconds(); +} + +int RIFF::WAV::Properties::lengthInSeconds() const +{ + return d->length / 1000; +} + +int RIFF::WAV::Properties::lengthInMilliseconds() const { return d->length; } @@ -99,9 +120,14 @@ int RIFF::WAV::Properties::channels() const return d->channels; } +int RIFF::WAV::Properties::bitsPerSample() const +{ + return d->bitsPerSample; +} + int RIFF::WAV::Properties::sampleWidth() const { - return d->sampleWidth; + return bitsPerSample(); } TagLib::uint RIFF::WAV::Properties::sampleFrames() const @@ -109,21 +135,78 @@ TagLib::uint RIFF::WAV::Properties::sampleFrames() const return d->sampleFrames; } +int RIFF::WAV::Properties::format() const +{ + return d->format; +} + //////////////////////////////////////////////////////////////////////////////// // private members //////////////////////////////////////////////////////////////////////////////// -void RIFF::WAV::Properties::read(const ByteVector &data) +void RIFF::WAV::Properties::read(File *file) { - d->format = data.toShort(0, false); - d->channels = data.toShort(2, false); - d->sampleRate = data.toUInt(4, false); - d->sampleWidth = data.toShort(14, false); + ByteVector data; + uint streamLength = 0; + uint totalSamples = 0; - const uint byteRate = data.toUInt(8, false); - d->bitrate = byteRate * 8 / 1000; + for(uint i = 0; i < file->chunkCount(); ++i) { + const ByteVector name = file->chunkName(i); + if(name == "fmt ") { + if(data.isEmpty()) + data = file->chunkData(i); + else + debug("RIFF::WAV::Properties::read() - Duplicate 'fmt ' chunk found."); + } + else if(name == "data") { + if(streamLength == 0) + streamLength = file->chunkDataSize(i) + file->chunkPadding(i); + else + debug("RIFF::WAV::Properties::read() - Duplicate 'data' chunk found."); + } + else if(name == "fact") { + if(totalSamples == 0) + totalSamples = file->chunkData(i).toUInt(0, false); + else + debug("RIFF::WAV::Properties::read() - Duplicate 'fact' chunk found."); + } + } - d->length = byteRate > 0 ? d->streamLength / byteRate : 0; - if(d->channels > 0 && d->sampleWidth > 0) - d->sampleFrames = d->streamLength / (d->channels * ((d->sampleWidth + 7) / 8)); + if(data.size() < 16) { + debug("RIFF::WAV::Properties::read() - 'fmt ' chunk not found or too short."); + return; + } + + if(streamLength == 0) { + debug("RIFF::WAV::Properties::read() - 'data' chunk not found."); + return; + } + + d->format = data.toShort(0, false); + if(d->format != FORMAT_PCM && totalSamples == 0) { + debug("RIFF::WAV::Properties::read() - Non-PCM format, but 'fact' chunk not found."); + return; + } + + d->channels = data.toShort(2, false); + d->sampleRate = data.toUInt(4, false); + d->bitsPerSample = data.toShort(14, false); + + if(totalSamples > 0) + d->sampleFrames = totalSamples; + else if(d->channels > 0 && d->bitsPerSample > 0) + d->sampleFrames = streamLength / (d->channels * ((d->bitsPerSample + 7) / 8)); + + if(d->sampleFrames > 0 && d->sampleRate > 0) { + const double length = d->sampleFrames * 1000.0 / d->sampleRate; + d->length = static_cast(length + 0.5); + d->bitrate = static_cast(streamLength * 8.0 / length + 0.5); + } + else { + const uint byteRate = data.toUInt(8, false); + if(byteRate > 0) { + d->length = static_cast(streamLength * 1000.0 / byteRate + 0.5); + d->bitrate = static_cast(byteRate * 8.0 / 1000.0 + 0.5); + } + } } diff --git a/3rdparty/taglib/riff/wav/wavproperties.h b/3rdparty/taglib/riff/wav/wavproperties.h index 2023f5392..1fd6a1369 100644 --- a/3rdparty/taglib/riff/wav/wavproperties.h +++ b/3rdparty/taglib/riff/wav/wavproperties.h @@ -52,35 +52,106 @@ namespace TagLib { /*! * Create an instance of WAV::Properties with the data read from the * ByteVector \a data. + * + * \deprecated */ Properties(const ByteVector &data, ReadStyle style); /*! * Create an instance of WAV::Properties with the data read from the * ByteVector \a data and the length calculated using \a streamLength. + * + * \deprecated */ Properties(const ByteVector &data, uint streamLength, ReadStyle style); + /*! + * Create an instance of WAV::Properties with the data read from the + * WAV::File \a file. + */ + Properties(File *file, ReadStyle style); + /*! * Destroys this WAV::Properties instance. */ virtual ~Properties(); - // Reimplementations. - + /*! + * Returns the length of the file in seconds. The length is rounded down to + * the nearest whole second. + * + * \note This method is just an alias of lengthInSeconds(). + * + * \deprecated + */ virtual int length() const; + + /*! + * Returns the length of the file in seconds. The length is rounded down to + * the nearest whole second. + * + * \see lengthInMilliseconds() + */ + // BIC: make virtual + int lengthInSeconds() const; + + /*! + * Returns the length of the file in milliseconds. + * + * \see lengthInSeconds() + */ + // BIC: make virtual + int lengthInMilliseconds() const; + + /*! + * Returns the average bit rate of the file in kb/s. + */ virtual int bitrate() const; + + /*! + * Returns the sample rate in Hz. + */ virtual int sampleRate() const; + + /*! + * Returns the number of audio channels. + */ virtual int channels() const; + /*! + * Returns the number of bits per audio sample. + */ + int bitsPerSample() const; + + /*! + * Returns the number of bits per audio sample. + * + * \note This method is just an alias of bitsPerSample(). + * + * \deprecated + */ int sampleWidth() const; + + /*! + * Returns the number of sample frames. + */ uint sampleFrames() const; + /*! + * Returns the format ID of the file. + * 0 for unknown, 1 for PCM, 2 for ADPCM, 3 for 32/64-bit IEEE754, and + * so forth. + * + * \note For further information, refer to the WAVE Form Registration + * Numbers in RFC 2361. + */ + int format() const; + private: Properties(const Properties &); Properties &operator=(const Properties &); - void read(const ByteVector &data); + void read(File *file); class PropertiesPrivate; PropertiesPrivate *d; diff --git a/3rdparty/taglib/s3m/s3mproperties.cpp b/3rdparty/taglib/s3m/s3mproperties.cpp index a1a010a39..6f93eec35 100644 --- a/3rdparty/taglib/s3m/s3mproperties.cpp +++ b/3rdparty/taglib/s3m/s3mproperties.cpp @@ -73,6 +73,16 @@ int S3M::Properties::length() const return 0; } +int S3M::Properties::lengthInSeconds() const +{ + return 0; +} + +int S3M::Properties::lengthInMilliseconds() const +{ + return 0; +} + int S3M::Properties::bitrate() const { return 0; diff --git a/3rdparty/taglib/s3m/s3mproperties.h b/3rdparty/taglib/s3m/s3mproperties.h index 6e9f1d293..4499627f7 100644 --- a/3rdparty/taglib/s3m/s3mproperties.h +++ b/3rdparty/taglib/s3m/s3mproperties.h @@ -44,10 +44,12 @@ namespace TagLib { Properties(AudioProperties::ReadStyle propertiesStyle); virtual ~Properties(); - int length() const; - int bitrate() const; - int sampleRate() const; - int channels() const; + int length() const; + int lengthInSeconds() const; + int lengthInMilliseconds() const; + int bitrate() const; + int sampleRate() const; + int channels() const; ushort lengthInPatterns() const; bool stereo() const; diff --git a/3rdparty/taglib/tag.cpp b/3rdparty/taglib/tag.cpp index 676340817..eb8fab7a3 100644 --- a/3rdparty/taglib/tag.cpp +++ b/3rdparty/taglib/tag.cpp @@ -137,11 +137,11 @@ PropertyMap Tag::setProperties(const PropertyMap &origProps) setTrack(0); } else - setYear(0); + setTrack(0); // for each tag that has been set above, remove the first entry in the corresponding // value list. The others will be returned as unsupported by this format. - for(StringList::Iterator it = oneValueSet.begin(); it != oneValueSet.end(); ++it) { + for(StringList::ConstIterator it = oneValueSet.begin(); it != oneValueSet.end(); ++it) { if(properties[*it].size() == 1) properties.erase(*it); else diff --git a/3rdparty/taglib/tag.h b/3rdparty/taglib/tag.h index 76c9a82af..e04567b79 100644 --- a/3rdparty/taglib/tag.h +++ b/3rdparty/taglib/tag.h @@ -48,7 +48,7 @@ namespace TagLib { public: /*! - * Detroys this Tag instance. + * Destroys this Tag instance. */ virtual ~Tag(); diff --git a/3rdparty/taglib/taglib_config.h.cmake b/3rdparty/taglib/taglib_config.h.cmake index 0f499e2c2..5f0ee6cfd 100644 --- a/3rdparty/taglib/taglib_config.h.cmake +++ b/3rdparty/taglib/taglib_config.h.cmake @@ -1,4 +1,6 @@ /* taglib_config.h. Generated by cmake from taglib_config.h.cmake */ +/* These values are no longer used. This file is present only for compatibility reasons. */ + #define TAGLIB_WITH_ASF 1 #define TAGLIB_WITH_MP4 1 diff --git a/3rdparty/taglib/toolkit/taglib.h b/3rdparty/taglib/toolkit/taglib.h index cbca1074a..ef7650ce5 100644 --- a/3rdparty/taglib/toolkit/taglib.h +++ b/3rdparty/taglib/toolkit/taglib.h @@ -29,8 +29,8 @@ #include "taglib_config.h" #define TAGLIB_MAJOR_VERSION 1 -#define TAGLIB_MINOR_VERSION 9 -#define TAGLIB_PATCH_VERSION 1 +#define TAGLIB_MINOR_VERSION 10 +#define TAGLIB_PATCH_VERSION 0 #if defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ > 1)) #define TAGLIB_IGNORE_MISSING_DESTRUCTOR _Pragma("GCC diagnostic ignored \"-Wnon-virtual-dtor\"") @@ -94,7 +94,7 @@ namespace TagLib { * - Full support for unicode and internationalized tags. * - Dual MPL and * LGPL licenses. - * - No external toolkit dependancies. + * - No external toolkit dependencies. * * \section why Why TagLib? * diff --git a/3rdparty/taglib/toolkit/tbytevector.cpp b/3rdparty/taglib/toolkit/tbytevector.cpp index b65824646..6262bec6e 100644 --- a/3rdparty/taglib/toolkit/tbytevector.cpp +++ b/3rdparty/taglib/toolkit/tbytevector.cpp @@ -29,8 +29,11 @@ #include #include +#include +#include #include #include +#include #include #include @@ -99,7 +102,7 @@ static const uint crcTable[256] = { }; /*! - * A templatized straightforward find that works with the types + * A templatized straightforward find that works with the types * std::vector::iterator and std::vector::reverse_iterator. */ template @@ -108,7 +111,7 @@ int findChar( char c, uint offset, int byteAlign) { const size_t dataSize = dataEnd - dataBegin; - if(dataSize == 0 || offset > dataSize - 1) + if(offset + 1 > dataSize) return -1; // n % 0 is invalid @@ -125,7 +128,7 @@ int findChar( } /*! - * A templatized KMP find that works with the types + * A templatized KMP find that works with the types * std::vector::iterator and std::vector::reverse_iterator. */ template @@ -136,7 +139,7 @@ int findVector( { const size_t dataSize = dataEnd - dataBegin; const size_t patternSize = patternEnd - patternBegin; - if(patternSize > dataSize || offset > dataSize - 1) + if(patternSize == 0 || offset + patternSize > dataSize) return -1; // n % 0 is invalid @@ -158,15 +161,12 @@ int findVector( lastOccurrence[static_cast(*(patternBegin + i))] = patternSize - i - 1; TIterator it = dataBegin + patternSize - 1 + offset; - while(true) - { + while(true) { TIterator itBuffer = it; TIterator itPattern = patternBegin + patternSize - 1; - while(*itBuffer == *itPattern) - { - if(itPattern == patternBegin) - { + while(*itBuffer == *itPattern) { + if(itPattern == patternBegin) { if((itBuffer - dataBegin - offset) % byteAlign == 0) return (itBuffer - dataBegin); else @@ -209,10 +209,10 @@ T toNumber(const ByteVector &v, size_t offset, size_t length, bool mostSignifica template T toNumber(const ByteVector &v, size_t offset, bool mostSignificantByteFirst) { - static const bool isBigEndian = (Utils::SystemByteOrder == Utils::BigEndian); + const bool isBigEndian = (Utils::systemByteOrder() == Utils::BigEndian); const bool swap = (mostSignificantByteFirst != isBigEndian); - if(offset + sizeof(T) > v.size()) + if(offset + sizeof(T) > v.size()) return toNumber(v, offset, v.size() - offset, mostSignificantByteFirst); // Uses memcpy instead of reinterpret_cast to avoid an alignment exception. @@ -228,7 +228,7 @@ T toNumber(const ByteVector &v, size_t offset, bool mostSignificantByteFirst) template ByteVector fromNumber(T value, bool mostSignificantByteFirst) { - static const bool isBigEndian = (Utils::SystemByteOrder == Utils::BigEndian); + const bool isBigEndian = (Utils::systemByteOrder() == Utils::BigEndian); const bool swap = (mostSignificantByteFirst != isBigEndian); if(swap) @@ -237,6 +237,95 @@ ByteVector fromNumber(T value, bool mostSignificantByteFirst) return ByteVector(reinterpret_cast(&value), sizeof(T)); } +template +TFloat toFloat(const ByteVector &v, size_t offset) +{ + if(offset > v.size() - sizeof(TInt)) { + debug("toFloat() - offset is out of range. Returning 0."); + return 0.0; + } + + union { + TInt i; + TFloat f; + } tmp; + ::memcpy(&tmp, v.data() + offset, sizeof(TInt)); + + if(ENDIAN != Utils::floatByteOrder()) + tmp.i = Utils::byteSwap(tmp.i); + + return tmp.f; +} + +template +ByteVector fromFloat(TFloat value) +{ + union { + TInt i; + TFloat f; + } tmp; + tmp.f = value; + + if(ENDIAN != Utils::floatByteOrder()) + tmp.i = Utils::byteSwap(tmp.i); + + return ByteVector(reinterpret_cast(&tmp), sizeof(TInt)); +} + +template +long double toFloat80(const ByteVector &v, size_t offset) +{ + if(offset > v.size() - 10) { + debug("toFloat80() - offset is out of range. Returning 0."); + return 0.0; + } + + uchar bytes[10]; + ::memcpy(bytes, v.data() + offset, 10); + + if(ENDIAN == Utils::LittleEndian) { + std::swap(bytes[0], bytes[9]); + std::swap(bytes[1], bytes[8]); + std::swap(bytes[2], bytes[7]); + std::swap(bytes[3], bytes[6]); + std::swap(bytes[4], bytes[5]); + } + + // 1-bit sign + const bool negative = ((bytes[0] & 0x80) != 0); + + // 15-bit exponent + const int exponent = ((bytes[0] & 0x7F) << 8) | bytes[1]; + + // 64-bit fraction. Leading 1 is explicit. + const ulonglong fraction + = (static_cast(bytes[2]) << 56) + | (static_cast(bytes[3]) << 48) + | (static_cast(bytes[4]) << 40) + | (static_cast(bytes[5]) << 32) + | (static_cast(bytes[6]) << 24) + | (static_cast(bytes[7]) << 16) + | (static_cast(bytes[8]) << 8) + | (static_cast(bytes[9])); + + long double val; + if(exponent == 0 && fraction == 0) + val = 0; + else { + if(exponent == 0x7FFF) { + debug("toFloat80() - can't handle the infinity or NaN. Returning 0."); + return 0.0; + } + else + val = ::ldexp(static_cast(fraction), exponent - 16383 - 63); + } + + if(negative) + return -val; + else + return val; +} + class DataPrivate : public RefCounter { public: @@ -244,8 +333,8 @@ public: { } - DataPrivate(const std::vector &v, uint offset, uint length) - : data(v.begin() + offset, v.begin() + offset + length) + DataPrivate(const std::vector &v, uint offset, uint length) + : data(v.begin() + offset, v.begin() + offset + length) { } @@ -255,8 +344,8 @@ public: { } - DataPrivate(uint len, char c) - : data(len, c) + DataPrivate(uint len, char c) + : data(len, c) { } @@ -266,11 +355,11 @@ public: class ByteVector::ByteVectorPrivate : public RefCounter { public: - ByteVectorPrivate() + ByteVectorPrivate() : RefCounter() , data(new DataPrivate()) , offset(0) - , length(0) + , length(0) { } @@ -291,7 +380,7 @@ public: { } - ByteVectorPrivate(uint l, char c) + ByteVectorPrivate(uint l, char c) : RefCounter() , data(new DataPrivate(l, c)) , offset(0) @@ -299,14 +388,14 @@ public: { } - ByteVectorPrivate(const char *s, uint l) + ByteVectorPrivate(const char *s, uint l) : RefCounter() , data(new DataPrivate(s, s + l)) , offset(0) , length(l) { } - + void detach() { if(data->count() > 1) { @@ -370,6 +459,26 @@ ByteVector ByteVector::fromLongLong(long long value, bool mostSignificantByteFir return fromNumber(value, mostSignificantByteFirst); } +ByteVector ByteVector::fromFloat32LE(float value) +{ + return fromFloat(value); +} + +ByteVector ByteVector::fromFloat32BE(float value) +{ + return fromFloat(value); +} + +ByteVector ByteVector::fromFloat64LE(double value) +{ + return fromFloat(value); +} + +ByteVector ByteVector::fromFloat64BE(double value) +{ + return fromFloat(value); +} + //////////////////////////////////////////////////////////////////////////////// // public members //////////////////////////////////////////////////////////////////////////////// @@ -384,7 +493,7 @@ ByteVector::ByteVector(uint size, char value) { } -ByteVector::ByteVector(const ByteVector &v) +ByteVector::ByteVector(const ByteVector &v) : d(v.d) { d->ref(); @@ -487,9 +596,9 @@ bool ByteVector::containsAt(const ByteVector &pattern, uint offset, uint pattern // do some sanity checking -- all of these things are needed for the search to be valid const uint compareLength = patternLength - patternOffset; - if(offset + compareLength > size() || patternOffset >= pattern.size() || patternLength == 0) + if(offset + compareLength > size() || patternOffset >= pattern.size() || patternLength == 0) return false; - + return (::memcmp(data() + offset, pattern.data() + patternOffset, compareLength) == 0); } @@ -508,63 +617,41 @@ ByteVector &ByteVector::replace(const ByteVector &pattern, const ByteVector &wit if(pattern.size() == 0 || pattern.size() > size()) return *this; - const uint withSize = with.size(); - const uint patternSize = pattern.size(); - int offset = 0; + const size_t withSize = with.size(); + const size_t patternSize = pattern.size(); + const ptrdiff_t diff = withSize - patternSize; + + size_t offset = 0; + while (true) + { + offset = find(pattern, offset); + if(offset == static_cast(-1)) // Use npos in taglib2. + break; - if(withSize == patternSize) { - // I think this case might be common enough to optimize it detach(); - offset = find(pattern); - while(offset >= 0) { - ::memcpy(data() + offset, with.data(), withSize); - offset = find(pattern, offset + withSize); - } - return *this; - } - // calculate new size: - uint newSize = 0; - for(;;) { - int next = find(pattern, offset); - if(next < 0) { - if(offset == 0) - // pattern not found, do nothing: - return *this; - newSize += size() - offset; + if(diff < 0) { + ::memmove( + data() + offset + withSize, + data() + offset + patternSize, + size() - offset - patternSize); + resize(size() + diff); + } + else if(diff > 0) { + resize(size() + diff); + ::memmove( + data() + offset + withSize, + data() + offset + patternSize, + size() - diff - offset - patternSize); + } + + ::memcpy(data() + offset, with.data(), with.size()); + + offset += withSize; + if(offset > size() - patternSize) break; - } - newSize += (next - offset) + withSize; - offset = next + patternSize; } - // new private data of appropriate size: - ByteVectorPrivate *newData = new ByteVectorPrivate(newSize, 0); - char *target = DATA(newData); - const char *source = data(); - - // copy modified data into new private data: - offset = 0; - for(;;) { - int next = find(pattern, offset); - if(next < 0) { - ::memcpy(target, source + offset, size() - offset); - break; - } - int chunkSize = next - offset; - ::memcpy(target, source + offset, chunkSize); - target += chunkSize; - ::memcpy(target, with.data(), withSize); - target += withSize; - offset += chunkSize + patternSize; - } - - // replace private data: - if(d->deref()) - delete d; - - d = newData; - return *this; } @@ -615,7 +702,14 @@ ByteVector &ByteVector::resize(uint size, char padding) { if(size != d->length) { detach(); + + // Remove the excessive length of the internal buffer first to pad correctly. + // This doesn't reallocate the buffer, since std::vector::resize() doesn't + // reallocate the buffer when shrinking. + + d->data->data.resize(d->offset + d->length); d->data->data.resize(d->offset + size, padding); + d->length = size; } @@ -624,6 +718,7 @@ ByteVector &ByteVector::resize(uint size, char padding) ByteVector::Iterator ByteVector::begin() { + detach(); return d->data->data.begin() + d->offset; } @@ -634,6 +729,7 @@ ByteVector::ConstIterator ByteVector::begin() const ByteVector::Iterator ByteVector::end() { + detach(); return d->data->data.begin() + d->offset + d->length; } @@ -644,26 +740,24 @@ ByteVector::ConstIterator ByteVector::end() const ByteVector::ReverseIterator ByteVector::rbegin() { - std::vector &v = d->data->data; - return v.rbegin() + (v.size() - (d->offset + d->length)); + detach(); + return d->data->data.rbegin() + (d->data->data.size() - (d->offset + d->length)); } ByteVector::ConstReverseIterator ByteVector::rbegin() const { - std::vector &v = d->data->data; - return v.rbegin() + (v.size() - (d->offset + d->length)); + return d->data->data.rbegin() + (d->data->data.size() - (d->offset + d->length)); } ByteVector::ReverseIterator ByteVector::rend() { - std::vector &v = d->data->data; - return v.rbegin() + (v.size() - d->offset); + detach(); + return d->data->data.rbegin() + (d->data->data.size() - d->offset); } ByteVector::ConstReverseIterator ByteVector::rend() const { - std::vector &v = d->data->data; - return v.rbegin() + (v.size() - d->offset); + return d->data->data.rbegin() + (d->data->data.size() - d->offset); } bool ByteVector::isNull() const @@ -729,6 +823,36 @@ long long ByteVector::toLongLong(uint offset, bool mostSignificantByteFirst) con return toNumber(*this, offset, mostSignificantByteFirst); } +float ByteVector::toFloat32LE(size_t offset) const +{ + return toFloat(*this, offset); +} + +float ByteVector::toFloat32BE(size_t offset) const +{ + return toFloat(*this, offset); +} + +double ByteVector::toFloat64LE(size_t offset) const +{ + return toFloat(*this, offset); +} + +double ByteVector::toFloat64BE(size_t offset) const +{ + return toFloat(*this, offset); +} + +long double ByteVector::toFloat80LE(size_t offset) const +{ + return toFloat80(*this, offset); +} + +long double ByteVector::toFloat80BE(size_t offset) const +{ + return toFloat80(*this, offset); +} + const char &ByteVector::operator[](int index) const { return d->data->data[d->offset + index]; diff --git a/3rdparty/taglib/toolkit/tbytevector.h b/3rdparty/taglib/toolkit/tbytevector.h index 538565b0a..793b414ce 100644 --- a/3rdparty/taglib/toolkit/tbytevector.h +++ b/3rdparty/taglib/toolkit/tbytevector.h @@ -39,7 +39,7 @@ namespace TagLib { /*! * This class provides a byte vector with some methods that are useful for * tagging purposes. Many of the search functions are tailored to what is - * useful for finding tag related paterns in a data array. + * useful for finding tag related patterns in a data array. */ class TAGLIB_EXPORT ByteVector @@ -85,9 +85,10 @@ namespace TagLib { /*! * Constructs a byte vector that copies \a data up to the first null - * byte. The behavior is undefined if \a data is not null terminated. - * This is particularly useful for constructing byte arrays from string - * constants. + * byte. This is particularly useful for constructing byte arrays from + * string constants. + * + * \warning The behavior is undefined if \a data is not null terminated. */ ByteVector(const char *data); @@ -144,7 +145,7 @@ namespace TagLib { /*! * Searches the char for \a c starting at \a offset and returns - * the offset. Returns \a npos if the pattern was not found. If \a byteAlign is + * the offset. Returns \a -1 if the pattern was not found. If \a byteAlign is * specified the pattern will only be matched if it starts on a byte divisible * by \a byteAlign (starting from \a offset). */ @@ -275,7 +276,10 @@ namespace TagLib { /*! * Returns a CRC checksum of the byte vector's data. + * + * \note This uses an uncommon variant of CRC32 specializes in Ogg. */ + // BIC: Remove or make generic. uint checksum() const; /*! @@ -291,7 +295,7 @@ namespace TagLib { uint toUInt(bool mostSignificantByteFirst = true) const; /*! - * Converts the 4 bytes at \a offset of the vector to an unsigned integer. + * Converts the 4 bytes at \a offset of the vector to an unsigned integer. * * If \a mostSignificantByteFirst is true this will operate left to right * evaluating the integer. For example if \a mostSignificantByteFirst is @@ -303,8 +307,8 @@ namespace TagLib { uint toUInt(uint offset, bool mostSignificantByteFirst = true) const; /*! - * Converts the \a length bytes at \a offset of the vector to an unsigned - * integer. If \a length is larger than 4, the excess is ignored. + * Converts the \a length bytes at \a offset of the vector to an unsigned + * integer. If \a length is larger than 4, the excess is ignored. * * If \a mostSignificantByteFirst is true this will operate left to right * evaluating the integer. For example if \a mostSignificantByteFirst is @@ -383,6 +387,46 @@ namespace TagLib { */ long long toLongLong(uint offset, bool mostSignificantByteFirst = true) const; + /* + * Converts the 4 bytes at \a offset of the vector to a float as an IEEE754 + * 32-bit little-endian floating point number. + */ + float toFloat32LE(size_t offset) const; + + /* + * Converts the 4 bytes at \a offset of the vector to a float as an IEEE754 + * 32-bit big-endian floating point number. + */ + float toFloat32BE(size_t offset) const; + + /* + * Converts the 8 bytes at \a offset of the vector to a double as an IEEE754 + * 64-bit little-endian floating point number. + */ + double toFloat64LE(size_t offset) const; + + /* + * Converts the 8 bytes at \a offset of the vector to a double as an IEEE754 + * 64-bit big-endian floating point number. + */ + double toFloat64BE(size_t offset) const; + + /* + * Converts the 10 bytes at \a offset of the vector to a long double as an + * IEEE754 80-bit little-endian floating point number. + * + * \note This may compromise the precision depends on the size of long double. + */ + long double toFloat80LE(size_t offset) const; + + /* + * Converts the 10 bytes at \a offset of the vector to a long double as an + * IEEE754 80-bit big-endian floating point number. + * + * \note This may compromise the precision depends on the size of long double. + */ + long double toFloat80BE(size_t offset) const; + /*! * Creates a 4 byte ByteVector based on \a value. If * \a mostSignificantByteFirst is true, then this will operate left to right @@ -415,13 +459,45 @@ namespace TagLib { */ static ByteVector fromLongLong(long long value, bool mostSignificantByteFirst = true); + /*! + * Creates a 4 byte ByteVector based on \a value as an IEEE754 32-bit + * little-endian floating point number. + * + * \see fromFloat32BE() + */ + static ByteVector fromFloat32LE(float value); + + /*! + * Creates a 4 byte ByteVector based on \a value as an IEEE754 32-bit + * big-endian floating point number. + * + * \see fromFloat32LE() + */ + static ByteVector fromFloat32BE(float value); + + /*! + * Creates a 8 byte ByteVector based on \a value as an IEEE754 64-bit + * little-endian floating point number. + * + * \see fromFloat64BE() + */ + static ByteVector fromFloat64LE(double value); + + /*! + * Creates a 8 byte ByteVector based on \a value as an IEEE754 64-bit + * big-endian floating point number. + * + * \see fromFloat64LE() + */ + static ByteVector fromFloat64BE(double value); + /*! * Returns a ByteVector based on the CString \a s. */ static ByteVector fromCString(const char *s, uint length = 0xffffffff); /*! - * Returns a const refernence to the byte at \a index. + * Returns a const reference to the byte at \a index. */ const char &operator[](int index) const; @@ -475,12 +551,14 @@ namespace TagLib { ByteVector &operator=(const ByteVector &v); /*! - * Copies ByteVector \a v. + * Copies a byte \a c. */ ByteVector &operator=(char c); /*! - * Copies ByteVector \a v. + * Copies \a data up to the first null byte. + * + * \warning The behavior is undefined if \a data is not null terminated. */ ByteVector &operator=(const char *data); diff --git a/3rdparty/taglib/toolkit/tdebug.cpp b/3rdparty/taglib/toolkit/tdebug.cpp index 71350af7c..557baed6e 100644 --- a/3rdparty/taglib/toolkit/tdebug.cpp +++ b/3rdparty/taglib/toolkit/tdebug.cpp @@ -30,43 +30,12 @@ #include "tdebug.h" #include "tstring.h" #include "tdebuglistener.h" +#include "tutils.h" #include #include #include -using namespace TagLib; - -namespace -{ - String format(const char *fmt, ...) - { - va_list args; - va_start(args, fmt); - - char buf[256]; - -#if defined(HAVE_SNPRINTF) - - vsnprintf(buf, sizeof(buf), fmt, args); - -#elif defined(HAVE_SPRINTF_S) - - vsprintf_s(buf, fmt, args); - -#else - - // Be careful. May cause a buffer overflow. - vsprintf(buf, fmt, args); - -#endif - - va_end(args); - - return String(buf); - } -} - namespace TagLib { // The instance is defined in tdebuglistener.cpp. @@ -75,7 +44,7 @@ namespace TagLib void debug(const String &s) { #if !defined(NDEBUG) || defined(TRACE_IN_RELEASE) - + debugListener->printMessage("TagLib: " + s + "\n"); #endif @@ -85,10 +54,11 @@ namespace TagLib { #if !defined(NDEBUG) || defined(TRACE_IN_RELEASE) - for(size_t i = 0; i < v.size(); ++i) + for(size_t i = 0; i < v.size(); ++i) { std::string bits = std::bitset<8>(v[i]).to_string(); - String msg = format("*** [%d] - char '%c' - int %d, 0x%02x, 0b%s\n", + String msg = Utils::formatString( + "*** [%d] - char '%c' - int %d, 0x%02x, 0b%s\n", i, v[i], v[i], v[i], bits.c_str()); debugListener->printMessage(msg); diff --git a/3rdparty/taglib/toolkit/tdebuglistener.h b/3rdparty/taglib/toolkit/tdebuglistener.h index a32f285f4..3c8e1185c 100644 --- a/3rdparty/taglib/toolkit/tdebuglistener.h +++ b/3rdparty/taglib/toolkit/tdebuglistener.h @@ -29,17 +29,17 @@ #include "taglib_export.h" #include "tstring.h" -namespace TagLib +namespace TagLib { //! An abstraction for the listener to the debug messages. /*! - * This class enables you to handle the debug messages in your preferred - * way by subclassing this class, reimplementing printMessage() and setting + * This class enables you to handle the debug messages in your preferred + * way by subclassing this class, reimplementing printMessage() and setting * your reimplementation as the default with setDebugListener(). * * \see setDebugListener() - */ + */ class TAGLIB_EXPORT DebugListener { public: @@ -60,8 +60,8 @@ namespace TagLib /*! * Sets the listener that decides how the debug messages are redirected. - * If the parameter \a listener is null, the previous listener is released - * and default stderr listener is restored. + * If the parameter \a listener is null, the previous listener is released + * and default stderr listener is restored. * * \note The caller is responsible for deleting the previous listener * as needed after it is released. diff --git a/3rdparty/taglib/toolkit/tfile.cpp b/3rdparty/taglib/toolkit/tfile.cpp index 4a05b05f0..a01c34608 100644 --- a/3rdparty/taglib/toolkit/tfile.cpp +++ b/3rdparty/taglib/toolkit/tfile.cpp @@ -66,15 +66,6 @@ using namespace TagLib; -namespace -{ -#ifdef _WIN32 - const TagLib::uint BufferSize = 8192; -#else - const TagLib::uint BufferSize = 1024; -#endif -} - class File::FilePrivate { public: @@ -278,7 +269,7 @@ long File::find(const ByteVector &pattern, long fromOffset, const ByteVector &be // (2) The search pattern is wholly contained within the current buffer. // // (3) The current buffer ends with a partial match of the pattern. We will - // note this for use in the next itteration, where we will check for the rest + // note this for use in the next iteration, where we will check for the rest // of the pattern. // // All three of these are done in two steps. First we check for the pattern @@ -363,25 +354,34 @@ long File::rfind(const ByteVector &pattern, long fromOffset, const ByteVector &b // Start the search at the offset. - long bufferOffset; - if(fromOffset == 0) { - seek(-1 * int(bufferSize()), End); - bufferOffset = tell(); - } - else { - seek(fromOffset + -1 * int(bufferSize()), Beginning); - bufferOffset = tell(); - } + if(fromOffset == 0) + fromOffset = length(); + + long bufferLength = bufferSize(); + long bufferOffset = fromOffset + pattern.size(); // See the notes in find() for an explanation of this algorithm. - for(buffer = readBlock(bufferSize()); buffer.size() > 0; buffer = readBlock(bufferSize())) { + while(true) { + + if(bufferOffset > bufferLength) { + bufferOffset -= bufferLength; + } + else { + bufferLength = bufferOffset; + bufferOffset = 0; + } + seek(bufferOffset); + + buffer = readBlock(bufferLength); + if(buffer.isEmpty()) + break; // TODO: (1) previous partial match // (2) pattern contained in current buffer - long location = buffer.rfind(pattern); + const long location = buffer.rfind(pattern); if(location >= 0) { seek(originalPosition); return bufferOffset + location; @@ -393,9 +393,6 @@ long File::rfind(const ByteVector &pattern, long fromOffset, const ByteVector &b } // TODO: (3) partial match - - bufferOffset -= bufferSize(); - seek(bufferOffset); } // Since we hit the end of the file, reset the status before continuing. @@ -493,7 +490,7 @@ bool File::isWritable(const char *file) TagLib::uint File::bufferSize() { - return BufferSize; + return 1024; } void File::setValid(bool valid) diff --git a/3rdparty/taglib/toolkit/tfile.h b/3rdparty/taglib/toolkit/tfile.h index 67f6f80f5..fe4efcbaa 100644 --- a/3rdparty/taglib/toolkit/tfile.h +++ b/3rdparty/taglib/toolkit/tfile.h @@ -83,7 +83,7 @@ namespace TagLib { * names (uppercase Strings) to StringLists of tag values. Calls the according * specialization in the File subclasses. * For each metadata object of the file that could not be parsed into the PropertyMap - * format, the returend map's unsupportedData() list will contain one entry identifying + * format, the returned map's unsupportedData() list will contain one entry identifying * that object (e.g. the frame type for ID3v2 tags). Use removeUnsupportedProperties() * to remove (a subset of) them. * For files that contain more than one tag (e.g. an MP3 with both an ID3v2 and an ID3v2 @@ -106,7 +106,7 @@ namespace TagLib { * into the format-specific details. * If some value(s) could not be written imported to the specific metadata format, * the returned PropertyMap will contain those value(s). Otherwise it will be empty, - * indicating that no problems occured. + * indicating that no problems occurred. * With file types that support several tag formats (for instance, MP3 files can have * ID3v1, ID3v2, and APEv2 tags), this function will create the most appropriate one * (ID3v2 for MP3 files). Older formats will be updated as well, if they exist, but won't @@ -115,7 +115,7 @@ namespace TagLib { * BIC: will become pure virtual in the future */ PropertyMap setProperties(const PropertyMap &properties); - + /*! * Returns a pointer to this file's audio properties. This should be * reimplemented in the concrete subclasses. If no audio properties were @@ -155,12 +155,12 @@ namespace TagLib { * Returns the offset in the file that \a pattern occurs at or -1 if it can * not be found. If \a before is set, the search will only continue until the * pattern \a before is found. This is useful for tagging purposes to search - * for a tag before the synch frame. + * for a tag before the sync frame. * * Searching starts at \a fromOffset, which defaults to the beginning of the * file. * - * \note This has the practial limitation that \a pattern can not be longer + * \note This has the practical limitation that \a pattern can not be longer * than the buffer size used by readBlock(). Currently this is 1024 bytes. */ long find(const ByteVector &pattern, @@ -171,12 +171,12 @@ namespace TagLib { * Returns the offset in the file that \a pattern occurs at or -1 if it can * not be found. If \a before is set, the search will only continue until the * pattern \a before is found. This is useful for tagging purposes to search - * for a tag before the synch frame. + * for a tag before the sync frame. * * Searching starts at \a fromOffset and proceeds from the that point to the * beginning of the file and defaults to the end of the file. * - * \note This has the practial limitation that \a pattern can not be longer + * \note This has the practical limitation that \a pattern can not be longer * than the buffer size used by readBlock(). Currently this is 1024 bytes. */ long rfind(const ByteVector &pattern, diff --git a/3rdparty/taglib/toolkit/tfilestream.cpp b/3rdparty/taglib/toolkit/tfilestream.cpp index eae525733..65f37d52e 100644 --- a/3rdparty/taglib/toolkit/tfilestream.cpp +++ b/3rdparty/taglib/toolkit/tfilestream.cpp @@ -36,7 +36,7 @@ using namespace TagLib; -namespace +namespace { #ifdef _WIN32 @@ -45,7 +45,6 @@ namespace typedef FileName FileNameHandle; typedef HANDLE FileHandle; - const TagLib::uint BufferSize = 8192; const FileHandle InvalidFileHandle = INVALID_HANDLE_VALUE; inline FileHandle openFile(const FileName &path, bool readOnly) @@ -79,7 +78,7 @@ namespace DWORD length; if(WriteFile(file, buffer.data(), static_cast(buffer.size()), &length, NULL)) return static_cast(length); - else + else return 0; } @@ -93,7 +92,6 @@ namespace typedef FILE* FileHandle; - const TagLib::uint BufferSize = 8192; const FileHandle InvalidFileHandle = 0; inline FileHandle openFile(const FileName &path, bool readOnly) @@ -151,13 +149,13 @@ FileStream::FileStream(FileName fileName, bool openReadOnly) else d->file = openFile(fileName, true); - if(d->file == InvalidFileHandle) + if(d->file == InvalidFileHandle) { # ifdef _WIN32 debug("Could not open file " + fileName.toString()); # else debug("Could not open file " + String(static_cast(d->name))); -# endif +# endif } } @@ -177,7 +175,7 @@ FileName FileStream::name() const ByteVector FileStream::readBlock(ulong length) { if(!isOpen()) { - debug("File::readBlock() -- invalid file."); + debug("FileStream::readBlock() -- invalid file."); return ByteVector::null; } @@ -192,19 +190,19 @@ ByteVector FileStream::readBlock(ulong length) const size_t count = readFile(d->file, buffer); buffer.resize(static_cast(count)); - + return buffer; } void FileStream::writeBlock(const ByteVector &data) { if(!isOpen()) { - debug("File::writeBlock() -- invalid file."); + debug("FileStream::writeBlock() -- invalid file."); return; } if(readOnly()) { - debug("File::writeBlock() -- read only file."); + debug("FileStream::writeBlock() -- read only file."); return; } @@ -214,12 +212,12 @@ void FileStream::writeBlock(const ByteVector &data) void FileStream::insert(const ByteVector &data, ulong start, ulong replace) { if(!isOpen()) { - debug("File::insert() -- invalid file."); + debug("FileStream::insert() -- invalid file."); return; } if(readOnly()) { - debug("File::insert() -- read only file."); + debug("FileStream::insert() -- read only file."); return; } @@ -262,7 +260,7 @@ void FileStream::insert(const ByteVector &data, ulong start, ulong replace) { // Seek to the current read position and read the data that we're about // to overwrite. Appropriately increment the readPosition. - + seek(readPosition); const size_t bytesRead = readFile(d->file, aboutToOverwrite); aboutToOverwrite.resize(bytesRead); @@ -288,7 +286,7 @@ void FileStream::insert(const ByteVector &data, ulong start, ulong replace) writePosition += buffer.size(); // Make the current buffer the data that we read in the beginning. - + buffer = aboutToOverwrite; } } @@ -296,7 +294,7 @@ void FileStream::insert(const ByteVector &data, ulong start, ulong replace) void FileStream::removeBlock(ulong start, ulong length) { if(!isOpen()) { - debug("File::removeBlock() -- invalid file."); + debug("FileStream::removeBlock() -- invalid file."); return; } @@ -343,7 +341,7 @@ bool FileStream::isOpen() const void FileStream::seek(long offset, Position p) { if(!isOpen()) { - debug("File::seek() -- invalid file."); + debug("FileStream::seek() -- invalid file."); return; } @@ -365,10 +363,12 @@ void FileStream::seek(long offset, Position p) return; } + SetLastError(NO_ERROR); SetFilePointer(d->file, offset, NULL, whence); - if(GetLastError() != NO_ERROR) { - debug("File::seek() -- Failed to set the file pointer."); - } + + const int lastError = GetLastError(); + if(lastError != NO_ERROR && lastError != ERROR_NEGATIVE_SEEK) + debug("FileStream::seek() -- Failed to set the file pointer."); #else @@ -410,12 +410,13 @@ long FileStream::tell() const { #ifdef _WIN32 + SetLastError(NO_ERROR); const DWORD position = SetFilePointer(d->file, 0, NULL, FILE_CURRENT); if(GetLastError() == NO_ERROR) { return static_cast(position); } else { - debug("File::tell() -- Failed to get the file pointer."); + debug("FileStream::tell() -- Failed to get the file pointer."); return 0; } @@ -429,18 +430,19 @@ long FileStream::tell() const long FileStream::length() { if(!isOpen()) { - debug("File::length() -- invalid file."); + debug("FileStream::length() -- invalid file."); return 0; } #ifdef _WIN32 + SetLastError(NO_ERROR); const DWORD fileSize = GetFileSize(d->file, NULL); if(GetLastError() == NO_ERROR) { - return static_cast(fileSize); + return static_cast(fileSize); } else { - debug("File::length() -- Failed to get the file size."); + debug("FileStream::length() -- Failed to get the file size."); return 0; } @@ -469,9 +471,11 @@ void FileStream::truncate(long length) const long currentPos = tell(); seek(length); + + SetLastError(NO_ERROR); SetEndOfFile(d->file); if(GetLastError() != NO_ERROR) { - debug("File::truncate() -- Failed to truncate the file."); + debug("FileStream::truncate() -- Failed to truncate the file."); } seek(currentPos); @@ -488,5 +492,5 @@ void FileStream::truncate(long length) TagLib::uint FileStream::bufferSize() { - return BufferSize; + return 1024; } diff --git a/3rdparty/taglib/toolkit/tiostream.cpp b/3rdparty/taglib/toolkit/tiostream.cpp index c2ad29125..0284aed6b 100644 --- a/3rdparty/taglib/toolkit/tiostream.cpp +++ b/3rdparty/taglib/toolkit/tiostream.cpp @@ -40,8 +40,12 @@ namespace bool supportsUnicode() { +#ifdef UNICODE + return true; +#else const FARPROC p = GetProcAddress(GetModuleHandleA("kernel32"), "CreateFileW"); return (p != NULL); +#endif } // Indicates whether the system supports Unicode file names. diff --git a/3rdparty/taglib/toolkit/tiostream.h b/3rdparty/taglib/toolkit/tiostream.h index 868269644..6bb96b54b 100644 --- a/3rdparty/taglib/toolkit/tiostream.h +++ b/3rdparty/taglib/toolkit/tiostream.h @@ -45,7 +45,7 @@ namespace TagLib { operator const char *() const; const std::wstring &wstr() const; - const std::string &str() const; + const std::string &str() const; String toString() const; diff --git a/3rdparty/taglib/toolkit/tlist.h b/3rdparty/taglib/toolkit/tlist.h index 0099dad56..4277a1820 100644 --- a/3rdparty/taglib/toolkit/tlist.h +++ b/3rdparty/taglib/toolkit/tlist.h @@ -72,7 +72,7 @@ namespace TagLib { /*! * Destroys this List instance. If auto deletion is enabled and this list - * contains a pointer type all of the memebers are also deleted. + * contains a pointer type all of the members are also deleted. */ virtual ~List(); diff --git a/3rdparty/taglib/toolkit/tlist.tcc b/3rdparty/taglib/toolkit/tlist.tcc index 72d4767b6..5f8414ca6 100644 --- a/3rdparty/taglib/toolkit/tlist.tcc +++ b/3rdparty/taglib/toolkit/tlist.tcc @@ -208,6 +208,7 @@ bool List::isEmpty() const template typename List::Iterator List::find(const T &value) { + detach(); return std::find(d->list.begin(), d->list.end(), value); } @@ -265,9 +266,7 @@ template T &List::operator[](uint i) { Iterator it = d->list.begin(); - - for(uint j = 0; j < i; j++) - ++it; + std::advance(it, i); return *it; } @@ -276,9 +275,7 @@ template const T &List::operator[](uint i) const { ConstIterator it = d->list.begin(); - - for(uint j = 0; j < i; j++) - ++it; + std::advance(it, i); return *it; } diff --git a/3rdparty/taglib/toolkit/tmap.tcc b/3rdparty/taglib/toolkit/tmap.tcc index 5d3abcad5..8c1106dfc 100644 --- a/3rdparty/taglib/toolkit/tmap.tcc +++ b/3rdparty/taglib/toolkit/tmap.tcc @@ -145,9 +145,7 @@ template Map &Map::erase(const Key &key) { detach(); - Iterator it = d->map.find(key); - if(it != d->map.end()) - d->map.erase(it); + d->map.erase(key); return *this; } diff --git a/3rdparty/taglib/toolkit/tpropertymap.cpp b/3rdparty/taglib/toolkit/tpropertymap.cpp index 3317cc927..313d7fb43 100644 --- a/3rdparty/taglib/toolkit/tpropertymap.cpp +++ b/3rdparty/taglib/toolkit/tpropertymap.cpp @@ -154,12 +154,12 @@ String PropertyMap::toString() const void PropertyMap::removeEmpty() { - StringList emptyKeys; - for(Iterator it = begin(); it != end(); ++it) - if(it->second.isEmpty()) - emptyKeys.append(it->first); - for(StringList::Iterator emptyIt = emptyKeys.begin(); emptyIt != emptyKeys.end(); emptyIt++ ) - erase(*emptyIt); + PropertyMap m; + for(ConstIterator it = begin(); it != end(); ++it) { + if(!it->second.isEmpty()) + m.insert(it->first, it->second); + } + *this = m; } StringList &PropertyMap::unsupportedData() diff --git a/3rdparty/taglib/toolkit/tpropertymap.h b/3rdparty/taglib/toolkit/tpropertymap.h index 2be49ddb0..c1b835bed 100644 --- a/3rdparty/taglib/toolkit/tpropertymap.h +++ b/3rdparty/taglib/toolkit/tpropertymap.h @@ -19,8 +19,8 @@ * MA 02110-1301 USA * ***************************************************************************/ -#ifndef PROPERTYMAP_H_ -#define PROPERTYMAP_H_ +#ifndef TAGLIB_PROPERTYMAP_H_ +#define TAGLIB_PROPERTYMAP_H_ #include "tmap.h" #include "tstringlist.h" @@ -34,13 +34,13 @@ namespace TagLib { /*! * This map implements a generic representation of textual audio metadata * ("tags") realized as pairs of a case-insensitive key - * and a nonempty list of corresponding values, each value being an an arbitrary + * and a nonempty list of corresponding values, each value being an arbitrary * unicode String. * * Note that most metadata formats pose additional conditions on the tag keys. The * most popular ones (Vorbis, APE, ID3v2) should support all ASCII only words of * length between 2 and 16. - * + * * This class can contain any tags, but here is a list of "well-known" tags that * you might want to use: * @@ -81,14 +81,14 @@ namespace TagLib { * - COPYRIGHT * - ENCODEDBY * - MOOD - * - COMMENT + * - COMMENT * - MEDIA * - LABEL * - CATALOGNUMBER * - BARCODE * * MusicBrainz identifiers: - * + * * - MUSICBRAINZ_TRACKID * - MUSICBRAINZ_ALBUMID * - MUSICBRAINZ_RELEASEGROUPID @@ -123,7 +123,7 @@ namespace TagLib { /*! * Inserts \a values under \a key in the map. If \a key already exists, - * then \values will be appended to the existing StringList. + * then \a values will be appended to the existing StringList. * The returned value indicates success, i.e. whether \a key is a * valid key. */ @@ -230,4 +230,4 @@ namespace TagLib { }; } -#endif /* PROPERTYMAP_H_ */ +#endif /* TAGLIB_PROPERTYMAP_H_ */ diff --git a/3rdparty/taglib/toolkit/trefcounter.cpp b/3rdparty/taglib/toolkit/trefcounter.cpp index 71f3c2f28..27d17b834 100644 --- a/3rdparty/taglib/toolkit/trefcounter.cpp +++ b/3rdparty/taglib/toolkit/trefcounter.cpp @@ -69,20 +69,18 @@ namespace TagLib { + class RefCounter::RefCounterPrivate { public: - RefCounterPrivate() : refCount(1) {} - - void ref() { ATOMIC_INC(refCount); } - bool deref() { return (ATOMIC_DEC(refCount) == 0); } - int count() const { return refCount; } + RefCounterPrivate() : + refCount(1) {} volatile ATOMIC_INT refCount; }; - RefCounter::RefCounter() - : d(new RefCounterPrivate()) + RefCounter::RefCounter() : + d(new RefCounterPrivate()) { } @@ -91,18 +89,18 @@ namespace TagLib delete d; } - void RefCounter::ref() - { - d->ref(); + void RefCounter::ref() + { + ATOMIC_INC(d->refCount); } - bool RefCounter::deref() - { - return d->deref(); + bool RefCounter::deref() + { + return (ATOMIC_DEC(d->refCount) == 0); } - int RefCounter::count() const - { - return d->count(); + int RefCounter::count() const + { + return static_cast(d->refCount); } } diff --git a/3rdparty/taglib/toolkit/trefcounter.h b/3rdparty/taglib/toolkit/trefcounter.h index 75388b101..c231779d3 100644 --- a/3rdparty/taglib/toolkit/trefcounter.h +++ b/3rdparty/taglib/toolkit/trefcounter.h @@ -33,7 +33,9 @@ # include # define TAGLIB_ATOMIC_MAC #elif defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__CYGWIN__) -# define NOMINMAX +# ifndef NOMINMAX +# define NOMINMAX +# endif # include # define TAGLIB_ATOMIC_WIN #elif defined (__GNUC__) && (__GNUC__ * 100 + __GNUC_MINOR__ >= 401) \ diff --git a/3rdparty/taglib/toolkit/tstring.cpp b/3rdparty/taglib/toolkit/tstring.cpp index 75a9833ef..258e1fcf5 100644 --- a/3rdparty/taglib/toolkit/tstring.cpp +++ b/3rdparty/taglib/toolkit/tstring.cpp @@ -39,110 +39,80 @@ #include #include -#ifdef HAVE_STD_CODECVT -# include +#ifdef _WIN32 +# include #else # include "unicode.h" #endif -namespace +namespace { + using namespace TagLib; - inline unsigned short combine(unsigned char c1, unsigned char c2) + inline size_t UTF16toUTF8( + const wchar_t *src, size_t srcLength, char *dst, size_t dstLength) { - return (c1 << 8) | c2; - } + size_t len = 0; - void UTF16toUTF8(const wchar_t *src, size_t srcLength, char *dst, size_t dstLength) - { -#ifdef HAVE_STD_CODECVT +#ifdef _WIN32 - typedef std::codecvt_utf8_utf16 utf8_utf16_t; - - using namespace TagLib; - - const wchar_t *srcBegin = src; - const wchar_t *srcEnd = srcBegin + srcLength; - - char *dstBegin = dst; - char *dstEnd = dstBegin + dstLength; - - std::mbstate_t st; - const wchar_t *source; - char *target; - memset(&st, 0, sizeof(st)); - std::codecvt_base::result result = utf8_utf16_t().out( - st, srcBegin, srcEnd, source, dstBegin, dstEnd, target); - - if(result != utf8_utf16_t::ok) { - debug("String::copyFromUTF8() - Unicode conversion error."); - } + len = ::WideCharToMultiByte(CP_UTF8, 0, src, srcLength, dst, dstLength, NULL, NULL); #else using namespace Unicode; - using namespace TagLib; - const Unicode::UTF16 *srcBegin = src; - const Unicode::UTF16 *srcEnd = srcBegin + srcLength; + const UTF16 *srcBegin = src; + const UTF16 *srcEnd = srcBegin + srcLength; - Unicode::UTF8 *dstBegin = reinterpret_cast(dst); - Unicode::UTF8 *dstEnd = dstBegin + dstLength; + UTF8 *dstBegin = reinterpret_cast(dst); + UTF8 *dstEnd = dstBegin + dstLength; - Unicode::ConversionResult result = Unicode::ConvertUTF16toUTF8( - &srcBegin, srcEnd, &dstBegin, dstEnd, Unicode::lenientConversion); + ConversionResult result = ConvertUTF16toUTF8( + &srcBegin, srcEnd, &dstBegin, dstEnd, lenientConversion); - if(result != Unicode::conversionOK) { - debug("String::to8Bit() - Unicode conversion error."); - } + if(result == conversionOK) + len = dstBegin - reinterpret_cast(dst); #endif + + if(len == 0) + debug("String::UTF16toUTF8() - Unicode conversion error."); + + return len; } - void UTF8toUTF16(const char *src, size_t srcLength, wchar_t *dst, size_t dstLength) + inline size_t UTF8toUTF16( + const char *src, size_t srcLength, wchar_t *dst, size_t dstLength) { -#ifdef HAVE_STD_CODECVT + size_t len = 0; - typedef std::codecvt_utf8_utf16 utf8_utf16_t; +#ifdef _WIN32 - using namespace TagLib; - - const char *srcBegin = src; - const char *srcEnd = srcBegin + srcLength; - - wchar_t *dstBegin = dst; - wchar_t *dstEnd = dstBegin + dstLength; - - std::mbstate_t st; - const char *source; - wchar_t *target; - memset(&st, 0, sizeof(st)); - std::codecvt_base::result result = utf8_utf16_t().in( - st, srcBegin, srcEnd, source, dstBegin, dstEnd, target); - - if(result != utf8_utf16_t::ok) { - debug("String::copyFromUTF8() - Unicode conversion error."); - } + len = ::MultiByteToWideChar(CP_UTF8, 0, src, srcLength, dst, dstLength); #else using namespace Unicode; - using namespace TagLib; - const Unicode::UTF8 *srcBegin = reinterpret_cast(src); - const Unicode::UTF8 *srcEnd = srcBegin + srcLength; + const UTF8 *srcBegin = reinterpret_cast(src); + const UTF8 *srcEnd = srcBegin + srcLength; - Unicode::UTF16 *dstBegin = dst; - Unicode::UTF16 *dstEnd = dstBegin + dstLength; + UTF16 *dstBegin = dst; + UTF16 *dstEnd = dstBegin + dstLength; - Unicode::ConversionResult result = Unicode::ConvertUTF8toUTF16( - &srcBegin, srcEnd, &dstBegin, dstEnd, Unicode::lenientConversion); + ConversionResult result = ConvertUTF8toUTF16( + &srcBegin, srcEnd, &dstBegin, dstEnd, lenientConversion); - if(result != Unicode::conversionOK) { - debug("String::copyFromUTF8() - Unicode conversion error."); - } + if(result == conversionOK) + len = dstBegin - dst; -#endif +#endif + + if(len == 0) + debug("String::UTF8toUTF16() - Unicode conversion error."); + + return len; } } @@ -151,25 +121,25 @@ namespace TagLib { class String::StringPrivate : public RefCounter { public: - StringPrivate() - : RefCounter() + StringPrivate() + : RefCounter() { } - StringPrivate(const wstring &s) + StringPrivate(const wstring &s) : RefCounter() - , data(s) + , data(s) { } - - StringPrivate(uint n, wchar_t c) + + StringPrivate(uint n, wchar_t c) : RefCounter() - , data(static_cast(n), c) + , data(static_cast(n), c) { } /*! - * Stores string in UTF-16. The byte order depends on the CPU endian. + * Stores string in UTF-16. The byte order depends on the CPU endian. */ TagLib::wstring data; @@ -183,12 +153,12 @@ String String::null; //////////////////////////////////////////////////////////////////////////////// -String::String() +String::String() : d(new StringPrivate()) { } -String::String(const String &s) +String::String(const String &s) : d(s.d) { d->ref(); @@ -198,31 +168,47 @@ String::String(const std::string &s, Type t) : d(new StringPrivate()) { if(t == Latin1) - copyFromLatin1(&s[0], s.length()); + copyFromLatin1(s.c_str(), s.length()); else if(t == String::UTF8) - copyFromUTF8(&s[0], s.length()); + copyFromUTF8(s.c_str(), s.length()); else { - debug("String::String() -- A std::string should not contain UTF16."); + debug("String::String() -- std::string should not contain UTF16."); } } String::String(const wstring &s, Type t) : d(new StringPrivate()) { - if(t == UTF16 || t == UTF16BE || t == UTF16LE) + if(t == UTF16 || t == UTF16BE || t == UTF16LE) { + // This looks ugly but needed for the compatibility with TagLib1.8. + // Should be removed in TabLib2.0. + if (t == UTF16BE) + t = WCharByteOrder; + else if (t == UTF16LE) + t = (WCharByteOrder == UTF16LE ? UTF16BE : UTF16LE); + copyFromUTF16(s.c_str(), s.length(), t); + } else { - debug("String::String() -- A TagLib::wstring should not contain Latin1 or UTF-8."); + debug("String::String() -- TagLib::wstring should not contain Latin1 or UTF-8."); } } String::String(const wchar_t *s, Type t) : d(new StringPrivate()) { - if(t == UTF16 || t == UTF16BE || t == UTF16LE) + if(t == UTF16 || t == UTF16BE || t == UTF16LE) { + // This looks ugly but needed for the compatibility with TagLib1.8. + // Should be removed in TabLib2.0. + if (t == UTF16BE) + t = WCharByteOrder; + else if (t == UTF16LE) + t = (WCharByteOrder == UTF16LE ? UTF16BE : UTF16LE); + copyFromUTF16(s, ::wcslen(s), t); + } else { - debug("String::String() -- A const wchar_t * should not contain Latin1 or UTF-8."); + debug("String::String() -- const wchar_t * should not contain Latin1 or UTF-8."); } } @@ -234,7 +220,7 @@ String::String(const char *s, Type t) else if(t == String::UTF8) copyFromUTF8(s, ::strlen(s)); else { - debug("String::String() -- A const char * should not contain UTF16."); + debug("String::String() -- const char * should not contain UTF16."); } } @@ -244,7 +230,7 @@ String::String(wchar_t c, Type t) if(t == UTF16 || t == UTF16BE || t == UTF16LE) copyFromUTF16(&c, 1, t); else { - debug("String::String() -- A const wchar_t should not contain Latin1 or UTF-8."); + debug("String::String() -- wchar_t should not contain Latin1 or UTF-8."); } } @@ -252,7 +238,7 @@ String::String(char c, Type t) : d(new StringPrivate(1, static_cast(c))) { if(t != Latin1 && t != UTF8) { - debug("String::String() -- A char should not contain UTF16."); + debug("String::String() -- char should not contain UTF16."); } } @@ -262,11 +248,11 @@ String::String(const ByteVector &v, Type t) if(v.isEmpty()) return; - if(t == Latin1) + if(t == Latin1) copyFromLatin1(v.data(), v.size()); - else if(t == UTF8) + else if(t == UTF8) copyFromUTF8(v.data(), v.size()); - else + else copyFromUTF16(v.data(), v.size(), t); // If we hit a null in the ByteVector, shrink the string again. @@ -283,25 +269,8 @@ String::~String() std::string String::to8Bit(bool unicode) const { - std::string s; - - if(!unicode) { - s.resize(d->data.size()); - - std::string::iterator targetIt = s.begin(); - for(wstring::const_iterator it = d->data.begin(); it != d->data.end(); it++) { - *targetIt = static_cast(*it); - ++targetIt; - } - } - else { - s.resize(d->data.size() * 4 + 1); - - UTF16toUTF8(&d->data[0], d->data.size(), &s[0], s.size()); - s.resize(::strlen(s.c_str())); - } - - return s; + const ByteVector v = data(unicode ? UTF8 : Latin1); + return std::string(v.data(), v.size()); } TagLib::wstring String::toWString() const @@ -322,6 +291,7 @@ const wchar_t *String::toCWString() const String::Iterator String::begin() { + detach(); return d->data.begin(); } @@ -332,6 +302,7 @@ String::ConstIterator String::begin() const String::Iterator String::end() { + detach(); return d->data.end(); } @@ -353,16 +324,13 @@ int String::rfind(const String &s, int offset) const StringList String::split(const String &separator) const { StringList list; - for(int index = 0;;) - { + for(int index = 0;;) { int sep = find(separator, index); - if(sep < 0) - { + if(sep < 0) { list.append(substr(index, size() - index)); break; } - else - { + else { list.append(substr(index, sep - index)); index = sep + separator.size(); } @@ -428,7 +396,7 @@ bool String::isNull() const ByteVector String::data(Type t) const { - switch(t) + switch(t) { case Latin1: { @@ -441,21 +409,25 @@ ByteVector String::data(Type t) const return v; } case UTF8: + if(!d->data.empty()) { ByteVector v(size() * 4 + 1, 0); - UTF16toUTF8(&d->data[0], d->data.size(), v.data(), v.size()); - v.resize(::strlen(v.data())); + const size_t len = UTF16toUTF8( + d->data.c_str(), d->data.size(), v.data(), v.size()); + v.resize(len); return v; } + else { + return ByteVector::null; + } case UTF16: { ByteVector v(2 + size() * 2, 0); char *p = v.data(); - // Assume that if we're doing UTF16 and not UTF16BE that we want little - // endian encoding. (Byte Order Mark) + // We use little-endian encoding here and need a BOM. *p++ = '\xff'; *p++ = '\xfe'; @@ -494,7 +466,7 @@ ByteVector String::data(Type t) const default: { debug("String::data() - Invalid Type value."); - return ByteVector(); + return ByteVector::null; } } } @@ -571,30 +543,7 @@ bool String::isAscii() const String String::number(int n) // static { - static const size_t BufferSize = 11; // Sufficient to store "-214748364". - static const char *Format = "%d"; - - char buffer[BufferSize]; - int length; - -#if defined(HAVE_SNPRINTF) - - length = snprintf(buffer, BufferSize, Format, n); - -#elif defined(HAVE_SPRINTF_S) - - length = sprintf_s(buffer, Format, n); - -#else - - length = sprintf(buffer, Format, n); - -#endif - - if(length > 0) - return String(buffer); - else - return String::null; + return Utils::formatString("%d", n); } TagLib::wchar &String::operator[](int i) @@ -610,12 +559,38 @@ const TagLib::wchar &String::operator[](int i) const bool String::operator==(const String &s) const { - return d == s.d || d->data == s.d->data; + return (d == s.d || d->data == s.d->data); } bool String::operator!=(const String &s) const { - return !operator==(s); + return !(*this == s); +} + +bool String::operator==(const char *s) const +{ + const wchar_t *p = toCWString(); + + while(*p != L'\0' || *s != '\0') { + if(*p++ != static_cast(*s++)) + return false; + } + return true; +} + +bool String::operator!=(const char *s) const +{ + return !(*this == s); +} + +bool String::operator==(const wchar_t *s) const +{ + return (d->data == s); +} + +bool String::operator!=(const wchar_t *s) const +{ + return !(*this == s); } String &String::operator+=(const String &s) @@ -775,17 +750,19 @@ void String::copyFromUTF8(const char *s, size_t length) { d->data.resize(length); - UTF8toUTF16(s, length, &d->data[0], d->data.size()); - d->data.resize(::wcslen(d->data.c_str())); + if(length > 0) { + const size_t len = UTF8toUTF16(s, length, &d->data[0], d->data.size()); + d->data.resize(len); + } } void String::copyFromUTF16(const wchar_t *s, size_t length, Type t) { bool swap; if(t == UTF16) { - if(length >= 1 && s[0] == 0xfeff) + if(length >= 1 && s[0] == 0xfeff) swap = false; // Same as CPU endian. No need to swap bytes. - else if(length >= 1 && s[0] == 0xfffe) + else if(length >= 1 && s[0] == 0xfffe) swap = true; // Not same as CPU endian. Need to swap bytes. else { debug("String::copyFromUTF16() - Invalid UTF16 string."); @@ -795,15 +772,18 @@ void String::copyFromUTF16(const wchar_t *s, size_t length, Type t) s++; length--; } - else + else swap = (t != WCharByteOrder); d->data.resize(length); - memcpy(&d->data[0], s, length * sizeof(wchar_t)); - - if(swap) { - for(size_t i = 0; i < length; ++i) - d->data[i] = Utils::byteSwap(static_cast(s[i])); + if(length > 0) { + if(swap) { + for(size_t i = 0; i < length; ++i) + d->data[i] = Utils::byteSwap(static_cast(s[i])); + } + else { + ::wmemcpy(&d->data[0], s, length); + } } } @@ -820,9 +800,9 @@ void String::copyFromUTF16(const char *s, size_t length, Type t) ushort bom; ::memcpy(&bom, s, 2); - if(bom == 0xfeff) + if(bom == 0xfeff) swap = false; // Same as CPU endian. No need to swap bytes. - else if(bom == 0xfffe) + else if(bom == 0xfffe) swap = true; // Not same as CPU endian. Need to swap bytes. else { debug("String::copyFromUTF16() - Invalid UTF16 string."); @@ -832,18 +812,23 @@ void String::copyFromUTF16(const char *s, size_t length, Type t) s += 2; length -= 2; } - else + else swap = (t != WCharByteOrder); d->data.resize(length / 2); for(size_t i = 0; i < length / 2; ++i) { - d->data[i] = swap ? combine(*s, *(s + 1)) : combine(*(s + 1), *s); + ushort c; + ::memcpy(&c, s, 2); + if(swap) + c = Utils::byteSwap(c); + + d->data[i] = static_cast(c); s += 2; } } -const String::Type String::WCharByteOrder - = (Utils::SystemByteOrder == Utils::BigEndian) ? String::UTF16BE : String::UTF16LE; +const String::Type String::WCharByteOrder + = (Utils::systemByteOrder() == Utils::BigEndian) ? String::UTF16BE : String::UTF16LE; } diff --git a/3rdparty/taglib/toolkit/tstring.h b/3rdparty/taglib/toolkit/tstring.h index 57945beed..8b7398823 100644 --- a/3rdparty/taglib/toolkit/tstring.h +++ b/3rdparty/taglib/toolkit/tstring.h @@ -41,7 +41,12 @@ * \note consider conversion via usual char-by-char for loop to avoid UTF16->UTF8->UTF16 * conversion happening in the background */ + +#if defined(QT_VERSION) && (QT_VERSION >= 0x040000) +#define QStringToTString(s) TagLib::String(s.toUtf8().data(), TagLib::String::UTF8) +#else #define QStringToTString(s) TagLib::String(s.utf8().data(), TagLib::String::UTF8) +#endif /*! * \relates TagLib::String @@ -52,6 +57,7 @@ * conversion happening in the background * */ + #define TStringToQString(s) QString::fromUtf8(s.toCString(true)) namespace TagLib { @@ -134,13 +140,21 @@ namespace TagLib { /*! * Makes a deep copy of the data in \a s. + * + * /note If \a t is UTF16LE, the byte order of \a s will be swapped regardless + * of the CPU byte order. If UTF16BE, it will not be swapped. This behavior + * will be changed in TagLib2.0. */ - String(const wstring &s, Type t = WCharByteOrder); + String(const wstring &s, Type t = UTF16BE); /*! * Makes a deep copy of the data in \a s. + * + * /note If \a t is UTF16LE, the byte order of \a s will be swapped regardless + * of the CPU byte order. If UTF16BE, it will not be swapped. This behavior + * will be changed in TagLib2.0. */ - String(const wchar_t *s, Type t = WCharByteOrder); + String(const wchar_t *s, Type t = UTF16BE); /*! * Makes a deep copy of the data in \a c. @@ -155,7 +169,6 @@ namespace TagLib { */ String(wchar_t c, Type t = Latin1); - /*! * Makes a deep copy of the data in \a s. * @@ -165,10 +178,7 @@ namespace TagLib { String(const char *s, Type t = Latin1); /*! - * Makes a deep copy of the data in \a s. - * - * \note This should only be used with the 8-bit codecs Latin1 and UTF8, when - * used with other codecs it will simply print a warning and exit. + * Makes a deep copy of the data in \a v. */ String(const ByteVector &v, Type t = Latin1); @@ -178,7 +188,7 @@ namespace TagLib { virtual ~String(); /*! - * Returns a deep copy of this String as an std::string. The returned string + * Returns a deep copy of this String as an std::string. The returned string * is encoded in UTF8 if \a unicode is true, otherwise Latin1. * * \see toCString() @@ -186,51 +196,52 @@ namespace TagLib { std::string to8Bit(bool unicode = false) const; /*! - * Returns a deep copy of this String as a wstring. The returned string is - * encoded in UTF-16 (without BOM/CPU byte order). + * Returns a deep copy of this String as a wstring. The returned string is + * encoded in UTF-16 (without BOM/CPU byte order), not UTF-32 even if wchar_t + * is 32-bit wide. * * \see toCWString() */ wstring toWString() const; /*! - * Creates and returns a standard C-style (null-terminated) version of this - * String. The returned string is encoded in UTF8 if \a unicode is true, + * Creates and returns a standard C-style (null-terminated) version of this + * String. The returned string is encoded in UTF8 if \a unicode is true, * otherwise Latin1. - * - * The returned string is still owned by this String and should not be deleted + * + * The returned string is still owned by this String and should not be deleted * by the user. * - * The returned pointer remains valid until this String instance is destroyed + * The returned pointer remains valid until this String instance is destroyed * or toCString() is called again. * * \warning This however has the side effect that the returned string will remain - * in memory in addition to other memory that is consumed by this + * in memory in addition to other memory that is consumed by this * String instance. So, this method should not be used on large strings or * where memory is critical. Consider using to8Bit() instead to avoid it. * * \see to8Bit() */ const char *toCString(bool unicode = false) const; - + /*! - * Returns a standard C-style (null-terminated) wide character version of - * this String. The returned string is encoded in UTF-16 (without BOM/CPU byte - * order). - * - * The returned string is still owned by this String and should not be deleted + * Returns a standard C-style (null-terminated) wide character version of + * this String. The returned string is encoded in UTF-16 (without BOM/CPU byte + * order), not UTF-32 even if wchar_t is 32-bit wide. + * + * The returned string is still owned by this String and should not be deleted * by the user. * - * The returned pointer remains valid until this String instance is destroyed + * The returned pointer remains valid until this String instance is destroyed * or any other method of this String is called. * - * /note This returns a pointer to the String's internal data without any + * \note This returns a pointer to the String's internal data without any * conversions. * * \see toWString() */ const wchar_t *toCWString() const; - + /*! * Returns an iterator pointing to the beginning of the string. */ @@ -325,6 +336,11 @@ namespace TagLib { * Returns a ByteVector containing the string's data. If \a t is Latin1 or * UTF8, this will return a vector of 8 bit characters, otherwise it will use * 16 bit characters. + * + * \note If \a t is UTF16, the returned data is encoded in little-endian + * format and has a BOM. + * + * \note The returned data is not null terminated. */ ByteVector data(Type t) const; @@ -340,7 +356,7 @@ namespace TagLib { /*! * Convert the string to an integer. * - * If the conversion was successfull, it sets the value of \a *ok to + * If the conversion was successful, it sets the value of \a *ok to * true and returns the integer. Otherwise it sets \a *ok to false * and the result is undefined. */ @@ -388,6 +404,30 @@ namespace TagLib { */ bool operator!=(const String &s) const; + /*! + * Compares each character of the String with each character of \a s and + * returns true if the strings match. + */ + bool operator==(const char *s) const; + + /*! + * Compares each character of the String with each character of \a s and + * returns false if the strings match. + */ + bool operator!=(const char *s) const; + + /*! + * Compares each character of the String with each character of \a s and + * returns true if the strings match. + */ + bool operator==(const wchar_t *s) const; + + /*! + * Compares each character of the String with each character of \a s and + * returns false if the strings match. + */ + bool operator!=(const wchar_t *s) const; + /*! * Appends \a s to the end of the String. */ @@ -456,7 +496,7 @@ namespace TagLib { /*! * To be able to use this class in a Map, this operator needed to be - * implemented. Returns true if \a s is less than this string in a bytewise + * implemented. Returns true if \a s is less than this string in a byte-wise * comparison. */ bool operator<(const String &s) const; @@ -476,31 +516,31 @@ namespace TagLib { private: /*! - * Converts a \e Latin-1 string into \e UTF-16(without BOM/CPU byte order) + * Converts a \e Latin-1 string into \e UTF-16(without BOM/CPU byte order) * and copies it to the internal buffer. */ void copyFromLatin1(const char *s, size_t length); /*! - * Converts a \e UTF-8 string into \e UTF-16(without BOM/CPU byte order) + * Converts a \e UTF-8 string into \e UTF-16(without BOM/CPU byte order) * and copies it to the internal buffer. */ void copyFromUTF8(const char *s, size_t length); /*! - * Converts a \e UTF-16 (with BOM), UTF-16LE or UTF16-BE string into + * Converts a \e UTF-16 (with BOM), UTF-16LE or UTF16-BE string into * \e UTF-16(without BOM/CPU byte order) and copies it to the internal buffer. */ void copyFromUTF16(const wchar_t *s, size_t length, Type t); /*! - * Converts a \e UTF-16 (with BOM), UTF-16LE or UTF16-BE string into + * Converts a \e UTF-16 (with BOM), UTF-16LE or UTF16-BE string into * \e UTF-16(without BOM/CPU byte order) and copies it to the internal buffer. */ void copyFromUTF16(const char *s, size_t length, Type t); - + /*! - * Indicates which byte order of UTF-16 is used to store strings internally. + * Indicates which byte order of UTF-16 is used to store strings internally. * * \note \e String::UTF16BE or \e String::UTF16LE */ diff --git a/3rdparty/taglib/toolkit/tstringlist.h b/3rdparty/taglib/toolkit/tstringlist.h index f94b50f5a..41b7f6ecd 100644 --- a/3rdparty/taglib/toolkit/tstringlist.h +++ b/3rdparty/taglib/toolkit/tstringlist.h @@ -38,7 +38,7 @@ namespace TagLib { //! A list of strings /*! - * This is a spcialization of the List class with some members convention for + * This is a specialization of the List class with some members convention for * string operations. */ diff --git a/3rdparty/taglib/toolkit/tutils.h b/3rdparty/taglib/toolkit/tutils.h index 4a398d26c..82f1dd9af 100644 --- a/3rdparty/taglib/toolkit/tutils.h +++ b/3rdparty/taglib/toolkit/tutils.h @@ -44,13 +44,22 @@ # include #endif +#include +#include +#include +#include + namespace TagLib { namespace Utils { + + /*! + * Reverses the order of bytes in an 16-bit integer. + */ inline ushort byteSwap(ushort x) { -#if defined(HAVE_GCC_BYTESWAP_16) +#if defined(HAVE_GCC_BYTESWAP) return __builtin_bswap16(x); @@ -77,9 +86,12 @@ namespace TagLib #endif } + /*! + * Reverses the order of bytes in an 32-bit integer. + */ inline uint byteSwap(uint x) { -#if defined(HAVE_GCC_BYTESWAP_32) +#if defined(HAVE_GCC_BYTESWAP) return __builtin_bswap32(x); @@ -109,9 +121,12 @@ namespace TagLib #endif } + /*! + * Reverses the order of bytes in an 64-bit integer. + */ inline ulonglong byteSwap(ulonglong x) { -#if defined(HAVE_GCC_BYTESWAP_64) +#if defined(HAVE_GCC_BYTESWAP) return __builtin_bswap64(x); @@ -145,26 +160,65 @@ namespace TagLib #endif } - enum ByteOrder + /*! + * Returns a formatted string just like standard sprintf(), but makes use of + * safer functions such as snprintf() if available. + */ + inline String formatString(const char *format, ...) { - LittleEndian, - BigEndian - }; + // Sufficient buffer size for the current internal uses. + // Consider changing this value when you use this function. -#ifdef SYSTEM_BYTEORDER + static const size_t BufferSize = 128; -# if SYSTEM_BYTEORDER == 1 + va_list args; + va_start(args, format); - const ByteOrder SystemByteOrder = LittleEndian; + char buf[BufferSize]; + int length; -# else +#if defined(HAVE_VSNPRINTF) - const ByteOrder SystemByteOrder = BigEndian; + length = vsnprintf(buf, BufferSize, format, args); -# endif +#elif defined(HAVE_VSPRINTF_S) + + length = vsprintf_s(buf, format, args); #else + // The last resort. May cause a buffer overflow. + + length = vsprintf(buf, format, args); + if(length >= BufferSize) { + debug("Utils::formatString() - Buffer overflow! Returning an empty string."); + length = -1; + } + +#endif + + va_end(args); + + if(length != -1) + return String(buf); + else + return String::null; + } + + /*! + * The types of byte order of the running system. + */ + enum ByteOrder + { + //! Little endian systems. + LittleEndian, + //! Big endian systems. + BigEndian + }; + + /*! + * Returns the integer byte order of the system. + */ inline ByteOrder systemByteOrder() { union { @@ -178,10 +232,27 @@ namespace TagLib else return BigEndian; } - - const ByteOrder SystemByteOrder = systemByteOrder(); -#endif + /*! + * Returns the IEEE754 byte order of the system. + */ + inline ByteOrder floatByteOrder() + { + union { + double d; + char c; + } u; + + // 1.0 is stored in memory like 0x3FF0000000000000 in canonical form. + // So the first byte is zero if little endian. + + u.d = 1.0; + if(u.c == 0) + return LittleEndian; + else + return BigEndian; + } + } } diff --git a/3rdparty/taglib/toolkit/unicode.h b/3rdparty/taglib/toolkit/unicode.h index ebf1915d8..d3a869f04 100644 --- a/3rdparty/taglib/toolkit/unicode.h +++ b/3rdparty/taglib/toolkit/unicode.h @@ -106,11 +106,6 @@ bit mask & shift operations. ------------------------------------------------------------------------ */ -// Workaround for when MSVC doesn't have wchar_t as a built-in type. -#if defined(_MSC_VER) && !defined(_WCHAR_T_DEFINED) -# include -#endif - /* Some fundamental constants */ #define UNI_REPLACEMENT_CHAR (UTF32)0x0000FFFD #define UNI_MAX_BMP (UTF32)0x0000FFFF diff --git a/3rdparty/taglib/trueaudio/trueaudiofile.cpp b/3rdparty/taglib/trueaudio/trueaudiofile.cpp index f98d6addc..8f40564cd 100644 --- a/3rdparty/taglib/trueaudio/trueaudiofile.cpp +++ b/3rdparty/taglib/trueaudio/trueaudiofile.cpp @@ -84,38 +84,38 @@ public: // public members //////////////////////////////////////////////////////////////////////////////// -TrueAudio::File::File(FileName file, bool readProperties, - Properties::ReadStyle propertiesStyle) : TagLib::File(file) +TrueAudio::File::File(FileName file, bool readProperties, Properties::ReadStyle) : + TagLib::File(file), + d(new FilePrivate()) { - d = new FilePrivate; if(isOpen()) - read(readProperties, propertiesStyle); + read(readProperties); } TrueAudio::File::File(FileName file, ID3v2::FrameFactory *frameFactory, - bool readProperties, Properties::ReadStyle propertiesStyle) : - TagLib::File(file) + bool readProperties, Properties::ReadStyle) : + TagLib::File(file), + d(new FilePrivate(frameFactory)) { - d = new FilePrivate(frameFactory); if(isOpen()) - read(readProperties, propertiesStyle); + read(readProperties); } -TrueAudio::File::File(IOStream *stream, bool readProperties, - Properties::ReadStyle propertiesStyle) : TagLib::File(stream) +TrueAudio::File::File(IOStream *stream, bool readProperties, Properties::ReadStyle) : + TagLib::File(stream), + d(new FilePrivate()) { - d = new FilePrivate; if(isOpen()) - read(readProperties, propertiesStyle); + read(readProperties); } TrueAudio::File::File(IOStream *stream, ID3v2::FrameFactory *frameFactory, - bool readProperties, Properties::ReadStyle propertiesStyle) : - TagLib::File(stream) + bool readProperties, Properties::ReadStyle) : + TagLib::File(stream), + d(new FilePrivate(frameFactory)) { - d = new FilePrivate(frameFactory); if(isOpen()) - read(readProperties, propertiesStyle); + read(readProperties); } TrueAudio::File::~File() @@ -246,12 +246,11 @@ bool TrueAudio::File::hasID3v2Tag() const return d->hasID3v2; } - //////////////////////////////////////////////////////////////////////////////// // private members //////////////////////////////////////////////////////////////////////////////// -void TrueAudio::File::read(bool readProperties, Properties::ReadStyle /* propertiesStyle */) +void TrueAudio::File::read(bool readProperties) { // Look for an ID3v2 tag @@ -284,16 +283,23 @@ void TrueAudio::File::read(bool readProperties, Properties::ReadStyle /* propert // Look for TrueAudio metadata if(readProperties) { - if(d->ID3v2Location >= 0) { + + long streamLength; + + if(d->hasID3v1) + streamLength = d->ID3v1Location; + else + streamLength = length(); + + if(d->hasID3v2) { seek(d->ID3v2Location + d->ID3v2OriginalSize); - d->properties = new Properties(readBlock(TrueAudio::HeaderSize), - length() - d->ID3v2OriginalSize); + streamLength -= (d->ID3v2Location + d->ID3v2OriginalSize); } else { seek(0); - d->properties = new Properties(readBlock(TrueAudio::HeaderSize), - length()); } + + d->properties = new Properties(readBlock(TrueAudio::HeaderSize), streamLength); } } diff --git a/3rdparty/taglib/trueaudio/trueaudiofile.h b/3rdparty/taglib/trueaudio/trueaudiofile.h index dbaafcdb9..3fc515f6a 100644 --- a/3rdparty/taglib/trueaudio/trueaudiofile.h +++ b/3rdparty/taglib/trueaudio/trueaudiofile.h @@ -79,7 +79,7 @@ namespace TagLib { }; /*! - * Constructs a TrueAudio file from \a file. If \a readProperties is true + * Constructs a TrueAudio file from \a file. If \a readProperties is true * the file's audio properties will also be read. * * \note In the current implementation, \a propertiesStyle is ignored. @@ -88,7 +88,7 @@ namespace TagLib { Properties::ReadStyle propertiesStyle = Properties::Average); /*! - * Constructs a TrueAudio file from \a file. If \a readProperties is true + * Constructs a TrueAudio file from \a file. If \a readProperties is true * the file's audio properties will also be read. * * If this file contains and ID3v2 tag the frames will be created using @@ -113,7 +113,7 @@ namespace TagLib { Properties::ReadStyle propertiesStyle = Properties::Average); /*! - * Constructs a TrueAudio file from \a stream. If \a readProperties is true + * Constructs a TrueAudio file from \a stream. If \a readProperties is true * the file's audio properties will also be read. * * \note TagLib will *not* take ownership of the stream, the caller is @@ -164,6 +164,7 @@ namespace TagLib { * Set the ID3v2::FrameFactory to something other than the default. * * \see ID3v2FrameFactory + * \deprecated This value should be passed in via the constructor */ void setID3v2FrameFactory(const ID3v2::FrameFactory *factory); @@ -179,8 +180,8 @@ namespace TagLib { * if there is no valid ID3v1 tag. If \a create is true it will create * an ID3v1 tag if one does not exist and returns a valid pointer. * - * \note This may return a valid pointer regardless of whether or not the - * file on disk has an ID3v1 tag. Use hasID3v1Tag() to check if the file + * \note This may return a valid pointer regardless of whether or not the + * file on disk has an ID3v1 tag. Use hasID3v1Tag() to check if the file * on disk actually has an ID3v1 tag. * * \note The Tag is still owned by the MPEG::File and should not be @@ -198,8 +199,8 @@ namespace TagLib { * if there is no valid ID3v2 tag. If \a create is true it will create * an ID3v2 tag if one does not exist and returns a valid pointer. * - * \note This may return a valid pointer regardless of whether or not the - * file on disk has an ID3v2 tag. Use hasID3v2Tag() to check if the file + * \note This may return a valid pointer regardless of whether or not the + * file on disk has an ID3v2 tag. Use hasID3v2Tag() to check if the file * on disk actually has an ID3v2 tag. * * \note The Tag is still owned by the MPEG::File and should not be @@ -219,7 +220,7 @@ namespace TagLib { * \note In order to make the removal permanent save() still needs to be called */ void strip(int tags = AllTags); - + /*! * Returns whether or not the file on disk actually has an ID3v1 tag. * @@ -233,13 +234,12 @@ namespace TagLib { * \see ID3v2Tag() */ bool hasID3v2Tag() const; - + private: File(const File &); File &operator=(const File &); - void read(bool readProperties, Properties::ReadStyle propertiesStyle); - void scan(); + void read(bool readProperties); long findID3v1(); long findID3v2(); diff --git a/3rdparty/taglib/trueaudio/trueaudioproperties.cpp b/3rdparty/taglib/trueaudio/trueaudioproperties.cpp index dedd74e94..17e3a2abe 100644 --- a/3rdparty/taglib/trueaudio/trueaudioproperties.cpp +++ b/3rdparty/taglib/trueaudio/trueaudioproperties.cpp @@ -39,10 +39,7 @@ using namespace TagLib; class TrueAudio::Properties::PropertiesPrivate { public: - PropertiesPrivate(const ByteVector &d, long length, ReadStyle s) : - data(d), - streamLength(length), - style(s), + PropertiesPrivate() : version(0), length(0), bitrate(0), @@ -51,9 +48,6 @@ public: bitsPerSample(0), sampleFrames(0) {} - ByteVector data; - long streamLength; - ReadStyle style; int version; int length; int bitrate; @@ -67,10 +61,11 @@ public: // public members //////////////////////////////////////////////////////////////////////////////// -TrueAudio::Properties::Properties(const ByteVector &data, long streamLength, ReadStyle style) : AudioProperties(style) +TrueAudio::Properties::Properties(const ByteVector &data, long streamLength, ReadStyle style) : + AudioProperties(style), + d(new PropertiesPrivate()) { - d = new PropertiesPrivate(data, streamLength, style); - read(); + read(data, streamLength); } TrueAudio::Properties::~Properties() @@ -79,6 +74,16 @@ TrueAudio::Properties::~Properties() } int TrueAudio::Properties::length() const +{ + return lengthInSeconds(); +} + +int TrueAudio::Properties::lengthInSeconds() const +{ + return d->length / 1000; +} + +int TrueAudio::Properties::lengthInMilliseconds() const { return d->length; } @@ -117,34 +122,50 @@ int TrueAudio::Properties::ttaVersion() const // private members //////////////////////////////////////////////////////////////////////////////// -void TrueAudio::Properties::read() +void TrueAudio::Properties::read(const ByteVector &data, long streamLength) { - if(!d->data.startsWith("TTA")) + if(data.size() < 4) { + debug("TrueAudio::Properties::read() -- data is too short."); return; + } - int pos = 3; + if(!data.startsWith("TTA")) { + debug("TrueAudio::Properties::read() -- invalid header signature."); + return; + } - d->version = d->data[pos] - '0'; + uint pos = 3; + + d->version = data[pos] - '0'; pos += 1; // According to http://en.true-audio.com/TTA_Lossless_Audio_Codec_-_Format_Description // TTA2 headers are in development, and have a different format if(1 == d->version) { + if(data.size() < 18) { + debug("TrueAudio::Properties::read() -- data is too short."); + return; + } + // Skip the audio format pos += 2; - d->channels = d->data.toShort(pos, false); + d->channels = data.toShort(pos, false); pos += 2; - d->bitsPerSample = d->data.toShort(pos, false); + d->bitsPerSample = data.toShort(pos, false); pos += 2; - d->sampleRate = d->data.toUInt(pos, false); + d->sampleRate = data.toUInt(pos, false); pos += 4; - d->sampleFrames = d->data.toUInt(pos, false); - d->length = d->sampleRate > 0 ? d->sampleFrames / d->sampleRate : 0; + d->sampleFrames = data.toUInt(pos, false); + pos += 4; - d->bitrate = d->length > 0 ? ((d->streamLength * 8L) / d->length) / 1000 : 0; + if(d->sampleFrames > 0 && d->sampleRate > 0) { + const double length = d->sampleFrames * 1000.0 / d->sampleRate; + d->length = static_cast(length + 0.5); + d->bitrate = static_cast(streamLength * 8.0 / length + 0.5); + } } } diff --git a/3rdparty/taglib/trueaudio/trueaudioproperties.h b/3rdparty/taglib/trueaudio/trueaudioproperties.h index 126b6788b..4d8cd4514 100644 --- a/3rdparty/taglib/trueaudio/trueaudioproperties.h +++ b/3rdparty/taglib/trueaudio/trueaudioproperties.h @@ -61,15 +61,50 @@ namespace TagLib { */ virtual ~Properties(); - // Reimplementations. - + /*! + * Returns the length of the file in seconds. The length is rounded down to + * the nearest whole second. + * + * \note This method is just an alias of lengthInSeconds(). + * + * \deprecated + */ virtual int length() const; + + /*! + * Returns the length of the file in seconds. The length is rounded down to + * the nearest whole second. + * + * \see lengthInMilliseconds() + */ + // BIC: make virtual + int lengthInSeconds() const; + + /*! + * Returns the length of the file in milliseconds. + * + * \see lengthInSeconds() + */ + // BIC: make virtual + int lengthInMilliseconds() const; + + /*! + * Returns the average bit rate of the file in kb/s. + */ virtual int bitrate() const; + + /*! + * Returns the sample rate in Hz. + */ virtual int sampleRate() const; + + /*! + * Returns the number of audio channels. + */ virtual int channels() const; /*! - * Returns number of bits per sample. + * Returns the number of bits per audio sample. */ int bitsPerSample() const; @@ -87,7 +122,7 @@ namespace TagLib { Properties(const Properties &); Properties &operator=(const Properties &); - void read(); + void read(const ByteVector &data, long streamLength); class PropertiesPrivate; PropertiesPrivate *d; diff --git a/3rdparty/taglib/wavpack/wavpackfile.cpp b/3rdparty/taglib/wavpack/wavpackfile.cpp index 13b8c570d..de0ba4159 100644 --- a/3rdparty/taglib/wavpack/wavpackfile.cpp +++ b/3rdparty/taglib/wavpack/wavpackfile.cpp @@ -82,20 +82,20 @@ public: // public members //////////////////////////////////////////////////////////////////////////////// -WavPack::File::File(FileName file, bool readProperties, - Properties::ReadStyle propertiesStyle) : TagLib::File(file) +WavPack::File::File(FileName file, bool readProperties, Properties::ReadStyle) : + TagLib::File(file), + d(new FilePrivate()) { - d = new FilePrivate; if(isOpen()) - read(readProperties, propertiesStyle); + read(readProperties); } -WavPack::File::File(IOStream *stream, bool readProperties, - Properties::ReadStyle propertiesStyle) : TagLib::File(stream) +WavPack::File::File(IOStream *stream, bool readProperties, Properties::ReadStyle) : + TagLib::File(stream), + d(new FilePrivate()) { - d = new FilePrivate; if(isOpen()) - read(readProperties, propertiesStyle); + read(readProperties); } WavPack::File::~File() @@ -245,7 +245,7 @@ bool WavPack::File::hasAPETag() const // private members //////////////////////////////////////////////////////////////////////////////// -void WavPack::File::read(bool readProperties, Properties::ReadStyle /* propertiesStyle */) +void WavPack::File::read(bool readProperties) { // Look for an ID3v1 tag @@ -273,8 +273,17 @@ void WavPack::File::read(bool readProperties, Properties::ReadStyle /* propertie // Look for WavPack audio properties if(readProperties) { - seek(0); - d->properties = new Properties(this, length() - d->APESize); + + long streamLength; + + if(d->hasAPE) + streamLength = d->APELocation; + else if(d->hasID3v1) + streamLength = d->ID3v1Location; + else + streamLength = length(); + + d->properties = new Properties(this, streamLength); } } diff --git a/3rdparty/taglib/wavpack/wavpackfile.h b/3rdparty/taglib/wavpack/wavpackfile.h index c85c75f2b..24511581b 100644 --- a/3rdparty/taglib/wavpack/wavpackfile.h +++ b/3rdparty/taglib/wavpack/wavpackfile.h @@ -133,6 +133,11 @@ namespace TagLib { /*! * Saves the file. + * + * This returns true if the save was successful. + * + * \warning In the current implementation, it's dangerous to call save() + * repeatedly. At worst it will corrupt the file. */ virtual bool save(); @@ -143,8 +148,8 @@ namespace TagLib { * if there is no valid ID3v1 tag. If \a create is true it will create * an ID3v1 tag if one does not exist and returns a valid pointer. * - * \note This may return a valid pointer regardless of whether or not the - * file on disk has an ID3v1 tag. Use hasID3v1Tag() to check if the file + * \note This may return a valid pointer regardless of whether or not the + * file on disk has an ID3v1 tag. Use hasID3v1Tag() to check if the file * on disk actually has an ID3v1 tag. * * \note The Tag is still owned by the MPEG::File and should not be @@ -162,8 +167,8 @@ namespace TagLib { * if there is no valid APE tag. If \a create is true it will create * an APE tag if one does not exist and returns a valid pointer. * - * \note This may return a valid pointer regardless of whether or not the - * file on disk has an APE tag. Use hasAPETag() to check if the file + * \note This may return a valid pointer regardless of whether or not the + * file on disk has an APE tag. Use hasAPETag() to check if the file * on disk actually has an APE tag. * * \note The Tag is still owned by the MPEG::File and should not be @@ -183,7 +188,7 @@ namespace TagLib { * \note In order to make the removal permanent save() still needs to be called */ void strip(int tags = AllTags); - + /*! * Returns whether or not the file on disk actually has an ID3v1 tag. * @@ -197,13 +202,12 @@ namespace TagLib { * \see APETag() */ bool hasAPETag() const; - + private: File(const File &); File &operator=(const File &); - void read(bool readProperties, Properties::ReadStyle propertiesStyle); - void scan(); + void read(bool readProperties); long findID3v1(); long findAPE(); diff --git a/3rdparty/taglib/wavpack/wavpackproperties.cpp b/3rdparty/taglib/wavpack/wavpackproperties.cpp index 085ddf8a4..4e4ccd582 100644 --- a/3rdparty/taglib/wavpack/wavpackproperties.cpp +++ b/3rdparty/taglib/wavpack/wavpackproperties.cpp @@ -33,53 +33,50 @@ #include "wavpackproperties.h" #include "wavpackfile.h" +// Implementation of this class is based on the information at: +// http://www.wavpack.com/file_format.txt + using namespace TagLib; class WavPack::Properties::PropertiesPrivate { public: - PropertiesPrivate(const ByteVector &d, long length, ReadStyle s) : - data(d), - streamLength(length), - style(s), + PropertiesPrivate() : length(0), bitrate(0), sampleRate(0), channels(0), version(0), bitsPerSample(0), - sampleFrames(0), - file(0) {} + lossless(false), + sampleFrames(0) {} - ByteVector data; - long streamLength; - ReadStyle style; int length; int bitrate; int sampleRate; int channels; int version; int bitsPerSample; + bool lossless; uint sampleFrames; - File *file; }; //////////////////////////////////////////////////////////////////////////////// // public members //////////////////////////////////////////////////////////////////////////////// -WavPack::Properties::Properties(const ByteVector &data, long streamLength, ReadStyle style) : AudioProperties(style) +WavPack::Properties::Properties(const ByteVector &, long, ReadStyle style) : + AudioProperties(style), + d(new PropertiesPrivate()) { - d = new PropertiesPrivate(data, streamLength, style); - read(); + debug("WavPack::Properties::Properties() -- This constructor is no longer used."); } -WavPack::Properties::Properties(File *file, long streamLength, ReadStyle style) : AudioProperties(style) +WavPack::Properties::Properties(File *file, long streamLength, ReadStyle style) : + AudioProperties(style), + d(new PropertiesPrivate()) { - ByteVector data = file->readBlock(32); - d = new PropertiesPrivate(data, streamLength, style); - d->file = file; - read(); + read(file, streamLength); } WavPack::Properties::~Properties() @@ -88,6 +85,16 @@ WavPack::Properties::~Properties() } int WavPack::Properties::length() const +{ + return lengthInSeconds(); +} + +int WavPack::Properties::lengthInSeconds() const +{ + return d->length / 1000; +} + +int WavPack::Properties::lengthInMilliseconds() const { return d->length; } @@ -117,6 +124,11 @@ int WavPack::Properties::bitsPerSample() const return d->bitsPerSample; } +bool WavPack::Properties::isLossless() const +{ + return d->lossless; +} + TagLib::uint WavPack::Properties::sampleFrames() const { return d->sampleFrames; @@ -126,12 +138,16 @@ TagLib::uint WavPack::Properties::sampleFrames() const // private members //////////////////////////////////////////////////////////////////////////////// -static const unsigned int sample_rates[] = { - 6000, 8000, 9600, 11025, 12000, 16000, 22050, 24000, - 32000, 44100, 48000, 64000, 88200, 96000, 192000, 0 }; +namespace +{ + const unsigned int sample_rates[] = { + 6000, 8000, 9600, 11025, 12000, 16000, 22050, 24000, + 32000, 44100, 48000, 64000, 88200, 96000, 192000, 0 }; +} #define BYTES_STORED 3 #define MONO_FLAG 4 +#define LOSSLESS_FLAG 8 #define SHIFT_LSB 13 #define SHIFT_MASK (0x1fL << SHIFT_LSB) @@ -144,60 +160,77 @@ static const unsigned int sample_rates[] = { #define FINAL_BLOCK 0x1000 -void WavPack::Properties::read() +void WavPack::Properties::read(File *file, long streamLength) { - if(!d->data.startsWith("wvpk")) - return; + long offset = 0; - d->version = d->data.toShort(8, false); - if(d->version < MIN_STREAM_VERS || d->version > MAX_STREAM_VERS) - return; + while(true) { + file->seek(offset); + const ByteVector data = file->readBlock(32); - const unsigned int flags = d->data.toUInt(24, false); - d->bitsPerSample = ((flags & BYTES_STORED) + 1) * 8 - - ((flags & SHIFT_MASK) >> SHIFT_LSB); - d->sampleRate = sample_rates[(flags & SRATE_MASK) >> SRATE_LSB]; - d->channels = (flags & MONO_FLAG) ? 1 : 2; - - unsigned int samples = d->data.toUInt(12, false); - if(samples == ~0u) { - if(d->file && d->style != Fast) { - samples = seekFinalIndex(); + if(data.size() < 32) { + debug("WavPack::Properties::read() -- data is too short."); + break; } - else { - samples = 0; + + if(!data.startsWith("wvpk")) { + debug("WavPack::Properties::read() -- Block header not found."); + break; } - } - d->length = d->sampleRate > 0 ? (samples + (d->sampleRate / 2)) / d->sampleRate : 0; - d->sampleFrames = samples; - d->bitrate = d->length > 0 ? ((d->streamLength * 8L) / d->length) / 1000 : 0; -} + const uint flags = data.toUInt(24, false); -unsigned int WavPack::Properties::seekFinalIndex() -{ - ByteVector blockID("wvpk", 4); + if(offset == 0) { + d->version = data.toShort(8, false); + if(d->version < MIN_STREAM_VERS || d->version > MAX_STREAM_VERS) + break; - long offset = d->streamLength; - while(offset > 0) { - offset = d->file->rfind(blockID, offset); - if(offset == -1) - return 0; - d->file->seek(offset); - ByteVector data = d->file->readBlock(32); - if(data.size() != 32) - return 0; - const int version = data.toShort(8, false); - if(version < MIN_STREAM_VERS || version > MAX_STREAM_VERS) - continue; - const unsigned int flags = data.toUInt(24, false); - if(!(flags & FINAL_BLOCK)) - return 0; - const unsigned int blockIndex = data.toUInt(16, false); - const unsigned int blockSamples = data.toUInt(20, false); - return blockIndex + blockSamples; + d->bitsPerSample = ((flags & BYTES_STORED) + 1) * 8 - ((flags & SHIFT_MASK) >> SHIFT_LSB); + d->sampleRate = sample_rates[(flags & SRATE_MASK) >> SRATE_LSB]; + d->lossless = !(flags & LOSSLESS_FLAG); + d->sampleFrames = data.toUInt(12, false); + } + + d->channels += (flags & MONO_FLAG) ? 1 : 2; + + if(flags & FINAL_BLOCK) + break; + + const uint blockSize = data.toUInt(4, false); + offset += blockSize + 8; } - return 0; + if(d->sampleFrames == ~0u) + d->sampleFrames = seekFinalIndex(file, streamLength); + + if(d->sampleFrames > 0 && d->sampleRate > 0) { + const double length = d->sampleFrames * 1000.0 / d->sampleRate; + d->length = static_cast(length + 0.5); + d->bitrate = static_cast(streamLength * 8.0 / length + 0.5); + } } +TagLib::uint WavPack::Properties::seekFinalIndex(File *file, long streamLength) +{ + const long offset = file->rfind("wvpk", streamLength); + if(offset == -1) + return 0; + + file->seek(offset); + const ByteVector data = file->readBlock(32); + if(data.size() < 32) + return 0; + + const int version = data.toShort(8, false); + if(version < MIN_STREAM_VERS || version > MAX_STREAM_VERS) + return 0; + + const uint flags = data.toUInt(24, false); + if(!(flags & FINAL_BLOCK)) + return 0; + + const uint blockIndex = data.toUInt(16, false); + const uint blockSamples = data.toUInt(20, false); + + return blockIndex + blockSamples; +} diff --git a/3rdparty/taglib/wavpack/wavpackproperties.h b/3rdparty/taglib/wavpack/wavpackproperties.h index c788fdd50..2955b1d80 100644 --- a/3rdparty/taglib/wavpack/wavpackproperties.h +++ b/3rdparty/taglib/wavpack/wavpackproperties.h @@ -71,22 +71,61 @@ namespace TagLib { */ virtual ~Properties(); - // Reimplementations. - + /*! + * Returns the length of the file in seconds. The length is rounded down to + * the nearest whole second. + * + * \note This method is just an alias of lengthInSeconds(). + * + * \deprecated + */ virtual int length() const; + + /*! + * Returns the length of the file in seconds. The length is rounded down to + * the nearest whole second. + * + * \see lengthInMilliseconds() + */ + // BIC: make virtual + int lengthInSeconds() const; + + /*! + * Returns the length of the file in milliseconds. + * + * \see lengthInSeconds() + */ + // BIC: make virtual + int lengthInMilliseconds() const; + + /*! + * Returns the average bit rate of the file in kb/s. + */ virtual int bitrate() const; /*! * Returns the sample rate in Hz. 0 means unknown or custom. */ virtual int sampleRate() const; - + + /*! + * Returns the number of audio channels. + */ virtual int channels() const; /*! - * Returns number of bits per sample. + * Returns the number of bits per audio sample. */ int bitsPerSample() const; + + /*! + * Returns whether or not the file is lossless encoded. + */ + bool isLossless() const; + + /*! + * Returns the total number of audio samples in file. + */ uint sampleFrames() const; /*! @@ -98,8 +137,8 @@ namespace TagLib { Properties(const Properties &); Properties &operator=(const Properties &); - void read(); - unsigned int seekFinalIndex(); + void read(File *file, long streamLength); + uint seekFinalIndex(File *file, long streamLength); class PropertiesPrivate; PropertiesPrivate *d; diff --git a/3rdparty/taglib/xm/xmfile.cpp b/3rdparty/taglib/xm/xmfile.cpp index d8373a781..7fe6f108d 100644 --- a/3rdparty/taglib/xm/xmfile.cpp +++ b/3rdparty/taglib/xm/xmfile.cpp @@ -329,7 +329,7 @@ public: uint read(TagLib::File &file, uint limit) { uint sumcount = 0; - for(List::Iterator i = m_readers.begin(); + for(List::ConstIterator i = m_readers.begin(); limit > 0 && i != m_readers.end(); ++ i) { uint count = (*i)->read(file, limit); limit -= count; @@ -403,97 +403,90 @@ bool XM::File::save() debug("XM::File::save() - Cannot save to a read only file."); return false; } + seek(17); writeString(d->tag.title(), 20); - seek(1, Current); + + seek(38); writeString(d->tag.trackerName(), 20); - seek(2, Current); + + seek(60); ulong headerSize = 0; if(!readU32L(headerSize)) return false; - seek(2+2+2, Current); + seek(70); ushort patternCount = 0; ushort instrumentCount = 0; if(!readU16L(patternCount) || !readU16L(instrumentCount)) return false; - seek(60 + headerSize); + long pos = 60 + headerSize; // should be offset_t in taglib2. // need to read patterns again in order to seek to the instruments: for(ushort i = 0; i < patternCount; ++ i) { + seek(pos); ulong patternHeaderLength = 0; if(!readU32L(patternHeaderLength) || patternHeaderLength < 4) return false; + seek(pos + 7); ushort dataSize = 0; - StructReader pattern; - pattern.skip(3).u16L(dataSize); - - uint count = pattern.read(*this, patternHeaderLength - 4U); - if(count != std::min(patternHeaderLength - 4U, (ulong)pattern.size())) + if (!readU16L(dataSize)) return false; - seek(patternHeaderLength - (4 + count) + dataSize, Current); + pos += patternHeaderLength + dataSize; } - StringList lines = d->tag.comment().split("\n"); + const StringList lines = d->tag.comment().split("\n"); uint sampleNameIndex = instrumentCount; for(ushort i = 0; i < instrumentCount; ++ i) { + seek(pos); ulong instrumentHeaderSize = 0; if(!readU32L(instrumentHeaderSize) || instrumentHeaderSize < 4) return false; - uint len = std::min(22UL, instrumentHeaderSize - 4U); + seek(pos + 4); + const uint len = std::min(22UL, instrumentHeaderSize - 4U); if(i >= lines.size()) writeString(String::null, len); else writeString(lines[i], len); - long offset = 0; + ushort sampleCount = 0; if(instrumentHeaderSize >= 29U) { - ushort sampleCount = 0; - seek(1, Current); + seek(pos + 27); if(!readU16L(sampleCount)) return false; + } - if(sampleCount > 0) { - ulong sampleHeaderSize = 0; - if(instrumentHeaderSize < 33U || !readU32L(sampleHeaderSize)) + ulong sampleHeaderSize = 0; + if(sampleCount > 0) { + seek(pos + 29); + if(instrumentHeaderSize < 33U || !readU32L(sampleHeaderSize)) + return false; + } + + pos += instrumentHeaderSize; + + for(ushort j = 0; j < sampleCount; ++ j) { + if(sampleHeaderSize > 4U) { + seek(pos); + ulong sampleLength = 0; + if(!readU32L(sampleLength)) return false; - // skip unhandeled header proportion: - seek(instrumentHeaderSize - 33, Current); - for(ushort j = 0; j < sampleCount; ++ j) { - if(sampleHeaderSize > 4U) { - ulong sampleLength = 0; - if(!readU32L(sampleLength)) - return false; - offset += sampleLength; - - seek(std::min(sampleHeaderSize, 14UL), Current); - if(sampleHeaderSize > 18U) { - uint len = std::min(sampleHeaderSize - 18U, 22UL); - if(sampleNameIndex >= lines.size()) - writeString(String::null, len); - else - writeString(lines[sampleNameIndex ++], len); - seek(sampleHeaderSize - (18U + len), Current); - } - } - else { - seek(sampleHeaderSize, Current); - } + if(sampleHeaderSize > 18U) { + seek(pos + 18); + const uint len = std::min(sampleHeaderSize - 18U, 22UL); + if(sampleNameIndex >= lines.size()) + writeString(String::null, len); + else + writeString(lines[sampleNameIndex ++], len); } } - else { - offset = instrumentHeaderSize - 29; - } + pos += sampleHeaderSize; } - else { - offset = instrumentHeaderSize - (4 + len); - } - seek(offset, Current); } return true; diff --git a/3rdparty/taglib/xm/xmproperties.cpp b/3rdparty/taglib/xm/xmproperties.cpp index 45da02bd3..3d4a3c382 100644 --- a/3rdparty/taglib/xm/xmproperties.cpp +++ b/3rdparty/taglib/xm/xmproperties.cpp @@ -69,6 +69,16 @@ int XM::Properties::length() const return 0; } +int XM::Properties::lengthInSeconds() const +{ + return 0; +} + +int XM::Properties::lengthInMilliseconds() const +{ + return 0; +} + int XM::Properties::bitrate() const { return 0; diff --git a/3rdparty/taglib/xm/xmproperties.h b/3rdparty/taglib/xm/xmproperties.h index 9b6624a77..f929982ba 100644 --- a/3rdparty/taglib/xm/xmproperties.h +++ b/3rdparty/taglib/xm/xmproperties.h @@ -28,7 +28,7 @@ namespace TagLib { namespace XM { - class Properties : public AudioProperties { + class TAGLIB_EXPORT Properties : public AudioProperties { friend class File; public: /*! Flag bits. */ @@ -39,10 +39,12 @@ namespace TagLib { Properties(AudioProperties::ReadStyle propertiesStyle); virtual ~Properties(); - int length() const; - int bitrate() const; - int sampleRate() const; - int channels() const; + int length() const; + int lengthInSeconds() const; + int lengthInMilliseconds() const; + int bitrate() const; + int sampleRate() const; + int channels() const; ushort lengthInPatterns() const; ushort version() const; diff --git a/CMakeLists.txt b/CMakeLists.txt index 7b349065e..e4cc4cdff 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -94,7 +94,7 @@ find_path(SPARSEHASH_INCLUDE_DIRS google/sparsetable) option(USE_BUILTIN_TAGLIB "If the system's version of Taglib is too old, compile our builtin version instead" ON) if (USE_BUILTIN_TAGLIB AND TAGLIB_VERSION VERSION_LESS 1.8) message(STATUS "Using builtin taglib because your system's version is too old") - set(TAGLIB_VERSION 1.9.1) + set(TAGLIB_VERSION 1.10.0) set(TAGLIB_INCLUDE_DIRS "${CMAKE_BINARY_DIR}/3rdparty/taglib/headers/taglib/;${CMAKE_BINARY_DIR}/3rdparty/taglib/headers/") set(TAGLIB_LIBRARY_DIRS "") set(TAGLIB_LIBRARIES tag)