diff --git a/3rdparty/taglib/CMakeLists.txt b/3rdparty/taglib/CMakeLists.txt index 1ee3e8576..f5b566d7e 100644 --- a/3rdparty/taglib/CMakeLists.txt +++ b/3rdparty/taglib/CMakeLists.txt @@ -47,6 +47,8 @@ include_directories( ${CMAKE_CURRENT_SOURCE_DIR}/s3m ${CMAKE_CURRENT_SOURCE_DIR}/it ${CMAKE_CURRENT_SOURCE_DIR}/xm + ${CMAKE_CURRENT_SOURCE_DIR}/dsf + ${CMAKE_CURRENT_SOURCE_DIR}/dsdiff ${CMAKE_SOURCE_DIR}/3rdparty ) @@ -161,6 +163,11 @@ set(tag_HDRS s3m/s3mproperties.h xm/xmfile.h xm/xmproperties.h + dsf/dsffile.h + dsf/dsfproperties.h + dsdiff/dsdifffile.h + dsdiff/dsdiffproperties.h + dsdiff/dsdiffdiintag.h ) set(mpeg_SRCS @@ -316,6 +323,17 @@ set(xm_SRCS xm/xmproperties.cpp ) +set(dsf_SRCS + dsf/dsffile.cpp + dsf/dsfproperties.cpp +) + +set(dsdiff_SRCS + dsdiff/dsdifffile.cpp + dsdiff/dsdiffproperties.cpp + dsdiff/dsdiffdiintag.cpp +) + set(toolkit_SRCS toolkit/tstring.cpp toolkit/tstringlist.cpp @@ -347,7 +365,7 @@ 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} + ${asf_SRCS} ${mp4_SRCS} ${mod_SRCS} ${s3m_SRCS} ${it_SRCS} ${xm_SRCS} ${opus_SRCS} ${dsf_SRCS} ${dsdiff_SRCS} ${zlib_SRCS} tag.cpp tagunion.cpp diff --git a/3rdparty/taglib/audioproperties.cpp b/3rdparty/taglib/audioproperties.cpp index c29051a64..94cddc72b 100644 --- a/3rdparty/taglib/audioproperties.cpp +++ b/3rdparty/taglib/audioproperties.cpp @@ -38,6 +38,8 @@ #include "vorbisproperties.h" #include "wavproperties.h" #include "wavpackproperties.h" +#include "dsfproperties.h" +#include "dsdiffproperties.h" #include "audioproperties.h" @@ -73,6 +75,10 @@ using namespace TagLib; return dynamic_cast(this)->function_name(); \ else if(dynamic_cast(this)) \ return dynamic_cast(this)->function_name(); \ + else if(dynamic_cast(this)) \ + return dynamic_cast(this)->function_name(); \ + else if(dynamic_cast(this)) \ + return dynamic_cast(this)->function_name(); \ else \ return (default_value); diff --git a/3rdparty/taglib/dsdiff/dsdiffdiintag.cpp b/3rdparty/taglib/dsdiff/dsdiffdiintag.cpp new file mode 100644 index 000000000..c027410a6 --- /dev/null +++ b/3rdparty/taglib/dsdiff/dsdiffdiintag.cpp @@ -0,0 +1,162 @@ +/*************************************************************************** + copyright : (C) 2016 by Damien Plisson, Audirvana + email : damien78@audirvana.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/ * + ***************************************************************************/ + +#include "dsdiffdiintag.h" +#include "tstringlist.h" +#include "tpropertymap.h" + +using namespace TagLib; +using namespace DSDIFF::DIIN; + +class DSDIFF::DIIN::Tag::TagPrivate +{ +public: + TagPrivate() + { + } + + String title; + String artist; +}; + +DSDIFF::DIIN::Tag::Tag() : TagLib::Tag() +{ + d = new TagPrivate; +} + +DSDIFF::DIIN::Tag::~Tag() +{ + delete d; +} + +String DSDIFF::DIIN::Tag::title() const +{ + return d->title; +} + +String DSDIFF::DIIN::Tag::artist() const +{ + return d->artist; +} + +String DSDIFF::DIIN::Tag::album() const +{ + return String(); +} + +String DSDIFF::DIIN::Tag::comment() const +{ + return String(); +} + +String DSDIFF::DIIN::Tag::genre() const +{ + return String(); +} + +unsigned int DSDIFF::DIIN::Tag::year() const +{ + return 0; +} + +unsigned int DSDIFF::DIIN::Tag::track() const +{ + return 0; +} + +void DSDIFF::DIIN::Tag::setTitle(const String &title) +{ + if(title.isNull() || title.isEmpty()) + d->title = String(); + else + d->title = title; +} + +void DSDIFF::DIIN::Tag::setArtist(const String &artist) +{ + if(artist.isNull() || artist.isEmpty()) + d->artist = String(); + else + d->artist = artist; +} + +void DSDIFF::DIIN::Tag::setAlbum(const String &) +{ +} + +void DSDIFF::DIIN::Tag::setComment(const String &) +{ +} + +void DSDIFF::DIIN::Tag::setGenre(const String &) +{ +} + +void DSDIFF::DIIN::Tag::setYear(unsigned int) +{ +} + +void DSDIFF::DIIN::Tag::setTrack(unsigned int) +{ +} + +PropertyMap DSDIFF::DIIN::Tag::properties() const +{ + PropertyMap properties; + properties["TITLE"] = d->title; + properties["ARTIST"] = d->artist; + return properties; +} + +PropertyMap DSDIFF::DIIN::Tag::setProperties(const PropertyMap &origProps) +{ + PropertyMap properties(origProps); + properties.removeEmpty(); + StringList oneValueSet; + + if(properties.contains("TITLE")) { + d->title = properties["TITLE"].front(); + oneValueSet.append("TITLE"); + } else + d->title = String(); + + if(properties.contains("ARTIST")) { + d->artist = properties["ARTIST"].front(); + oneValueSet.append("ARTIST"); + } else + d->artist = String(); + + // 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) { + if(properties[*it].size() == 1) + properties.erase(*it); + else + properties[*it].erase(properties[*it].begin()); + } + + return properties; +} + diff --git a/3rdparty/taglib/dsdiff/dsdiffdiintag.h b/3rdparty/taglib/dsdiff/dsdiffdiintag.h new file mode 100644 index 000000000..0eff545e2 --- /dev/null +++ b/3rdparty/taglib/dsdiff/dsdiffdiintag.h @@ -0,0 +1,150 @@ +/*************************************************************************** + copyright : (C) 2016 by Damien Plisson, Audirvana + email : damien78@audirvana.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_DSDIFFDIINTAG_H +#define TAGLIB_DSDIFFDIINTAG_H + +#include "tag.h" + +namespace TagLib { + + namespace DSDIFF { + + namespace DIIN { + + /*! + * Tags from the Edited Master Chunk Info + * + * Only Title and Artist tags are supported + */ + class TAGLIB_EXPORT Tag : public TagLib::Tag + { + public: + Tag(); + virtual ~Tag(); + + /*! + * Returns the track name; if no track name is present in the tag + * String() will be returned. + */ + String title() const; + + /*! + * Returns the artist name; if no artist name is present in the tag + * String() will be returned. + */ + String artist() const; + + /*! + * Not supported. Therefore always returns String(). + */ + String album() const; + + /*! + * Not supported. Therefore always returns String(). + */ + String comment() const; + + /*! + * Not supported. Therefore always returns String(). + */ + String genre() const; + + /*! + * Not supported. Therefore always returns 0. + */ + unsigned int year() const; + + /*! + * Not supported. Therefore always returns 0. + */ + unsigned int track() const; + + /*! + * Sets the title to \a title. If \a title is String() then this + * value will be cleared. + */ + void setTitle(const String &title); + + /*! + * Sets the artist to \a artist. If \a artist is String() then this + * value will be cleared. + */ + void setArtist(const String &artist); + + /*! + * Not supported and therefore ignored. + */ + void setAlbum(const String &album); + + /*! + * Not supported and therefore ignored. + */ + void setComment(const String &comment); + + /*! + * Not supported and therefore ignored. + */ + void setGenre(const String &genre); + + /*! + * Not supported and therefore ignored. + */ + void setYear(unsigned int year); + + /*! + * Not supported and therefore ignored. + */ + void setTrack(unsigned int track); + + /*! + * Implements the unified property interface -- export function. + * Since the DIIN tag is very limited, the exported map is as well. + */ + PropertyMap properties() const; + + /*! + * Implements the unified property interface -- import function. + * Because of the limitations of the DIIN file tag, any tags besides + * TITLE and ARTIST, will be + * returned. Additionally, if the map contains tags with multiple values, + * all but the first will be contained in the returned map of unsupported + * properties. + */ + PropertyMap setProperties(const PropertyMap &); + + private: + Tag(const Tag &); + Tag &operator=(const Tag &); + + class TagPrivate; + TagPrivate *d; + }; + } + } +} + +#endif + diff --git a/3rdparty/taglib/dsdiff/dsdifffile.cpp b/3rdparty/taglib/dsdiff/dsdifffile.cpp new file mode 100644 index 000000000..73b2dbb25 --- /dev/null +++ b/3rdparty/taglib/dsdiff/dsdifffile.cpp @@ -0,0 +1,812 @@ +/*************************************************************************** + copyright : (C) 2016 by Damien Plisson, Audirvana + email : damien78@audirvana.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/ * + ***************************************************************************/ + +#include +#include +#include +#include +#include +#include + +#include "tagunion.h" +#include "dsdifffile.h" + +using namespace TagLib; + +struct Chunk64 +{ + ByteVector name; + unsigned long long offset; + unsigned long long size; + char padding; +}; + +namespace +{ + enum { + ID3v2Index = 0, + DIINIndex = 1 + }; + enum { + PROPChunk = 0, + DIINChunk = 1 + }; +} + +class DSDIFF::File::FilePrivate +{ +public: + FilePrivate() : + endianness(BigEndian), + size(0), + isID3InPropChunk(false), + duplicateID3V2chunkIndex(-1), + properties(0), + id3v2TagChunkID("ID3 "), + hasID3v2(false), + hasDiin(false) + { + childChunkIndex[ID3v2Index] = -1; + childChunkIndex[DIINIndex] = -1; + } + + ~FilePrivate() + { + delete properties; + } + + Endianness endianness; + ByteVector type; + unsigned long long size; + ByteVector format; + std::vector chunks; + std::vector childChunks[2]; + int childChunkIndex[2]; + bool isID3InPropChunk; // Two possibilities can be found: ID3V2 chunk inside PROP chunk or at root level + int duplicateID3V2chunkIndex; // 2 ID3 chunks are present. This is then the index of the one in + // PROP chunk that will be removed upon next save to remove duplicates. + + Properties *properties; + + TagUnion tag; + + ByteVector id3v2TagChunkID; + + bool hasID3v2; + bool hasDiin; +}; + +//////////////////////////////////////////////////////////////////////////////// +// static members +//////////////////////////////////////////////////////////////////////////////// + +bool DSDIFF::File::isSupported(IOStream *stream) +{ + // A DSDIFF file has to start with "FRM8????????DSD ". + + const ByteVector id = Utils::readHeader(stream, 16, false); + return (id.startsWith("FRM8") && id.containsAt("DSD ", 12)); +} + +//////////////////////////////////////////////////////////////////////////////// +// public members +//////////////////////////////////////////////////////////////////////////////// + +DSDIFF::File::File(FileName file, bool readProperties, + Properties::ReadStyle propertiesStyle) : TagLib::File(file) +{ + d = new FilePrivate; + d->endianness = BigEndian; + if(isOpen()) + read(readProperties, propertiesStyle); +} + +DSDIFF::File::File(IOStream *stream, bool readProperties, + Properties::ReadStyle propertiesStyle) : TagLib::File(stream) +{ + d = new FilePrivate; + d->endianness = BigEndian; + if(isOpen()) + read(readProperties, propertiesStyle); +} + +DSDIFF::File::~File() +{ + delete d; +} + +TagLib::Tag *DSDIFF::File::tag() const +{ + return &d->tag; +} + +ID3v2::Tag *DSDIFF::File::ID3v2Tag() const +{ + return d->tag.access(ID3v2Index, false); +} + +bool DSDIFF::File::hasID3v2Tag() const +{ + return d->hasID3v2; +} + +DSDIFF::DIIN::Tag *DSDIFF::File::DIINTag() const +{ + return d->tag.access(DIINIndex, false); +} + +bool DSDIFF::File::hasDIINTag() const +{ + return d->hasDiin; +} + +PropertyMap DSDIFF::File::properties() const +{ + if(d->hasID3v2) + return d->tag.access(ID3v2Index, false)->properties(); + + return PropertyMap(); +} + +void DSDIFF::File::removeUnsupportedProperties(const StringList &unsupported) +{ + if(d->hasID3v2) + d->tag.access(ID3v2Index, false)->removeUnsupportedProperties(unsupported); + + if(d->hasDiin) + d->tag.access(DIINIndex, false)->removeUnsupportedProperties(unsupported); +} + +PropertyMap DSDIFF::File::setProperties(const PropertyMap &properties) +{ + return d->tag.access(ID3v2Index, true)->setProperties(properties); +} + +DSDIFF::Properties *DSDIFF::File::audioProperties() const +{ + return d->properties; +} + +bool DSDIFF::File::save() +{ + if(readOnly()) { + debug("DSDIFF::File::save() -- File is read only."); + return false; + } + + if(!isValid()) { + debug("DSDIFF::File::save() -- Trying to save invalid file."); + return false; + } + + // First: save ID3V2 chunk + ID3v2::Tag *id3v2Tag = d->tag.access(ID3v2Index, false); + if(d->isID3InPropChunk) { + if(id3v2Tag != NULL && !id3v2Tag->isEmpty()) { + setChildChunkData(d->id3v2TagChunkID, id3v2Tag->render(), PROPChunk); + d->hasID3v2 = true; + } + else { + // Empty tag: remove it + setChildChunkData(d->id3v2TagChunkID, ByteVector(), PROPChunk); + d->hasID3v2 = false; + } + } + else { + if(id3v2Tag != NULL && !id3v2Tag->isEmpty()) { + setRootChunkData(d->id3v2TagChunkID, id3v2Tag->render()); + d->hasID3v2 = true; + } + else { + // Empty tag: remove it + setRootChunkData(d->id3v2TagChunkID, ByteVector()); + d->hasID3v2 = false; + } + } + + // Second: save the DIIN chunk + if(d->hasDiin) { + DSDIFF::DIIN::Tag *diinTag = d->tag.access(DIINIndex, false); + + if(!diinTag->title().isNull() && !diinTag->title().isEmpty()) { + ByteVector diinTitle; + diinTitle.append(ByteVector::fromUInt(diinTag->title().size(), d->endianness == BigEndian)); + diinTitle.append(ByteVector::fromCString(diinTag->title().toCString())); + setChildChunkData("DITI", diinTitle, DIINChunk); + } + else + setChildChunkData("DITI", ByteVector(), DIINChunk); + + if(!diinTag->artist().isNull() && !diinTag->artist().isEmpty()) { + ByteVector diinArtist; + diinArtist.append(ByteVector::fromUInt(diinTag->artist().size(), d->endianness == BigEndian)); + diinArtist.append(ByteVector::fromCString(diinTag->artist().toCString())); + setChildChunkData("DIAR", diinArtist, DIINChunk); + } + else + setChildChunkData("DIAR", ByteVector(), DIINChunk); + } + + // Third: remove the duplicate ID3V2 chunk (inside PROP chunk) if any + if(d->duplicateID3V2chunkIndex>=0) { + setChildChunkData(d->duplicateID3V2chunkIndex, ByteVector(), PROPChunk); + d->duplicateID3V2chunkIndex = -1; + } + + return true; +} + +//////////////////////////////////////////////////////////////////////////////// +// private members +//////////////////////////////////////////////////////////////////////////////// + +void DSDIFF::File::setRootChunkData(unsigned int i, const ByteVector &data) +{ + if(data.isNull() || data.isEmpty()) { + // Null data: remove chunk + // Update global size + unsigned long long removedChunkTotalSize = d->chunks[i].size + d->chunks[i].padding + 12; + d->size -= removedChunkTotalSize; + insert(ByteVector::fromLongLong(d->size, d->endianness == BigEndian), 4, 8); + + removeBlock(d->chunks[i].offset - 12, removedChunkTotalSize); + + // Update the internal offsets + for(unsigned long r = i + 1; r < d->chunks.size(); r++) + d->chunks[r].offset = d->chunks[r - 1].offset + 12 + + d->chunks[r - 1].size + d->chunks[r - 1].padding; + + d->chunks.erase(d->chunks.begin() + i); + } + else { + // Non null data: update chunk + // First we update the global size + d->size += ((data.size() + 1) & ~1) - (d->chunks[i].size + d->chunks[i].padding); + insert(ByteVector::fromLongLong(d->size, d->endianness == BigEndian), 4, 8); + + // Now update the specific chunk + writeChunk(d->chunks[i].name, + data, + d->chunks[i].offset - 12, + d->chunks[i].size + d->chunks[i].padding + 12); + + d->chunks[i].size = data.size(); + d->chunks[i].padding = (data.size() & 0x01) ? 1 : 0; + + // Finally update the internal offsets + updateRootChunksStructure(i + 1); + } +} + +void DSDIFF::File::setRootChunkData(const ByteVector &name, const ByteVector &data) +{ + if(d->chunks.size() == 0) { + debug("DSDIFF::File::setPropChunkData - No valid chunks found."); + return; + } + + for(unsigned int i = 0; i < d->chunks.size(); i++) { + if(d->chunks[i].name == name) { + setRootChunkData(i, data); + return; + } + } + + // Couldn't find an existing chunk, so let's create a new one. + unsigned int i = d->chunks.size() - 1; + unsigned long offset = d->chunks[i].offset + d->chunks[i].size + d->chunks[i].padding; + + // First we update the global size + d->size += (offset & 1) + ((data.size() + 1) & ~1) + 12; + insert(ByteVector::fromLongLong(d->size, d->endianness == BigEndian), 4, 8); + + // Now add the chunk to the file + writeChunk(name, + data, + offset, + std::max(0, length() - offset), + (offset & 1) ? 1 : 0); + + Chunk64 chunk; + chunk.name = name; + chunk.size = data.size(); + chunk.offset = offset + 12; + chunk.padding = (data.size() & 0x01) ? 1 : 0; + + d->chunks.push_back(chunk); +} + +void DSDIFF::File::setChildChunkData(unsigned int i, + const ByteVector &data, + unsigned int childChunkNum) +{ + std::vector &childChunks = d->childChunks[childChunkNum]; + + if(data.isNull() || data.isEmpty()) { + // Null data: remove chunk + // Update global size + unsigned long long removedChunkTotalSize = childChunks[i].size + childChunks[i].padding + 12; + d->size -= removedChunkTotalSize; + insert(ByteVector::fromLongLong(d->size, d->endianness == BigEndian), 4, 8); + // Update child chunk size + d->chunks[d->childChunkIndex[childChunkNum]].size -= removedChunkTotalSize; + insert(ByteVector::fromLongLong(d->chunks[d->childChunkIndex[childChunkNum]].size, + d->endianness == BigEndian), + d->chunks[d->childChunkIndex[childChunkNum]].offset - 8, 8); + // Remove the chunk + removeBlock(childChunks[i].offset - 12, removedChunkTotalSize); + + // Update the internal offsets + // For child chunks + if((i + 1) < childChunks.size()) { + childChunks[i + 1].offset = childChunks[i].offset; + i++; + for(i++; i < childChunks.size(); i++) + childChunks[i].offset = childChunks[i - 1].offset + 12 + + childChunks[i - 1].size + childChunks[i - 1].padding; + } + + // And for root chunks + for(i = d->childChunkIndex[childChunkNum] + 1; i < d->chunks.size(); i++) + d->chunks[i].offset = d->chunks[i - 1].offset + 12 + + d->chunks[i - 1].size + d->chunks[i - 1].padding; + + childChunks.erase(childChunks.begin() + i); + } + else { + // Non null data: update chunk + // First we update the global size + d->size += ((data.size() + 1) & ~1) - (childChunks[i].size + childChunks[i].padding); + insert(ByteVector::fromLongLong(d->size, d->endianness == BigEndian), 4, 8); + // And the PROP chunk size + d->chunks[d->childChunkIndex[childChunkNum]].size += ((data.size() + 1) & ~1) + - (childChunks[i].size + childChunks[i].padding); + insert(ByteVector::fromLongLong(d->chunks[d->childChunkIndex[childChunkNum]].size, + d->endianness == BigEndian), + d->chunks[d->childChunkIndex[childChunkNum]].offset - 8, 8); + + // Now update the specific chunk + writeChunk(childChunks[i].name, + data, + childChunks[i].offset - 12, + childChunks[i].size + childChunks[i].padding + 12); + + childChunks[i].size = data.size(); + childChunks[i].padding = (data.size() & 0x01) ? 1 : 0; + + // Now update the internal offsets + // For child Chunks + for(i++; i < childChunks.size(); i++) + childChunks[i].offset = childChunks[i - 1].offset + 12 + + childChunks[i - 1].size + childChunks[i - 1].padding; + + // And for root chunks + updateRootChunksStructure(d->childChunkIndex[childChunkNum] + 1); + } +} + +void DSDIFF::File::setChildChunkData(const ByteVector &name, + const ByteVector &data, + unsigned int childChunkNum) +{ + std::vector &childChunks = d->childChunks[childChunkNum]; + + if(childChunks.size() == 0) { + debug("DSDIFF::File::setPropChunkData - No valid chunks found."); + return; + } + + for(unsigned int i = 0; i < childChunks.size(); i++) { + if(childChunks[i].name == name) { + setChildChunkData(i, data, childChunkNum); + return; + } + } + + // Do not attempt to remove a non existing chunk + if(data.isNull() || data.isEmpty()) + return; + + // Couldn't find an existing chunk, so let's create a new one. + unsigned int i = childChunks.size() - 1; + unsigned long offset = childChunks[i].offset + childChunks[i].size + childChunks[i].padding; + + // First we update the global size + d->size += (offset & 1) + ((data.size() + 1) & ~1) + 12; + insert(ByteVector::fromLongLong(d->size, d->endianness == BigEndian), 4, 8); + // And the child chunk size + d->chunks[d->childChunkIndex[childChunkNum]].size += (offset & 1) + + ((data.size() + 1) & ~1) + 12; + insert(ByteVector::fromLongLong(d->chunks[d->childChunkIndex[childChunkNum]].size, + d->endianness == BigEndian), + d->chunks[d->childChunkIndex[childChunkNum]].offset - 8, 8); + + // Now add the chunk to the file + unsigned long long nextRootChunkIdx = length(); + if((d->childChunkIndex[childChunkNum] + 1) < static_cast(d->chunks.size())) + nextRootChunkIdx = d->chunks[d->childChunkIndex[childChunkNum] + 1].offset - 12; + + writeChunk(name, data, offset, + std::max(0, nextRootChunkIdx - offset), + (offset & 1) ? 1 : 0); + + // For root chunks + updateRootChunksStructure(d->childChunkIndex[childChunkNum] + 1); + + Chunk64 chunk; + chunk.name = name; + chunk.size = data.size(); + chunk.offset = offset + 12; + chunk.padding = (data.size() & 0x01) ? 1 : 0; + + childChunks.push_back(chunk); +} + +static bool isValidChunkID(const ByteVector &name) +{ + if(name.size() != 4) + return false; + + for(int i = 0; i < 4; i++) { + if(name[i] < 32 || name[i] > 127) + return false; + } + + return true; +} + +void DSDIFF::File::updateRootChunksStructure(unsigned int startingChunk) +{ + for(unsigned int i = startingChunk; i < d->chunks.size(); i++) + d->chunks[i].offset = d->chunks[i - 1].offset + 12 + + d->chunks[i - 1].size + d->chunks[i - 1].padding; + + // Update childchunks structure as well + if(d->childChunkIndex[PROPChunk] >= static_cast(startingChunk)) { + std::vector &childChunksToUpdate = d->childChunks[PROPChunk]; + if(childChunksToUpdate.size() > 0) { + childChunksToUpdate[0].offset = d->chunks[d->childChunkIndex[PROPChunk]].offset + 12; + for(unsigned int i = 1; i < childChunksToUpdate.size(); i++) + childChunksToUpdate[i].offset = childChunksToUpdate[i - 1].offset + 12 + + childChunksToUpdate[i - 1].size + childChunksToUpdate[i - 1].padding; + } + } + if(d->childChunkIndex[DIINChunk] >= static_cast(startingChunk)) { + std::vector &childChunksToUpdate = d->childChunks[DIINChunk]; + if(childChunksToUpdate.size() > 0) { + childChunksToUpdate[0].offset = d->chunks[d->childChunkIndex[DIINChunk]].offset + 12; + for(unsigned int i = 1; i < childChunksToUpdate.size(); i++) + childChunksToUpdate[i].offset = childChunksToUpdate[i - 1].offset + 12 + + childChunksToUpdate[i - 1].size + childChunksToUpdate[i - 1].padding; + } + } +} + +void DSDIFF::File::read(bool readProperties, Properties::ReadStyle propertiesStyle) +{ + bool bigEndian = (d->endianness == BigEndian); + + d->type = readBlock(4); + d->size = readBlock(8).toLongLong(bigEndian); + d->format = readBlock(4); + + // + 12: chunk header at least, fix for additional junk bytes + while(tell() + 12 <= length()) { + ByteVector chunkName = readBlock(4); + unsigned long long chunkSize = readBlock(8).toLongLong(bigEndian); + + if(!isValidChunkID(chunkName)) { + debug("DSDIFF::File::read() -- Chunk '" + chunkName + "' has invalid ID"); + setValid(false); + break; + } + + if(static_cast(tell()) + chunkSize > static_cast(length())) { + debug("DSDIFF::File::read() -- Chunk '" + chunkName + + "' has invalid size (larger than the file size)"); + setValid(false); + break; + } + + Chunk64 chunk; + chunk.name = chunkName; + chunk.size = chunkSize; + chunk.offset = tell(); + + seek(chunk.size, Current); + + // Check padding + chunk.padding = 0; + long uPosNotPadded = tell(); + if((uPosNotPadded & 0x01) != 0) { + ByteVector iByte = readBlock(1); + if((iByte.size() != 1) || (iByte[0] != 0)) + // Not well formed, re-seek + seek(uPosNotPadded, Beginning); + else + chunk.padding = 1; + } + d->chunks.push_back(chunk); + } + + unsigned long long lengthDSDSamplesTimeChannels = 0; // For DSD uncompressed + unsigned long long audioDataSizeinBytes = 0; // For computing bitrate + unsigned long dstNumFrames = 0; // For DST compressed frames + unsigned short dstFrameRate = 0; // For DST compressed frames + + for(unsigned int i = 0; i < d->chunks.size(); i++) { + if(d->chunks[i].name == "DSD ") { + lengthDSDSamplesTimeChannels = d->chunks[i].size * 8; + audioDataSizeinBytes = d->chunks[i].size; + } + else if(d->chunks[i].name == "DST ") { + // Now decode the chunks inside the DST chunk to read the DST Frame Information one + long long dstChunkEnd = d->chunks[i].offset + d->chunks[i].size; + seek(d->chunks[i].offset); + + audioDataSizeinBytes = d->chunks[i].size; + + while(tell() + 12 <= dstChunkEnd) { + ByteVector dstChunkName = readBlock(4); + long long dstChunkSize = readBlock(8).toLongLong(bigEndian); + + if(!isValidChunkID(dstChunkName)) { + debug("DSDIFF::File::read() -- DST Chunk '" + dstChunkName + "' has invalid ID"); + setValid(false); + break; + } + + if(static_cast(tell()) + dstChunkSize > dstChunkEnd) { + debug("DSDIFF::File::read() -- DST Chunk '" + dstChunkName + + "' has invalid size (larger than the DST chunk)"); + setValid(false); + break; + } + + if(dstChunkName == "FRTE") { + // Found the DST frame information chunk + dstNumFrames = readBlock(4).toUInt(bigEndian); + dstFrameRate = readBlock(2).toUShort(bigEndian); + break; // Found the wanted one, no need to look at the others + } + + seek(dstChunkSize, Current); + + // Check padding + long uPosNotPadded = tell(); + if((uPosNotPadded & 0x01) != 0) { + ByteVector iByte = readBlock(1); + if((iByte.size() != 1) || (iByte[0] != 0)) + // Not well formed, re-seek + seek(uPosNotPadded, Beginning); + } + } + } + else if(d->chunks[i].name == "PROP") { + d->childChunkIndex[PROPChunk] = i; + // Now decodes the chunks inside the PROP chunk + long long propChunkEnd = d->chunks[i].offset + d->chunks[i].size; + seek(d->chunks[i].offset + 4); // +4 to remove the 'SND ' marker at beginning of 'PROP' chunk + while(tell() + 12 <= propChunkEnd) { + ByteVector propChunkName = readBlock(4); + long long propChunkSize = readBlock(8).toLongLong(bigEndian); + + if(!isValidChunkID(propChunkName)) { + debug("DSDIFF::File::read() -- PROP Chunk '" + propChunkName + "' has invalid ID"); + setValid(false); + break; + } + + if(static_cast(tell()) + propChunkSize > propChunkEnd) { + debug("DSDIFF::File::read() -- PROP Chunk '" + propChunkName + + "' has invalid size (larger than the PROP chunk)"); + setValid(false); + break; + } + + Chunk64 chunk; + chunk.name = propChunkName; + chunk.size = propChunkSize; + chunk.offset = tell(); + + seek(chunk.size, Current); + + // Check padding + chunk.padding = 0; + long uPosNotPadded = tell(); + if((uPosNotPadded & 0x01) != 0) { + ByteVector iByte = readBlock(1); + if((iByte.size() != 1) || (iByte[0] != 0)) + // Not well formed, re-seek + seek(uPosNotPadded, Beginning); + else + chunk.padding = 1; + } + d->childChunks[PROPChunk].push_back(chunk); + } + } + else if(d->chunks[i].name == "DIIN") { + d->childChunkIndex[DIINChunk] = i; + d->hasDiin = true; + // Now decode the chunks inside the DIIN chunk + long long diinChunkEnd = d->chunks[i].offset + d->chunks[i].size; + seek(d->chunks[i].offset); + + while(tell() + 12 <= diinChunkEnd) { + ByteVector diinChunkName = readBlock(4); + long long diinChunkSize = readBlock(8).toLongLong(bigEndian); + + if(!isValidChunkID(diinChunkName)) { + debug("DSDIFF::File::read() -- DIIN Chunk '" + diinChunkName + "' has invalid ID"); + setValid(false); + break; + } + + if(static_cast(tell()) + diinChunkSize > diinChunkEnd) { + debug("DSDIFF::File::read() -- DIIN Chunk '" + diinChunkName + + "' has invalid size (larger than the DIIN chunk)"); + setValid(false); + break; + } + + Chunk64 chunk; + chunk.name = diinChunkName; + chunk.size = diinChunkSize; + chunk.offset = tell(); + + seek(chunk.size, Current); + + // Check padding + chunk.padding = 0; + long uPosNotPadded = tell(); + if((uPosNotPadded & 0x01) != 0) { + ByteVector iByte = readBlock(1); + if((iByte.size() != 1) || (iByte[0] != 0)) + // Not well formed, re-seek + seek(uPosNotPadded, Beginning); + else + chunk.padding = 1; + } + d->childChunks[DIINChunk].push_back(chunk); + } + } + else if(d->chunks[i].name == "ID3 " || d->chunks[i].name == "id3 ") { + d->id3v2TagChunkID = d->chunks[i].name; + d->tag.set(ID3v2Index, new ID3v2::Tag(this, d->chunks[i].offset)); + d->isID3InPropChunk = false; + d->hasID3v2 = true; + } + } + + if(!isValid()) + return; + + if(d->childChunkIndex[PROPChunk] < 0) { + debug("DSDIFF::File::read() -- no PROP chunk found"); + setValid(false); + return; + } + + // Read properties + + unsigned int sampleRate=0; + unsigned short channels=0; + + for(unsigned int i = 0; i < d->childChunks[PROPChunk].size(); i++) { + if(d->childChunks[PROPChunk][i].name == "ID3 " || d->childChunks[PROPChunk][i].name == "id3 ") { + if(d->hasID3v2) { + d->duplicateID3V2chunkIndex = i; + continue; // ID3V2 tag has already been found at root level + } + d->id3v2TagChunkID = d->childChunks[PROPChunk][i].name; + d->tag.set(ID3v2Index, new ID3v2::Tag(this, d->childChunks[PROPChunk][i].offset)); + d->isID3InPropChunk = true; + d->hasID3v2 = true; + } + else if(d->childChunks[PROPChunk][i].name == "FS ") { + // Sample rate + seek(d->childChunks[PROPChunk][i].offset); + sampleRate = readBlock(4).toUInt(0, 4, bigEndian); + } + else if(d->childChunks[PROPChunk][i].name == "CHNL") { + // Channels + seek(d->childChunks[PROPChunk][i].offset); + channels = readBlock(2).toShort(0, bigEndian); + } + } + + // Read title & artist from DIIN chunk + d->tag.access(DIINIndex, true); + + if(d->hasDiin) { + for(unsigned int i = 0; i < d->childChunks[DIINChunk].size(); i++) { + if(d->childChunks[DIINChunk][i].name == "DITI") { + seek(d->childChunks[DIINChunk][i].offset); + unsigned int titleStrLength = readBlock(4).toUInt(0, 4, bigEndian); + if(titleStrLength <= d->childChunks[DIINChunk][i].size) { + ByteVector titleStr = readBlock(titleStrLength); + d->tag.access(DIINIndex, false)->setTitle(titleStr); + } + } + else if(d->childChunks[DIINChunk][i].name == "DIAR") { + seek(d->childChunks[DIINChunk][i].offset); + unsigned int artistStrLength = readBlock(4).toUInt(0, 4, bigEndian); + if(artistStrLength <= d->childChunks[DIINChunk][i].size) { + ByteVector artistStr = readBlock(artistStrLength); + d->tag.access(DIINIndex, false)->setArtist(artistStr); + } + } + } + } + + if(readProperties) { + if(lengthDSDSamplesTimeChannels == 0) { + // DST compressed signal : need to compute length of DSD uncompressed frames + if(dstFrameRate > 0) + lengthDSDSamplesTimeChannels = (unsigned long long)dstNumFrames + * (unsigned long long)sampleRate / (unsigned long long)dstFrameRate; + else + lengthDSDSamplesTimeChannels = 0; + } + else { + // In DSD uncompressed files, the read number of samples is the total for each channel + if(channels > 0) + lengthDSDSamplesTimeChannels /= channels; + } + int bitrate = 0; + if(lengthDSDSamplesTimeChannels > 0) + bitrate = (audioDataSizeinBytes*8*sampleRate) / lengthDSDSamplesTimeChannels / 1000; + + d->properties = new Properties(sampleRate, + channels, + lengthDSDSamplesTimeChannels, + bitrate, + propertiesStyle); + } + + if(!ID3v2Tag()) { + d->tag.access(ID3v2Index, true); + d->isID3InPropChunk = false; // By default, ID3 chunk is at root level + d->hasID3v2 = false; + } +} + +void DSDIFF::File::writeChunk(const ByteVector &name, const ByteVector &data, + unsigned long long offset, unsigned long replace, + unsigned int leadingPadding) +{ + ByteVector combined; + if(leadingPadding) + combined.append(ByteVector(leadingPadding, '\x00')); + + combined.append(name); + combined.append(ByteVector::fromLongLong(data.size(), d->endianness == BigEndian)); + combined.append(data); + if((data.size() & 0x01) != 0) + combined.append('\x00'); + + insert(combined, offset, replace); +} + diff --git a/3rdparty/taglib/dsdiff/dsdifffile.h b/3rdparty/taglib/dsdiff/dsdifffile.h new file mode 100644 index 000000000..8b8887442 --- /dev/null +++ b/3rdparty/taglib/dsdiff/dsdifffile.h @@ -0,0 +1,260 @@ +/*************************************************************************** + copyright : (C) 2016 by Damien Plisson, Audirvana + email : damien78@audirvana.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_DSDIFFFILE_H +#define TAGLIB_DSDIFFFILE_H + +#include "rifffile.h" +#include "id3v2tag.h" +#include "dsdiffproperties.h" +#include "dsdiffdiintag.h" + +namespace TagLib { + + //! An implementation of DSDIFF metadata + + /*! + * This is implementation of DSDIFF metadata. + * + * This supports an ID3v2 tag as well as reading stream from the ID3 RIFF + * chunk as well as properties from the file. + * Description of the DSDIFF format is available + * at http://dsd-guide.com/sites/default/files/white-papers/DSDIFF_1.5_Spec.pdf + * DSDIFF standard does not explictly specify the ID3V2 chunk + * It can be found at the root level, but also sometimes inside the PROP chunk + * In addition, title and artist info are stored as part of the standard + */ + + namespace DSDIFF { + + //! An implementation of TagLib::File with DSDIFF specific methods + + /*! + * This implements and provides an interface for DSDIFF files to the + * TagLib::Tag and TagLib::AudioProperties interfaces by way of implementing + * the abstract TagLib::File API as well as providing some additional + * information specific to DSDIFF files. + */ + + class TAGLIB_EXPORT File : public TagLib::File + { + public: + /*! + * Constructs an DSDIFF 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. + */ + File(FileName file, bool readProperties = true, + Properties::ReadStyle propertiesStyle = Properties::Average); + + /*! + * Constructs an DSDIFF 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 + * responsible for deleting it after the File object. + * + * \note In the current implementation, \a propertiesStyle is ignored. + */ + File(IOStream *stream, bool readProperties = true, + Properties::ReadStyle propertiesStyle = Properties::Average); + + /*! + * Destroys this instance of the File. + */ + virtual ~File(); + + /*! + * Returns a pointer to a tag that is the union of the ID3v2 and DIIN + * tags. The ID3v2 tag is given priority in reading the information -- if + * requested information exists in both the ID3v2 tag and the ID3v1 tag, + * the information from the ID3v2 tag will be returned. + * + * If you would like more granular control over the content of the tags, + * with the concession of generality, use the tag-type specific calls. + * + * \note As this tag is not implemented as an ID3v2 tag or a DIIN tag, + * but a union of the two this pointer may not be cast to the specific + * tag types. + * + * \see ID3v2Tag() + * \see DIINTag() + */ + virtual Tag *tag() const; + + /*! + * 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 + * file on disk actually has an ID3v2 tag. + * + * \see hasID3v2Tag() + */ + virtual ID3v2::Tag *ID3v2Tag() const; + + /*! + * Returns the DSDIFF DIIN Tag for this file + * + */ + DSDIFF::DIIN::Tag *DIINTag() const; + + /*! + * Implements the unified property interface -- export function. + * This method forwards to ID3v2::Tag::properties(). + */ + PropertyMap properties() const; + + void removeUnsupportedProperties(const StringList &properties); + + /*! + * Implements the unified property interface -- import function. + * This method forwards to ID3v2::Tag::setProperties(). + */ + PropertyMap setProperties(const PropertyMap &); + + /*! + * Returns the AIFF::Properties for this file. If no audio properties + * were read then this will return a null pointer. + */ + virtual Properties *audioProperties() const; + + /*! + * Save the file. If at least one tag -- ID3v1 or DIIN -- exists this + * will duplicate its content into the other tag. This returns true + * if saving was successful. + * + * If neither exists or if both tags are empty, this will strip the tags + * from the file. + * + * 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. + * + * \see save(int tags) + */ + virtual bool save(); + + /*! + * Save the file. This will attempt to save all of the tag types that are + * specified by OR-ing together TagTypes values. The save() method above + * uses AllTags. This returns true if saving was successful. + * + * 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. + */ + bool save(int tags); + + /*! + * Returns whether or not the file on disk actually has an ID3v2 tag. + * + * \see ID3v2Tag() + */ + bool hasID3v2Tag() const; + + /*! + * Returns whether or not the file on disk actually has the DSDIFF + * Title & Artist tag. + * + * \see DIINTag() + */ + bool hasDIINTag() const; + + /*! + * Returns whether or not the given \a stream can be opened as a DSDIFF + * file. + * + * \note This method is designed to do a quick check. The result may + * not necessarily be correct. + */ + static bool isSupported(IOStream *stream); + + protected: + enum Endianness { BigEndian, LittleEndian }; + + File(FileName file, Endianness endianness); + File(IOStream *stream, Endianness endianness); + + private: + File(const File &); + File &operator=(const File &); + + /*! + * Sets the data for the the specified chunk at root level to \a data. + * + * \warning This will update the file immediately. + */ + void setRootChunkData(unsigned int i, const ByteVector &data); + + /*! + * Sets the data for the root-level chunk \a name to \a data. + * If a root-level chunk with the given name already exists + * it will be overwritten, otherwise it will be + * created after the existing chunks. + * + * \warning This will update the file immediately. + */ + void setRootChunkData(const ByteVector &name, const ByteVector &data); + + /*! + * Sets the data for the the specified child chunk to \a data. + * + * If data is null, then remove the chunk + * + * \warning This will update the file immediately. + */ + void setChildChunkData(unsigned int i, const ByteVector &data, + unsigned int childChunkNum); + + /*! + * Sets the data for the child chunk \a name to \a data. If a chunk with + * the given name already exists it will be overwritten, otherwise it will + * be created after the existing chunks inside child chunk. + * + * If data is null, then remove the chunks with \a name name + * + * \warning This will update the file immediately. + */ + void setChildChunkData(const ByteVector &name, const ByteVector &data, + unsigned int childChunkNum); + + void updateRootChunksStructure(unsigned int startingChunk); + + void read(bool readProperties, Properties::ReadStyle propertiesStyle); + void writeChunk(const ByteVector &name, const ByteVector &data, + unsigned long long offset, unsigned long replace = 0, + unsigned int leadingPadding = 0); + + class FilePrivate; + FilePrivate *d; + }; + } +} + +#endif + diff --git a/3rdparty/taglib/dsdiff/dsdiffproperties.cpp b/3rdparty/taglib/dsdiff/dsdiffproperties.cpp new file mode 100644 index 000000000..b8d362cc0 --- /dev/null +++ b/3rdparty/taglib/dsdiff/dsdiffproperties.cpp @@ -0,0 +1,120 @@ +/*************************************************************************** + copyright : (C) 2016 by Damien Plisson, Audirvana + email : damien78@audirvana.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/ * + ***************************************************************************/ + +#include +#include + +#include "dsdiffproperties.h" + +using namespace TagLib; + +class DSDIFF::Properties::PropertiesPrivate +{ +public: + PropertiesPrivate() : + length(0), + bitrate(0), + sampleRate(0), + channels(0), + sampleWidth(0), + sampleCount(0) + { + } + + int length; + int bitrate; + int sampleRate; + int channels; + int sampleWidth; + unsigned long long sampleCount; +}; + +//////////////////////////////////////////////////////////////////////////////// +// public members +//////////////////////////////////////////////////////////////////////////////// + +DSDIFF::Properties::Properties(const unsigned int sampleRate, + const unsigned short channels, + const unsigned long long samplesCount, + const int bitrate, + ReadStyle style) : AudioProperties(style) +{ + d = new PropertiesPrivate; + + d->channels = channels; + d->sampleCount = samplesCount; + d->sampleWidth = 1; + d->sampleRate = sampleRate; + d->bitrate = bitrate; + d->length = d->sampleRate > 0 + ? static_cast((d->sampleCount * 1000.0) / d->sampleRate + 0.5) + : 0; +} + +DSDIFF::Properties::~Properties() +{ + delete d; +} + +int DSDIFF::Properties::length() const +{ + return lengthInSeconds(); +} + +int DSDIFF::Properties::lengthInSeconds() const +{ + return d->length / 1000; +} + +int DSDIFF::Properties::lengthInMilliseconds() const +{ + return d->length; +} + +int DSDIFF::Properties::bitrate() const +{ + return d->bitrate; +} + +int DSDIFF::Properties::sampleRate() const +{ + return d->sampleRate; +} + +int DSDIFF::Properties::channels() const +{ + return d->channels; +} + +int DSDIFF::Properties::bitsPerSample() const +{ + return d->sampleWidth; +} + +long long DSDIFF::Properties::sampleCount() const +{ + return d->sampleCount; +} + diff --git a/3rdparty/taglib/dsdiff/dsdiffproperties.h b/3rdparty/taglib/dsdiff/dsdiffproperties.h new file mode 100644 index 000000000..6aff03982 --- /dev/null +++ b/3rdparty/taglib/dsdiff/dsdiffproperties.h @@ -0,0 +1,83 @@ +/*************************************************************************** + copyright : (C) 2016 by Damien Plisson, Audirvana + email : damien78@audirvana.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_DSDIFFPROPERTIES_H +#define TAGLIB_DSDIFFPROPERTIES_H + +#include "audioproperties.h" + +namespace TagLib { + + namespace DSDIFF { + + class File; + + //! An implementation of audio property reading for DSDIFF + + /*! + * This reads the data from an DSDIFF stream found in the AudioProperties + * API. + */ + + class TAGLIB_EXPORT Properties : public AudioProperties + { + public: + /*! + * Create an instance of DSDIFF::Properties with the data read from the + * ByteVector \a data. + */ + Properties(const unsigned int sampleRate, const unsigned short channels, + const unsigned long long samplesCount, const int bitrate, + ReadStyle style); + + /*! + * Destroys this DSDIFF::Properties instance. + */ + virtual ~Properties(); + + // Reimplementations. + + virtual int length() const; + virtual int lengthInSeconds() const; + virtual int lengthInMilliseconds() const; + virtual int bitrate() const; + virtual int sampleRate() const; + virtual int channels() const; + + int bitsPerSample() const; + long long sampleCount() const; + + private: + Properties(const Properties &); + Properties &operator=(const Properties &); + + class PropertiesPrivate; + PropertiesPrivate *d; + }; + } +} + +#endif + diff --git a/3rdparty/taglib/dsf/dsffile.cpp b/3rdparty/taglib/dsf/dsffile.cpp new file mode 100644 index 000000000..1b2c24031 --- /dev/null +++ b/3rdparty/taglib/dsf/dsffile.cpp @@ -0,0 +1,241 @@ +/*************************************************************************** + copyright : (C) 2013 - 2018 by Stephen F. Booth + email : me@sbooth.org + ***************************************************************************/ + +/*************************************************************************** + * 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 +#include + +#include "dsffile.h" + +using namespace TagLib; + +// The DSF specification is located at http://dsd-guide.com/sites/default/files/white-papers/DSFFileFormatSpec_E.pdf + +class DSF::File::FilePrivate +{ +public: + FilePrivate() : + properties(0), + tag(0) + { + } + + ~FilePrivate() + { + delete properties; + delete tag; + } + + long long fileSize; + long long metadataOffset; + Properties *properties; + ID3v2::Tag *tag; +}; + +//////////////////////////////////////////////////////////////////////////////// +// static members +//////////////////////////////////////////////////////////////////////////////// + +bool DSF::File::isSupported(IOStream *stream) +{ + // A DSF file has to start with "DSD " + const ByteVector id = Utils::readHeader(stream, 4, false); + return id.startsWith("DSD "); +} + +//////////////////////////////////////////////////////////////////////////////// +// public members +//////////////////////////////////////////////////////////////////////////////// + +DSF::File::File(FileName file, bool readProperties, + Properties::ReadStyle propertiesStyle) : + TagLib::File(file), + d(new FilePrivate()) +{ + if(isOpen()) + read(readProperties, propertiesStyle); +} + +DSF::File::File(IOStream *stream, bool readProperties, + Properties::ReadStyle propertiesStyle) : + TagLib::File(stream), + d(new FilePrivate()) +{ + if(isOpen()) + read(readProperties, propertiesStyle); +} + +DSF::File::~File() +{ + delete d; +} + +ID3v2::Tag *DSF::File::tag() const +{ + return d->tag; +} + +PropertyMap DSF::File::properties() const +{ + return d->tag->properties(); +} + +PropertyMap DSF::File::setProperties(const PropertyMap &properties) +{ + return d->tag->setProperties(properties); +} + +DSF::Properties *DSF::File::audioProperties() const +{ + return d->properties; +} + +bool DSF::File::save() +{ + if(readOnly()) { + debug("DSF::File::save() -- File is read only."); + return false; + } + + if(!isValid()) { + debug("DSF::File::save() -- Trying to save invalid file."); + return false; + } + + // Three things must be updated: the file size, the tag data, and the metadata offset + + if(d->tag->isEmpty()) { + long long newFileSize = d->metadataOffset ? d->metadataOffset : d->fileSize; + + // Update the file size + if(d->fileSize != newFileSize) { + insert(ByteVector::fromLongLong(newFileSize, false), 12, 8); + d->fileSize = newFileSize; + } + + // Update the metadata offset to 0 since there is no longer a tag + if(d->metadataOffset) { + insert(ByteVector::fromLongLong(0ULL, false), 20, 8); + d->metadataOffset = 0; + } + + // Delete the old tag + truncate(newFileSize); + } + else { + ByteVector tagData = d->tag->render(); + + long long newMetadataOffset = d->metadataOffset ? d->metadataOffset : d->fileSize; + long long newFileSize = newMetadataOffset + tagData.size(); + long long oldTagSize = d->fileSize - newMetadataOffset; + + // Update the file size + if(d->fileSize != newFileSize) { + insert(ByteVector::fromLongLong(newFileSize, false), 12, 8); + d->fileSize = newFileSize; + } + + // Update the metadata offset + if(d->metadataOffset != newMetadataOffset) { + insert(ByteVector::fromLongLong(newMetadataOffset, false), 20, 8); + d->metadataOffset = newMetadataOffset; + } + + // Delete the old tag and write the new one + insert(tagData, newMetadataOffset, static_cast(oldTagSize)); + } + + return true; +} + +//////////////////////////////////////////////////////////////////////////////// +// private members +//////////////////////////////////////////////////////////////////////////////// + + +void DSF::File::read(bool readProperties, Properties::ReadStyle propertiesStyle) +{ + // A DSF file consists of four chunks: DSD chunk, format chunk, data chunk, and metadata chunk + // The file format is not chunked in the sense of a RIFF File, though + + // DSD chunk + ByteVector chunkName = readBlock(4); + if(chunkName != "DSD ") { + debug("DSF::File::read() -- Not a DSF file."); + setValid(false); + return; + } + + long long chunkSize = readBlock(8).toLongLong(false); + + // Integrity check + if(28 != chunkSize) { + debug("DSF::File::read() -- File is corrupted, wrong chunk size"); + setValid(false); + return; + } + + d->fileSize = readBlock(8).toLongLong(false); + + // File is malformed or corrupted + if(d->fileSize != length()) { + debug("DSF::File::read() -- File is corrupted wrong length"); + setValid(false); + return; + } + + d->metadataOffset = readBlock(8).toLongLong(false); + + // File is malformed or corrupted + if(d->metadataOffset > d->fileSize) { + debug("DSF::File::read() -- Invalid metadata offset."); + setValid(false); + return; + } + + // Format chunk + chunkName = readBlock(4); + if(chunkName != "fmt ") { + debug("DSF::File::read() -- Missing 'fmt ' chunk."); + setValid(false); + return; + } + + chunkSize = readBlock(8).toLongLong(false); + + d->properties = new Properties(readBlock(chunkSize), propertiesStyle); + + // Skip the data chunk + + // A metadata offset of 0 indicates the absence of an ID3v2 tag + if(0 == d->metadataOffset) + d->tag = new ID3v2::Tag(); + else + d->tag = new ID3v2::Tag(this, d->metadataOffset); +} + diff --git a/3rdparty/taglib/dsf/dsffile.h b/3rdparty/taglib/dsf/dsffile.h new file mode 100644 index 000000000..e75298d7c --- /dev/null +++ b/3rdparty/taglib/dsf/dsffile.h @@ -0,0 +1,128 @@ +/*************************************************************************** + copyright : (C) 2013 - 2018 by Stephen F. Booth + email : me@sbooth.org + ***************************************************************************/ + +/*************************************************************************** + * 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_DSFFILE_H +#define TAGLIB_DSFFILE_H + +#include "tfile.h" +#include "id3v2tag.h" +#include "dsfproperties.h" + +namespace TagLib { + + //! An implementation of DSF metadata + + /*! + * This is implementation of DSF metadata. + * + * This supports an ID3v2 tag as well as properties from the file. + */ + + namespace DSF { + + //! An implementation of TagLib::File with DSF specific methods + + /*! + * This implements and provides an interface for DSF files to the + * TagLib::Tag and TagLib::AudioProperties interfaces by way of implementing + * the abstract TagLib::File API as well as providing some additional + * information specific to DSF files. + */ + + class TAGLIB_EXPORT File : public TagLib::File + { + public: + /*! + * Contructs an DSF file from \a file. If \a readProperties is true the + * file's audio properties will also be read using \a propertiesStyle. If + * false, \a propertiesStyle is ignored. + */ + File(FileName file, bool readProperties = true, + Properties::ReadStyle propertiesStyle = Properties::Average); + + /*! + * Contructs an DSF file from \a file. If \a readProperties is true the + * file's audio properties will also be read using \a propertiesStyle. If + * false, \a propertiesStyle is ignored. + */ + File(IOStream *stream, bool readProperties = true, + Properties::ReadStyle propertiesStyle = Properties::Average); + + /*! + * Destroys this instance of the File. + */ + virtual ~File(); + + /*! + * Returns the Tag for this file. + */ + ID3v2::Tag *tag() const; + + /*! + * Implements the unified property interface -- export function. + * This method forwards to ID3v2::Tag::properties(). + */ + PropertyMap properties() const; + + /*! + * Implements the unified property interface -- import function. + * This method forwards to ID3v2::Tag::setProperties(). + */ + PropertyMap setProperties(const PropertyMap &); + + /*! + * Returns the DSF::AudioProperties for this file. If no audio properties + * were read then this will return a null pointer. + */ + virtual Properties *audioProperties() const; + + /*! + * Saves the file. + */ + virtual bool save(); + + /*! + * Returns whether or not the given \a stream can be opened as a DSF + * file. + * + * \note This method is designed to do a quick check. The result may + * not necessarily be correct. + */ + static bool isSupported(IOStream *stream); + + private: + File(const File &); + File &operator=(const File &); + + void read(bool readProperties, Properties::ReadStyle propertiesStyle); + + class FilePrivate; + FilePrivate *d; + }; + } +} + +#endif + diff --git a/3rdparty/taglib/dsf/dsfproperties.cpp b/3rdparty/taglib/dsf/dsfproperties.cpp new file mode 100644 index 000000000..18511fa72 --- /dev/null +++ b/3rdparty/taglib/dsf/dsfproperties.cpp @@ -0,0 +1,161 @@ +/*************************************************************************** + copyright : (C) 2013 by Stephen F. Booth + email : me@sbooth.org + ***************************************************************************/ + +/*************************************************************************** + * 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 "dsfproperties.h" + +using namespace TagLib; + +class DSF::Properties::PropertiesPrivate +{ +public: + PropertiesPrivate() : + formatVersion(0), + formatID(0), + channelType(0), + channelNum(0), + samplingFrequency(0), + bitsPerSample(0), + sampleCount(0), + blockSizePerChannel(0), + bitrate(0), + length(0) + { + } + + // Nomenclature is from DSF file format specification + unsigned int formatVersion; + unsigned int formatID; + unsigned int channelType; + unsigned int channelNum; + unsigned int samplingFrequency; + unsigned int bitsPerSample; + long long sampleCount; + unsigned int blockSizePerChannel; + + // Computed + unsigned int bitrate; + unsigned int length; +}; + +//////////////////////////////////////////////////////////////////////////////// +// public members +//////////////////////////////////////////////////////////////////////////////// + +DSF::Properties::Properties(const ByteVector &data, ReadStyle style) : TagLib::AudioProperties(style) +{ + d = new PropertiesPrivate; + read(data); +} + +DSF::Properties::~Properties() +{ + delete d; +} + +int DSF::Properties::length() const +{ + return lengthInSeconds(); +} + +int DSF::Properties::lengthInSeconds() const +{ + return d->length / 1000; +} + +int DSF::Properties::lengthInMilliseconds() const +{ + return d->length; +} + +int DSF::Properties::bitrate() const +{ + return d->bitrate; +} + +int DSF::Properties::sampleRate() const +{ + return d->samplingFrequency; +} + +int DSF::Properties::channels() const +{ + return d->channelNum; +} + +// DSF specific +int DSF::Properties::formatVersion() const +{ + return d->formatVersion; +} + +int DSF::Properties::formatID() const +{ + return d->formatID; +} + +int DSF::Properties::channelType() const +{ + return d->channelType; +} + +int DSF::Properties::bitsPerSample() const +{ + return d->bitsPerSample; +} + +long long DSF::Properties::sampleCount() const +{ + return d->sampleCount; +} + +int DSF::Properties::blockSizePerChannel() const +{ + return d->blockSizePerChannel; +} + +//////////////////////////////////////////////////////////////////////////////// +// private members +//////////////////////////////////////////////////////////////////////////////// + +void DSF::Properties::read(const ByteVector &data) +{ + d->formatVersion = data.toUInt(0U,false); + d->formatID = data.toUInt(4U,false); + d->channelType = data.toUInt(8U,false); + d->channelNum = data.toUInt(12U,false); + d->samplingFrequency = data.toUInt(16U,false); + d->bitsPerSample = data.toUInt(20U,false); + d->sampleCount = data.toLongLong(24U,false); + d->blockSizePerChannel = data.toUInt(32U,false); + + d->bitrate + = static_cast((d->samplingFrequency * d->bitsPerSample * d->channelNum) / 1000.0 + 0.5); + d->length + = d->samplingFrequency > 0 ? static_cast(d->sampleCount * 1000.0 / d->samplingFrequency + 0.5) : 0; +} + diff --git a/3rdparty/taglib/dsf/dsfproperties.h b/3rdparty/taglib/dsf/dsfproperties.h new file mode 100644 index 000000000..273a9f5ae --- /dev/null +++ b/3rdparty/taglib/dsf/dsfproperties.h @@ -0,0 +1,92 @@ +/*************************************************************************** + copyright : (C) 2013 by Stephen F. Booth + email : me@sbooth.org + ***************************************************************************/ + +/*************************************************************************** + * 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_DSFPROPERTIES_H +#define TAGLIB_DSFPROPERTIES_H + +#include "audioproperties.h" + +namespace TagLib { + + namespace DSF { + + class File; + + //! An implementation of audio property reading for DSF + + /*! + * This reads the data from a DSF stream found in the AudioProperties + * API. + */ + + class TAGLIB_EXPORT Properties : public TagLib::AudioProperties + { + public: + /*! + * Create an instance of DSF::AudioProperties with the data read from the + * ByteVector \a data. + */ + Properties(const ByteVector &data, ReadStyle style); + + /*! + * Destroys this DSF::AudioProperties instance. + */ + virtual ~Properties(); + + // Reimplementations. + + virtual int length() const; + virtual int lengthInSeconds() const; + virtual int lengthInMilliseconds() const; + virtual int bitrate() const; + virtual int sampleRate() const; + virtual int channels() const; + + int formatVersion() const; + int formatID() const; + + /*! + * Channel type values: 1 = mono, 2 = stereo, 3 = 3 channels, + * 4 = quad, 5 = 4 channels, 6 = 5 channels, 7 = 5.1 channels + */ + int channelType() const; + int bitsPerSample() const; + long long sampleCount() const; + int blockSizePerChannel() const; + + private: + Properties(const Properties &); + Properties &operator=(const Properties &); + + void read(const ByteVector &data); + + class PropertiesPrivate; + PropertiesPrivate *d; + }; + } +} + +#endif + diff --git a/3rdparty/taglib/fileref.cpp b/3rdparty/taglib/fileref.cpp index 935c371bd..5ec4dc06d 100644 --- a/3rdparty/taglib/fileref.cpp +++ b/3rdparty/taglib/fileref.cpp @@ -52,6 +52,8 @@ #include "s3mfile.h" #include "itfile.h" #include "xmfile.h" +#include "dsffile.h" +#include "dsdifffile.h" using namespace TagLib; @@ -135,6 +137,10 @@ namespace return new IT::File(stream, readAudioProperties, audioPropertiesStyle); if(ext == "XM") return new XM::File(stream, readAudioProperties, audioPropertiesStyle); + if(ext == "DFF" || ext == "DSDIFF") + return new DSDIFF::File(stream, readAudioProperties, audioPropertiesStyle); + if(ext == "DSF") + return new DSF::File(stream, readAudioProperties, audioPropertiesStyle); return 0; } @@ -174,6 +180,10 @@ namespace file = new RIFF::WAV::File(stream, readAudioProperties, audioPropertiesStyle); else if(APE::File::isSupported(stream)) file = new APE::File(stream, readAudioProperties, audioPropertiesStyle); + else if(DSDIFF::File::isSupported(stream)) + file = new DSDIFF::File(stream, readAudioProperties, audioPropertiesStyle); + else if(DSF::File::isSupported(stream)) + file = new DSF::File(stream, readAudioProperties, audioPropertiesStyle); // isSupported() only does a quick check, so double check the file here. @@ -255,6 +265,10 @@ namespace return new IT::File(fileName, readAudioProperties, audioPropertiesStyle); if(ext == "XM") return new XM::File(fileName, readAudioProperties, audioPropertiesStyle); + if(ext == "DFF" || ext == "DSDIFF") + return new DSDIFF::File(fileName, readAudioProperties, audioPropertiesStyle); + if(ext == "DSF") + return new DSF::File(fileName, readAudioProperties, audioPropertiesStyle); return 0; } @@ -387,6 +401,9 @@ StringList FileRef::defaultFileExtensions() l.append("s3m"); l.append("it"); l.append("xm"); + l.append("dsf"); + l.append("dff"); + l.append("dsdiff"); // alias for "dff" return l; } diff --git a/3rdparty/taglib/mpeg/id3v2/frames/tableofcontentsframe.cpp b/3rdparty/taglib/mpeg/id3v2/frames/tableofcontentsframe.cpp index ddd3b88c6..efe89d171 100644 --- a/3rdparty/taglib/mpeg/id3v2/frames/tableofcontentsframe.cpp +++ b/3rdparty/taglib/mpeg/id3v2/frames/tableofcontentsframe.cpp @@ -216,7 +216,23 @@ void TableOfContentsFrame::removeEmbeddedFrames(const ByteVector &id) String TableOfContentsFrame::toString() const { - return String(); + String s = String(d->elementID) + + ": top level: " + (d->isTopLevel ? "true" : "false") + + ", ordered: " + (d->isOrdered ? "true" : "false"); + + if(!d->childElements.isEmpty()) { + s+= ", chapters: [ " + String(d->childElements.toByteVector(", ")) + " ]"; + } + + 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 TableOfContentsFrame::asProperties() const diff --git a/3rdparty/taglib/mpeg/id3v2/frames/textidentificationframe.cpp b/3rdparty/taglib/mpeg/id3v2/frames/textidentificationframe.cpp index db9a177e5..61cf6ed8b 100644 --- a/3rdparty/taglib/mpeg/id3v2/frames/textidentificationframe.cpp +++ b/3rdparty/taglib/mpeg/id3v2/frames/textidentificationframe.cpp @@ -339,7 +339,13 @@ UserTextIdentificationFrame::UserTextIdentificationFrame(const String &descripti String UserTextIdentificationFrame::toString() const { - return "[" + description() + "] " + fieldList().toString(); + // first entry is the description itself, drop from values list + StringList l = fieldList(); + for(StringList::Iterator it = l.begin(); it != l.end(); ++it) { + l.erase(it); + break; + } + return "[" + description() + "] " + l.toString(); } String UserTextIdentificationFrame::description() const diff --git a/3rdparty/taglib/ogg/flac/oggflacfile.cpp b/3rdparty/taglib/ogg/flac/oggflacfile.cpp index 53d04508a..07ea9dccc 100644 --- a/3rdparty/taglib/ogg/flac/oggflacfile.cpp +++ b/3rdparty/taglib/ogg/flac/oggflacfile.cpp @@ -231,11 +231,21 @@ void Ogg::FLAC::File::scan() if(!metadataHeader.startsWith("fLaC")) { // FLAC 1.1.2+ + // See https://xiph.org/flac/ogg_mapping.html for the header specification. + if(metadataHeader.size() < 13) + return; + + if(metadataHeader[0] != 0x7f) + return; + if(metadataHeader.mid(1, 4) != "FLAC") return; - if(metadataHeader[5] != 1) - return; // not version 1 + if(metadataHeader[5] != 1 && metadataHeader[6] != 0) + return; // not version 1.0 + + if(metadataHeader.mid(9, 4) != "fLaC") + return; metadataHeader = metadataHeader.mid(13); } diff --git a/3rdparty/taglib/toolkit/tfile.cpp b/3rdparty/taglib/toolkit/tfile.cpp index aff1684d9..055b0d6d3 100644 --- a/3rdparty/taglib/toolkit/tfile.cpp +++ b/3rdparty/taglib/toolkit/tfile.cpp @@ -63,6 +63,8 @@ #include "itfile.h" #include "xmfile.h" #include "mp4file.h" +#include "dsffile.h" +#include "dsdifffile.h" using namespace TagLib; @@ -148,6 +150,10 @@ PropertyMap File::properties() const return dynamic_cast(this)->properties(); if(dynamic_cast(this)) return dynamic_cast(this)->properties(); + if(dynamic_cast(this)) + return dynamic_cast(this)->properties(); + if(dynamic_cast(this)) + return dynamic_cast(this)->properties(); return tag()->properties(); } @@ -177,6 +183,10 @@ void File::removeUnsupportedProperties(const StringList &properties) dynamic_cast(this)->removeUnsupportedProperties(properties); else if(dynamic_cast(this)) dynamic_cast(this)->removeUnsupportedProperties(properties); + else if(dynamic_cast(this)) + dynamic_cast(this)->removeUnsupportedProperties(properties); + else if(dynamic_cast(this)) + dynamic_cast(this)->removeUnsupportedProperties(properties); else tag()->removeUnsupportedProperties(properties); } @@ -219,6 +229,10 @@ PropertyMap File::setProperties(const PropertyMap &properties) return dynamic_cast(this)->setProperties(properties); else if(dynamic_cast(this)) return dynamic_cast(this)->setProperties(properties); + else if(dynamic_cast(this)) + return dynamic_cast(this)->setProperties(properties); + else if(dynamic_cast(this)) + return dynamic_cast(this)->setProperties(properties); else return tag()->setProperties(properties); } diff --git a/3rdparty/taglib/toolkit/tfilestream.cpp b/3rdparty/taglib/toolkit/tfilestream.cpp index 17a09f3da..487df8e39 100644 --- a/3rdparty/taglib/toolkit/tfilestream.cpp +++ b/3rdparty/taglib/toolkit/tfilestream.cpp @@ -58,6 +58,11 @@ namespace #endif } + FileHandle openFile(const int fileDescriptor, bool readOnly) + { + return InvalidFileHandle; + } + void closeFile(FileHandle file) { CloseHandle(file); @@ -98,6 +103,11 @@ namespace return fopen(path, readOnly ? "rb" : "rb+"); } + FileHandle openFile(const int fileDescriptor, bool readOnly) + { + return fdopen(fileDescriptor, readOnly ? "rb" : "rb+"); + } + void closeFile(FileHandle file) { fclose(file); @@ -149,13 +159,28 @@ FileStream::FileStream(FileName fileName, bool openReadOnly) d->file = openFile(fileName, true); 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 - } +} + +FileStream::FileStream(int fileDescriptor, bool openReadOnly) + : d(new FileStreamPrivate("")) +{ + // First try with read / write mode, if that fails, fall back to read only. + + if(!openReadOnly) + d->file = openFile(fileDescriptor, false); + + if(d->file != InvalidFileHandle) + d->readOnly = false; + else + d->file = openFile(fileDescriptor, true); + + if(d->file == InvalidFileHandle) + debug("Could not open file using file descriptor"); } FileStream::~FileStream() @@ -255,8 +280,7 @@ void FileStream::insert(const ByteVector &data, unsigned long start, unsigned lo ByteVector buffer = data; ByteVector aboutToOverwrite(static_cast(bufferLength)); - while(true) - { + while(true) { // Seek to the current read position and read the data that we're about // to overwrite. Appropriately increment the readPosition. @@ -304,8 +328,7 @@ void FileStream::removeBlock(unsigned long start, unsigned long length) ByteVector buffer(static_cast(bufferLength)); - for(unsigned int bytesRead = -1; bytesRead != 0;) - { + for(unsigned int bytesRead = -1; bytesRead != 0;) { seek(readPosition); bytesRead = static_cast(readFile(d->file, buffer)); readPosition += bytesRead; @@ -401,7 +424,8 @@ long FileStream::tell() const const LARGE_INTEGER zero = {}; LARGE_INTEGER position; - if(SetFilePointerEx(d->file, zero, &position, FILE_CURRENT) && position.QuadPart <= LONG_MAX) { + if(SetFilePointerEx(d->file, zero, &position, FILE_CURRENT) && + position.QuadPart <= LONG_MAX) { return static_cast(position.QuadPart); } else { @@ -470,9 +494,8 @@ void FileStream::truncate(long length) #else const int error = ftruncate(fileno(d->file), length); - if(error != 0) { + if(error != 0) debug("FileStream::truncate() -- Coundn't truncate the file."); - } #endif } diff --git a/3rdparty/taglib/toolkit/tfilestream.h b/3rdparty/taglib/toolkit/tfilestream.h index 96a476d65..aa4d71b30 100644 --- a/3rdparty/taglib/toolkit/tfilestream.h +++ b/3rdparty/taglib/toolkit/tfilestream.h @@ -54,6 +54,11 @@ namespace TagLib { */ FileStream(FileName file, bool openReadOnly = false); + /*! + * Construct a File object and opens the \a file using file descriptor. + */ + FileStream(int fileDescriptor, bool openReadOnly = false); + /*! * Destroys this FileStream instance. */