From e7cfbfed76dbaeaacf3e78ba0a7dfc8bc4801526 Mon Sep 17 00:00:00 2001 From: "Stephen F. Booth" Date: Mon, 23 Sep 2013 21:02:30 -0400 Subject: [PATCH 01/14] Added DSF support --- taglib/CMakeLists.txt | 10 +- taglib/dsf/dsffile.cpp | 183 +++++++++++++++++++++++++++++++++++ taglib/dsf/dsffile.h | 118 ++++++++++++++++++++++ taglib/dsf/dsfproperties.cpp | 149 ++++++++++++++++++++++++++++ taglib/dsf/dsfproperties.h | 84 ++++++++++++++++ taglib/fileref.cpp | 4 + 6 files changed, 547 insertions(+), 1 deletion(-) create mode 100644 taglib/dsf/dsffile.cpp create mode 100644 taglib/dsf/dsffile.h create mode 100644 taglib/dsf/dsfproperties.cpp create mode 100644 taglib/dsf/dsfproperties.h diff --git a/taglib/CMakeLists.txt b/taglib/CMakeLists.txt index c61b500e..70a8a7d7 100755 --- a/taglib/CMakeLists.txt +++ b/taglib/CMakeLists.txt @@ -26,6 +26,7 @@ include_directories( ${CMAKE_CURRENT_SOURCE_DIR}/xm ${CMAKE_CURRENT_SOURCE_DIR}/ebml ${CMAKE_CURRENT_SOURCE_DIR}/ebml/matroska + ${CMAKE_CURRENT_SOURCE_DIR}/dsf ) if(ZLIB_FOUND) @@ -138,6 +139,8 @@ set(tag_HDRS ebml/matroska/ebmlmatroskafile.h ebml/matroska/ebmlmatroskaconstants.h ebml/matroska/ebmlmatroskaaudio.h + dsf/dsffile.h + dsf/dsfproperties.h ) set(mpeg_SRCS @@ -288,6 +291,11 @@ set(xm_SRCS xm/xmproperties.cpp ) +set(dsf_SRCS + dsf/dsffile.cpp + dsf/dsfproperties.cpp +) + set(toolkit_SRCS toolkit/tstring.cpp toolkit/tstringlist.cpp @@ -320,7 +328,7 @@ set(tag_LIB_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} - ${ebml_SRCS} ${matroska_SRCS} + ${ebml_SRCS} ${matroska_SRCS} ${dsf_SRCS} tag.cpp tagunion.cpp fileref.cpp diff --git a/taglib/dsf/dsffile.cpp b/taglib/dsf/dsffile.cpp new file mode 100644 index 00000000..d64de246 --- /dev/null +++ b/taglib/dsf/dsffile.cpp @@ -0,0 +1,183 @@ +/*************************************************************************** + 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 +#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; +}; + +//////////////////////////////////////////////////////////////////////////////// +// 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; + } + + // Two things must be updated: the file size and the tag data + // The metadata offset doesn't change + + ByteVector tagData = d->tag->render(); + + long long oldTagSize = d->fileSize - d->metadataOffset; + long long newFileSize = d->metadataOffset + tagData.size(); + + // Write the file size + insert(ByteVector::fromUInt64LE(newFileSize), 12, 8); + + // Delete the old tag and write the new one + insert(tagData, d->metadataOffset, 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."); + return; + } + + long long chunkSize = readBlock(8).toInt64LE(0); + + // Integrity check + if(28 != chunkSize) { + debug("DSF::File::read() -- File is corrupted."); + return; + } + + d->fileSize = readBlock(8).toInt64LE(0); + d->metadataOffset = readBlock(8).toUInt32LE(0); + + // File is malformed or corrupted + if(d->metadataOffset > d->fileSize) { + debug("DSF::File::read() -- Invalid metadata offset."); + return; + } + + // Format chunk + chunkName = readBlock(4); + if(chunkName != "fmt ") { + debug("DSF::File::read() -- Missing 'fmt ' chunk."); + return; + } + + chunkSize = readBlock(8).toInt64LE(0); + + d->properties = new Properties(readBlock(chunkSize), propertiesStyle); + + // Skip the data chunk + + // TODO: Is it better to read the chunk size directly or trust the offset in the DSD chunk? + d->tag = new ID3v2::Tag(this, d->metadataOffset); +} + diff --git a/taglib/dsf/dsffile.h b/taglib/dsf/dsffile.h new file mode 100644 index 00000000..bf79fde9 --- /dev/null +++ b/taglib/dsf/dsffile.h @@ -0,0 +1,118 @@ +/*************************************************************************** + 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_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. + */ + virtual 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 AIFF::Properties 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(); + + private: + File(const File &); + File &operator=(const File &); + + void read(bool readProperties, Properties::ReadStyle propertiesStyle); + + class FilePrivate; + FilePrivate *d; + }; + } +} + +#endif diff --git a/taglib/dsf/dsfproperties.cpp b/taglib/dsf/dsfproperties.cpp new file mode 100644 index 00000000..90db6124 --- /dev/null +++ b/taglib/dsf/dsfproperties.cpp @@ -0,0 +1,149 @@ +/*************************************************************************** + 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 + uint formatVersion; + uint formatID; + uint channelType; + uint channelNum; + uint samplingFrequency; + uint bitsPerSample; + long long sampleCount; + uint blockSizePerChannel; + + // Computed + uint bitrate; + uint length; +}; + +//////////////////////////////////////////////////////////////////////////////// +// public members +//////////////////////////////////////////////////////////////////////////////// + +DSF::Properties::Properties(const ByteVector &data, ReadStyle style) +{ + d = new PropertiesPrivate; + read(data); +} + +DSF::Properties::~Properties() +{ + delete d; +} + +int DSF::Properties::length() 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.mid(0, 4).toUInt32LE(0); + d->formatID = data.mid(4, 4).toUInt32LE(0); + d->channelType = data.mid(8, 4).toUInt32LE(0); + d->channelNum = data.mid(12, 4).toUInt32LE(0); + d->samplingFrequency = data.mid(16, 4).toUInt32LE(0); + d->bitsPerSample = data.mid(20, 4).toUInt32LE(0); + d->sampleCount = data.mid(24, 8).toInt64LE(0); + d->blockSizePerChannel = data.mid(32, 4).toUInt32LE(0); + + d->bitrate = (d->samplingFrequency * d->bitsPerSample * d->channelNum) / 1000.0; + d->length = d->samplingFrequency > 0 ? d->sampleCount / d->samplingFrequency : 0; +} diff --git a/taglib/dsf/dsfproperties.h b/taglib/dsf/dsfproperties.h new file mode 100644 index 00000000..ed16b84d --- /dev/null +++ b/taglib/dsf/dsfproperties.h @@ -0,0 +1,84 @@ +/*************************************************************************** + 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 AudioProperties + { + public: + /*! + * Create an instance of DSF::Properties with the data read from the + * ByteVector \a data. + */ + Properties(const ByteVector &data, ReadStyle style); + + /*! + * Destroys this AIFF::Properties instance. + */ + virtual ~Properties(); + + // Reimplementations. + + virtual int length() const; + virtual int bitrate() const; + virtual int sampleRate() const; + virtual int channels() const; + + int formatVersion() const; + int formatID() const; + 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/taglib/fileref.cpp b/taglib/fileref.cpp index 455321d6..9b41457f 100644 --- a/taglib/fileref.cpp +++ b/taglib/fileref.cpp @@ -51,6 +51,7 @@ #include "s3mfile.h" #include "itfile.h" #include "xmfile.h" +#include "dsffile.h" using namespace TagLib; @@ -137,6 +138,8 @@ namespace file.reset(new IT::File(fileName, readAudioProperties, audioPropertiesStyle)); else if(ext == "XM") file.reset(new XM::File(fileName, readAudioProperties, audioPropertiesStyle)); + else if(ext == "DSF") + file.reset(new DSF::File(fileName, readAudioProperties, audioPropertiesStyle)); } return file; @@ -294,6 +297,7 @@ StringList FileRef::defaultFileExtensions() l.append("s3m"); l.append("it"); l.append("xm"); + l.append("dsf"); return l; } From c09ea21ae365369dc97659d893a65c18edcfeae7 Mon Sep 17 00:00:00 2001 From: "Stephen F. Booth" Date: Sat, 5 Oct 2013 09:58:14 -0400 Subject: [PATCH 02/14] Correctly handle files with no ID3v2 tag --- taglib/dsf/dsffile.cpp | 36 ++++++++++++++++++++++++------------ 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/taglib/dsf/dsffile.cpp b/taglib/dsf/dsffile.cpp index d64de246..6df9adc5 100644 --- a/taglib/dsf/dsffile.cpp +++ b/taglib/dsf/dsffile.cpp @@ -114,20 +114,30 @@ bool DSF::File::save() return false; } - // Two things must be updated: the file size and the tag data - // The metadata offset doesn't change + // Three things must be updated: the file size, the tag data, and the metadata offset ByteVector tagData = d->tag->render(); - - long long oldTagSize = d->fileSize - d->metadataOffset; - long long newFileSize = d->metadataOffset + tagData.size(); - // Write the file size - insert(ByteVector::fromUInt64LE(newFileSize), 12, 8); + long long metadataLocation = d->metadataOffset ? d->metadataOffset : d->fileSize; + long long newFileSize = metadataLocation + tagData.size(); + long long newMetadataOffset = tagData.size() ? metadataLocation : 0; + long long oldTagSize = d->fileSize - metadataLocation; + + // Update the file size + if(d->fileSize != newFileSize) { + insert(ByteVector::fromUInt64LE(newFileSize), 12, 8); + d->fileSize = newFileSize; + } + + // Update the metadata offset + if(d->metadataOffset != newMetadataOffset) { + insert(ByteVector::fromUInt64LE(newMetadataOffset), 20, 8); + d->metadataOffset = newMetadataOffset; + } // Delete the old tag and write the new one - insert(tagData, d->metadataOffset, oldTagSize); - + insert(tagData, metadataLocation, oldTagSize); + return true; } @@ -177,7 +187,9 @@ void DSF::File::read(bool readProperties, Properties::ReadStyle propertiesStyle) // Skip the data chunk - // TODO: Is it better to read the chunk size directly or trust the offset in the DSD chunk? - d->tag = new ID3v2::Tag(this, d->metadataOffset); + // 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); } - From 43240b8ecc70156b1073a405c724f8f77d74ee2f Mon Sep 17 00:00:00 2001 From: "Stephen F. Booth" Date: Sat, 5 Oct 2013 13:51:39 -0400 Subject: [PATCH 03/14] Fixed a comment --- taglib/dsf/dsffile.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/taglib/dsf/dsffile.h b/taglib/dsf/dsffile.h index bf79fde9..a931d86b 100644 --- a/taglib/dsf/dsffile.h +++ b/taglib/dsf/dsffile.h @@ -93,7 +93,7 @@ namespace TagLib { PropertyMap setProperties(const PropertyMap &); /*! - * Returns the AIFF::Properties for this file. If no audio properties + * Returns the DSF::Properties for this file. If no audio properties * were read then this will return a null pointer. */ virtual Properties *audioProperties() const; From b949e23be3f77e9f3b1ece2f108097c83452db83 Mon Sep 17 00:00:00 2001 From: "Stephen F. Booth" Date: Sat, 5 Oct 2013 13:58:14 -0400 Subject: [PATCH 04/14] Rename `DSF::Properties` to `DSF::AudioProperties` --- taglib/dsf/dsffile.cpp | 12 ++++++------ taglib/dsf/dsffile.h | 10 +++++----- taglib/dsf/dsfproperties.cpp | 28 ++++++++++++++-------------- taglib/dsf/dsfproperties.h | 14 +++++++------- 4 files changed, 32 insertions(+), 32 deletions(-) diff --git a/taglib/dsf/dsffile.cpp b/taglib/dsf/dsffile.cpp index 6df9adc5..02460b93 100644 --- a/taglib/dsf/dsffile.cpp +++ b/taglib/dsf/dsffile.cpp @@ -53,7 +53,7 @@ public: long long fileSize; long long metadataOffset; - Properties *properties; + AudioProperties *properties; ID3v2::Tag *tag; }; @@ -62,7 +62,7 @@ public: //////////////////////////////////////////////////////////////////////////////// DSF::File::File(FileName file, bool readProperties, - Properties::ReadStyle propertiesStyle) : TagLib::File(file) + AudioProperties::ReadStyle propertiesStyle) : TagLib::File(file) { d = new FilePrivate; if(isOpen()) @@ -70,7 +70,7 @@ DSF::File::File(FileName file, bool readProperties, } DSF::File::File(IOStream *stream, bool readProperties, - Properties::ReadStyle propertiesStyle) : TagLib::File(stream) + AudioProperties::ReadStyle propertiesStyle) : TagLib::File(stream) { d = new FilePrivate; if(isOpen()) @@ -97,7 +97,7 @@ PropertyMap DSF::File::setProperties(const PropertyMap &properties) return d->tag->setProperties(properties); } -DSF::Properties *DSF::File::audioProperties() const +DSF::AudioProperties *DSF::File::audioProperties() const { return d->properties; } @@ -145,7 +145,7 @@ bool DSF::File::save() // private members //////////////////////////////////////////////////////////////////////////////// -void DSF::File::read(bool readProperties, Properties::ReadStyle propertiesStyle) +void DSF::File::read(bool readProperties, AudioProperties::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 @@ -183,7 +183,7 @@ void DSF::File::read(bool readProperties, Properties::ReadStyle propertiesStyle) chunkSize = readBlock(8).toInt64LE(0); - d->properties = new Properties(readBlock(chunkSize), propertiesStyle); + d->properties = new AudioProperties(readBlock(chunkSize), propertiesStyle); // Skip the data chunk diff --git a/taglib/dsf/dsffile.h b/taglib/dsf/dsffile.h index a931d86b..60c25174 100644 --- a/taglib/dsf/dsffile.h +++ b/taglib/dsf/dsffile.h @@ -60,7 +60,7 @@ namespace TagLib { * false, \a propertiesStyle is ignored. */ File(FileName file, bool readProperties = true, - Properties::ReadStyle propertiesStyle = Properties::Average); + AudioProperties::ReadStyle propertiesStyle = AudioProperties::Average); /*! * Contructs an DSF file from \a file. If \a readProperties is true the @@ -68,7 +68,7 @@ namespace TagLib { * false, \a propertiesStyle is ignored. */ File(IOStream *stream, bool readProperties = true, - Properties::ReadStyle propertiesStyle = Properties::Average); + AudioProperties::ReadStyle propertiesStyle = AudioProperties::Average); /*! * Destroys this instance of the File. @@ -93,10 +93,10 @@ namespace TagLib { PropertyMap setProperties(const PropertyMap &); /*! - * Returns the DSF::Properties for this file. If no audio properties + * Returns the DSF::AudioProperties for this file. If no audio properties * were read then this will return a null pointer. */ - virtual Properties *audioProperties() const; + virtual AudioProperties *audioProperties() const; /*! * Saves the file. @@ -107,7 +107,7 @@ namespace TagLib { File(const File &); File &operator=(const File &); - void read(bool readProperties, Properties::ReadStyle propertiesStyle); + void read(bool readProperties, AudioProperties::ReadStyle propertiesStyle); class FilePrivate; FilePrivate *d; diff --git a/taglib/dsf/dsfproperties.cpp b/taglib/dsf/dsfproperties.cpp index 90db6124..e0c2c9cb 100644 --- a/taglib/dsf/dsfproperties.cpp +++ b/taglib/dsf/dsfproperties.cpp @@ -30,7 +30,7 @@ using namespace TagLib; -class DSF::Properties::PropertiesPrivate +class DSF::AudioProperties::PropertiesPrivate { public: PropertiesPrivate() : @@ -67,64 +67,64 @@ public: // public members //////////////////////////////////////////////////////////////////////////////// -DSF::Properties::Properties(const ByteVector &data, ReadStyle style) +DSF::AudioProperties::AudioProperties(const ByteVector &data, ReadStyle style) { d = new PropertiesPrivate; read(data); } -DSF::Properties::~Properties() +DSF::AudioProperties::~AudioProperties() { delete d; } -int DSF::Properties::length() const +int DSF::AudioProperties::length() const { return d->length; } -int DSF::Properties::bitrate() const +int DSF::AudioProperties::bitrate() const { return d->bitrate; } -int DSF::Properties::sampleRate() const +int DSF::AudioProperties::sampleRate() const { return d->samplingFrequency; } -int DSF::Properties::channels() const +int DSF::AudioProperties::channels() const { return d->channelNum; } // DSF specific -int DSF::Properties::formatVersion() const +int DSF::AudioProperties::formatVersion() const { return d->formatVersion; } -int DSF::Properties::formatID() const +int DSF::AudioProperties::formatID() const { return d->formatID; } -int DSF::Properties::channelType() const +int DSF::AudioProperties::channelType() const { return d->channelType; } -int DSF::Properties::bitsPerSample() const +int DSF::AudioProperties::bitsPerSample() const { return d->bitsPerSample; } -long long DSF::Properties::sampleCount() const +long long DSF::AudioProperties::sampleCount() const { return d->sampleCount; } -int DSF::Properties::blockSizePerChannel() const +int DSF::AudioProperties::blockSizePerChannel() const { return d->blockSizePerChannel; } @@ -133,7 +133,7 @@ int DSF::Properties::blockSizePerChannel() const // private members //////////////////////////////////////////////////////////////////////////////// -void DSF::Properties::read(const ByteVector &data) +void DSF::AudioProperties::read(const ByteVector &data) { d->formatVersion = data.mid(0, 4).toUInt32LE(0); d->formatID = data.mid(4, 4).toUInt32LE(0); diff --git a/taglib/dsf/dsfproperties.h b/taglib/dsf/dsfproperties.h index ed16b84d..0f48663c 100644 --- a/taglib/dsf/dsfproperties.h +++ b/taglib/dsf/dsfproperties.h @@ -41,19 +41,19 @@ namespace TagLib { * API. */ - class TAGLIB_EXPORT Properties : public AudioProperties + class TAGLIB_EXPORT AudioProperties : public TagLib::AudioProperties { public: /*! - * Create an instance of DSF::Properties with the data read from the + * Create an instance of DSF::AudioProperties with the data read from the * ByteVector \a data. */ - Properties(const ByteVector &data, ReadStyle style); + AudioProperties(const ByteVector &data, ReadStyle style); /*! - * Destroys this AIFF::Properties instance. + * Destroys this DSF::AudioProperties instance. */ - virtual ~Properties(); + virtual ~AudioProperties(); // Reimplementations. @@ -70,8 +70,8 @@ namespace TagLib { int blockSizePerChannel() const; private: - Properties(const Properties &); - Properties &operator=(const Properties &); + AudioProperties(const AudioProperties &); + AudioProperties &operator=(const AudioProperties &); void read(const ByteVector &data); From 8fc110dfd0cbd2266845f6020a0283720c82a7b9 Mon Sep 17 00:00:00 2001 From: "Stephen F. Booth" Date: Sat, 5 Oct 2013 13:59:59 -0400 Subject: [PATCH 05/14] Added some basic tests for DSF --- tests/CMakeLists.txt | 2 ++ tests/data/empty.dsf | Bin 0 -> 92 bytes tests/test_dsf.cpp | 31 +++++++++++++++++++++++++++++++ 3 files changed, 33 insertions(+) create mode 100644 tests/data/empty.dsf create mode 100644 tests/test_dsf.cpp diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 7a5bbbf9..5012065e 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -24,6 +24,7 @@ INCLUDE_DIRECTORIES( ${CMAKE_CURRENT_SOURCE_DIR}/../taglib/s3m ${CMAKE_CURRENT_SOURCE_DIR}/../taglib/it ${CMAKE_CURRENT_SOURCE_DIR}/../taglib/xm + ${CMAKE_CURRENT_SOURCE_DIR}/../taglib/dsf ${CMAKE_CURRENT_SOURCE_DIR}/../taglib/ebml ${CMAKE_CURRENT_SOURCE_DIR}/../taglib/ebml/matroska ) @@ -66,6 +67,7 @@ SET(test_runner_SRCS test_xm.cpp test_mpc.cpp test_opus.cpp + test_dsf.cpp test_matroska.cpp ) diff --git a/tests/data/empty.dsf b/tests/data/empty.dsf new file mode 100644 index 0000000000000000000000000000000000000000..373a73b46c1d74cbcc8370f1920a9cbe31fd74e7 GIT binary patch literal 92 ucmZ<>c2SUFfPfe%4WrU>OB76?97ZG>%ofyUfQf;n1Rx4h5=# +#include +#include +#include +#include +#include +#include "utils.h" + +using namespace std; +using namespace TagLib; + +class TestDSF : public CppUnit::TestFixture +{ + CPPUNIT_TEST_SUITE(TestDSF); + CPPUNIT_TEST(testBasic); + CPPUNIT_TEST_SUITE_END(); + +public: + + void testBasic() + { + DSF::File f(TEST_FILE_PATH_C("empty.dsf")); + DSF::AudioProperties *props = f.audioProperties(); + CPPUNIT_ASSERT_EQUAL(2822400, props->sampleRate()); + CPPUNIT_ASSERT_EQUAL(1, props->channels()); + CPPUNIT_ASSERT_EQUAL(1, props->bitsPerSample()); + } + +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(TestDSF); From e5c2232b10e0b34642ce11bd318f93335042c8b6 Mon Sep 17 00:00:00 2001 From: "Stephen F. Booth" Date: Sat, 5 Oct 2013 14:03:19 -0400 Subject: [PATCH 06/14] Added tag setting test --- tests/test_dsf.cpp | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/tests/test_dsf.cpp b/tests/test_dsf.cpp index f6c33ff3..8759ce39 100644 --- a/tests/test_dsf.cpp +++ b/tests/test_dsf.cpp @@ -13,6 +13,7 @@ class TestDSF : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(TestDSF); CPPUNIT_TEST(testBasic); + CPPUNIT_TEST(testTags); CPPUNIT_TEST_SUITE_END(); public: @@ -26,6 +27,22 @@ public: CPPUNIT_ASSERT_EQUAL(1, props->bitsPerSample()); } + void testTags() + { + ScopedFileCopy copy("empty", ".dsf"); + string newname = copy.fileName(); + + DSF::File *f = new DSF::File(newname.c_str()); + CPPUNIT_ASSERT_EQUAL(String(""), f->tag()->artist()); + f->tag()->setArtist("The Artist"); + f->save(); + delete f; + + f = new DSF::File(newname.c_str()); + CPPUNIT_ASSERT_EQUAL(String("The Artist"), f->tag()->artist()); + delete f; + } + }; CPPUNIT_TEST_SUITE_REGISTRATION(TestDSF); From 69bcc52ef380ee7cfd68bfc3c77ca2a1b2e03401 Mon Sep 17 00:00:00 2001 From: "Stephen F. Booth" Date: Fri, 29 Nov 2013 08:18:59 -0500 Subject: [PATCH 07/14] Invalidate the file when necessary --- taglib/dsf/dsffile.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/taglib/dsf/dsffile.cpp b/taglib/dsf/dsffile.cpp index 02460b93..5a674112 100644 --- a/taglib/dsf/dsffile.cpp +++ b/taglib/dsf/dsffile.cpp @@ -154,6 +154,7 @@ void DSF::File::read(bool readProperties, AudioProperties::ReadStyle propertiesS ByteVector chunkName = readBlock(4); if(chunkName != "DSD ") { debug("DSF::File::read() -- Not a DSF file."); + setValid(false); return; } @@ -162,6 +163,7 @@ void DSF::File::read(bool readProperties, AudioProperties::ReadStyle propertiesS // Integrity check if(28 != chunkSize) { debug("DSF::File::read() -- File is corrupted."); + setValid(false); return; } @@ -171,6 +173,7 @@ void DSF::File::read(bool readProperties, AudioProperties::ReadStyle propertiesS // File is malformed or corrupted if(d->metadataOffset > d->fileSize) { debug("DSF::File::read() -- Invalid metadata offset."); + setValid(false); return; } @@ -178,6 +181,7 @@ void DSF::File::read(bool readProperties, AudioProperties::ReadStyle propertiesS chunkName = readBlock(4); if(chunkName != "fmt ") { debug("DSF::File::read() -- Missing 'fmt ' chunk."); + setValid(false); return; } @@ -193,3 +197,4 @@ void DSF::File::read(bool readProperties, AudioProperties::ReadStyle propertiesS else d->tag = new ID3v2::Tag(this, d->metadataOffset); } + From ebaecc47f48d659abb49b41bced5ea3f1577b551 Mon Sep 17 00:00:00 2001 From: "Stephen F. Booth" Date: Fri, 29 Nov 2013 08:21:23 -0500 Subject: [PATCH 08/14] Correctly read all 8 bytes for the metadata offset --- taglib/dsf/dsffile.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/taglib/dsf/dsffile.cpp b/taglib/dsf/dsffile.cpp index 5a674112..860096a3 100644 --- a/taglib/dsf/dsffile.cpp +++ b/taglib/dsf/dsffile.cpp @@ -168,7 +168,7 @@ void DSF::File::read(bool readProperties, AudioProperties::ReadStyle propertiesS } d->fileSize = readBlock(8).toInt64LE(0); - d->metadataOffset = readBlock(8).toUInt32LE(0); + d->metadataOffset = readBlock(8).toInt64LE(0); // File is malformed or corrupted if(d->metadataOffset > d->fileSize) { From caf705958e9d664811a0ec5a951d92c0140fddae Mon Sep 17 00:00:00 2001 From: "Stephen F. Booth" Date: Fri, 29 Nov 2013 08:24:22 -0500 Subject: [PATCH 09/14] Validate the tag's version of file length against the actual length --- taglib/dsf/dsffile.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/taglib/dsf/dsffile.cpp b/taglib/dsf/dsffile.cpp index 860096a3..2000774e 100644 --- a/taglib/dsf/dsffile.cpp +++ b/taglib/dsf/dsffile.cpp @@ -168,6 +168,14 @@ void DSF::File::read(bool readProperties, AudioProperties::ReadStyle propertiesS } d->fileSize = readBlock(8).toInt64LE(0); + + // File is malformed or corrupted + if(d->fileSize != length()) { + debug("DSF::File::read() -- File is corrupted."); + setValid(false); + return; + } + d->metadataOffset = readBlock(8).toInt64LE(0); // File is malformed or corrupted From 733b537c63fc2471b2854947a66427a05a4871ef Mon Sep 17 00:00:00 2001 From: "Stephen F. Booth" Date: Fri, 29 Nov 2013 08:27:48 -0500 Subject: [PATCH 10/14] Read properties more efficiently --- taglib/dsf/dsfproperties.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/taglib/dsf/dsfproperties.cpp b/taglib/dsf/dsfproperties.cpp index e0c2c9cb..8484030e 100644 --- a/taglib/dsf/dsfproperties.cpp +++ b/taglib/dsf/dsfproperties.cpp @@ -135,14 +135,14 @@ int DSF::AudioProperties::blockSizePerChannel() const void DSF::AudioProperties::read(const ByteVector &data) { - d->formatVersion = data.mid(0, 4).toUInt32LE(0); - d->formatID = data.mid(4, 4).toUInt32LE(0); - d->channelType = data.mid(8, 4).toUInt32LE(0); - d->channelNum = data.mid(12, 4).toUInt32LE(0); - d->samplingFrequency = data.mid(16, 4).toUInt32LE(0); - d->bitsPerSample = data.mid(20, 4).toUInt32LE(0); - d->sampleCount = data.mid(24, 8).toInt64LE(0); - d->blockSizePerChannel = data.mid(32, 4).toUInt32LE(0); + d->formatVersion = data.toUInt32LE(0); + d->formatID = data.toUInt32LE(4); + d->channelType = data.toUInt32LE(8); + d->channelNum = data.toUInt32LE(12); + d->samplingFrequency = data.toUInt32LE(16); + d->bitsPerSample = data.toUInt32LE(20); + d->sampleCount = data.toInt64LE(24); + d->blockSizePerChannel = data.toUInt32LE(32); d->bitrate = (d->samplingFrequency * d->bitsPerSample * d->channelNum) / 1000.0; d->length = d->samplingFrequency > 0 ? d->sampleCount / d->samplingFrequency : 0; From dad73ebfc5d589d2b7218727ca085f946959a690 Mon Sep 17 00:00:00 2001 From: "Stephen F. Booth" Date: Fri, 29 Nov 2013 10:50:58 -0500 Subject: [PATCH 11/14] Strip tag if empty --- taglib/dsf/dsffile.cpp | 52 +++++++++++++++++++++++++++++------------- 1 file changed, 36 insertions(+), 16 deletions(-) diff --git a/taglib/dsf/dsffile.cpp b/taglib/dsf/dsffile.cpp index 2000774e..f0109cfb 100644 --- a/taglib/dsf/dsffile.cpp +++ b/taglib/dsf/dsffile.cpp @@ -116,28 +116,48 @@ bool DSF::File::save() // Three things must be updated: the file size, the tag data, and the metadata offset - ByteVector tagData = d->tag->render(); + long long newMetadataOffset = d->metadataOffset ? d->metadataOffset : d->fileSize; - long long metadataLocation = d->metadataOffset ? d->metadataOffset : d->fileSize; - long long newFileSize = metadataLocation + tagData.size(); - long long newMetadataOffset = tagData.size() ? metadataLocation : 0; - long long oldTagSize = d->fileSize - metadataLocation; + if(d->tag->isEmpty()) { + long long newFileSize = newMetadataOffset; - // Update the file size - if(d->fileSize != newFileSize) { - insert(ByteVector::fromUInt64LE(newFileSize), 12, 8); - d->fileSize = newFileSize; + // Update the file size + if(d->fileSize != newFileSize) { + insert(ByteVector::fromUInt64LE(newFileSize), 12, 8); + d->fileSize = newFileSize; + } + + // Update the metadata offset + if(d->metadataOffset != newMetadataOffset) { + insert(ByteVector::fromUInt64LE(newMetadataOffset), 20, 8); + d->metadataOffset = newMetadataOffset; + } + + // Delete the old tag + truncate(newMetadataOffset); } + else { + ByteVector tagData = d->tag->render(); - // Update the metadata offset - if(d->metadataOffset != newMetadataOffset) { - insert(ByteVector::fromUInt64LE(newMetadataOffset), 20, 8); - d->metadataOffset = newMetadataOffset; + long long newFileSize = newMetadataOffset + tagData.size(); + long long oldTagSize = d->fileSize - newMetadataOffset; + + // Update the file size + if(d->fileSize != newFileSize) { + insert(ByteVector::fromUInt64LE(newFileSize), 12, 8); + d->fileSize = newFileSize; + } + + // Update the metadata offset + if(d->metadataOffset != newMetadataOffset) { + insert(ByteVector::fromUInt64LE(newMetadataOffset), 20, 8); + d->metadataOffset = newMetadataOffset; + } + + // Delete the old tag and write the new one + insert(tagData, newMetadataOffset, oldTagSize); } - // Delete the old tag and write the new one - insert(tagData, metadataLocation, oldTagSize); - return true; } From 0c2e21024f77e2e0543266892f0ba7244b93e09f Mon Sep 17 00:00:00 2001 From: "Stephen F. Booth" Date: Fri, 29 Nov 2013 13:36:57 -0500 Subject: [PATCH 12/14] Correctly write the metadata offset in absence of ID3v2 tag --- taglib/dsf/dsffile.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/taglib/dsf/dsffile.cpp b/taglib/dsf/dsffile.cpp index f0109cfb..4cb927cf 100644 --- a/taglib/dsf/dsffile.cpp +++ b/taglib/dsf/dsffile.cpp @@ -127,9 +127,9 @@ bool DSF::File::save() d->fileSize = newFileSize; } - // Update the metadata offset + // Update the metadata offset to 0 since there is no longer a tag if(d->metadataOffset != newMetadataOffset) { - insert(ByteVector::fromUInt64LE(newMetadataOffset), 20, 8); + insert(ByteVector::fromUInt64LE(0ULL), 20, 8); d->metadataOffset = newMetadataOffset; } From d643878e9feb877a7e986dfcfc6481f210bffa10 Mon Sep 17 00:00:00 2001 From: "Stephen F. Booth" Date: Fri, 29 Nov 2013 13:38:54 -0500 Subject: [PATCH 13/14] Update internal state after saving --- taglib/dsf/dsffile.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/taglib/dsf/dsffile.cpp b/taglib/dsf/dsffile.cpp index 4cb927cf..1f8eb4c0 100644 --- a/taglib/dsf/dsffile.cpp +++ b/taglib/dsf/dsffile.cpp @@ -130,7 +130,7 @@ bool DSF::File::save() // Update the metadata offset to 0 since there is no longer a tag if(d->metadataOffset != newMetadataOffset) { insert(ByteVector::fromUInt64LE(0ULL), 20, 8); - d->metadataOffset = newMetadataOffset; + d->metadataOffset = 0; } // Delete the old tag From be041ef2aae9b78c482e810679aed846aaa8eb21 Mon Sep 17 00:00:00 2001 From: "Stephen F. Booth" Date: Fri, 29 Nov 2013 14:54:15 -0500 Subject: [PATCH 14/14] Another fix for tag stripping --- taglib/dsf/dsffile.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/taglib/dsf/dsffile.cpp b/taglib/dsf/dsffile.cpp index 1f8eb4c0..c79288a5 100644 --- a/taglib/dsf/dsffile.cpp +++ b/taglib/dsf/dsffile.cpp @@ -116,10 +116,8 @@ bool DSF::File::save() // Three things must be updated: the file size, the tag data, and the metadata offset - long long newMetadataOffset = d->metadataOffset ? d->metadataOffset : d->fileSize; - if(d->tag->isEmpty()) { - long long newFileSize = newMetadataOffset; + long long newFileSize = d->metadataOffset ? d->metadataOffset : d->fileSize; // Update the file size if(d->fileSize != newFileSize) { @@ -128,17 +126,18 @@ bool DSF::File::save() } // Update the metadata offset to 0 since there is no longer a tag - if(d->metadataOffset != newMetadataOffset) { + if(d->metadataOffset) { insert(ByteVector::fromUInt64LE(0ULL), 20, 8); d->metadataOffset = 0; } // Delete the old tag - truncate(newMetadataOffset); + 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;