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; }