TagLib 1.9 (In Development)
==========================
+ * Added support for the Ogg Opus file format.
* Added support for INFO tags in WAV files.
* Changed FileStream to use Windows file API.
* Included taglib-config.cmd script for Windows.
${CMAKE_CURRENT_SOURCE_DIR}/mp4
${CMAKE_CURRENT_SOURCE_DIR}/ogg/vorbis
${CMAKE_CURRENT_SOURCE_DIR}/ogg/speex
+ ${CMAKE_CURRENT_SOURCE_DIR}/ogg/opus
${CMAKE_CURRENT_SOURCE_DIR}/mpeg/id3v2
${CMAKE_CURRENT_SOURCE_DIR}/mpeg/id3v2/frames
${CMAKE_CURRENT_SOURCE_DIR}/mpeg/id3v1
ogg/flac/oggflacfile.h
ogg/speex/speexfile.h
ogg/speex/speexproperties.h
+ ogg/opus/opusfile.h
+ ogg/opus/opusproperties.h
flac/flacfile.h
flac/flacpicture.h
flac/flacproperties.h
ogg/speex/speexproperties.cpp
)
+set(opus_SRCS
+ ogg/opus/opusfile.cpp
+ ogg/opus/opusproperties.cpp
+)
+
set(trueaudio_SRCS
trueaudio/trueaudiofile.cpp
trueaudio/trueaudioproperties.cpp
${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}
+ ${asf_SRCS} ${mp4_SRCS} ${mod_SRCS} ${s3m_SRCS} ${it_SRCS} ${xm_SRCS} ${opus_SRCS}
tag.cpp
tagunion.cpp
fileref.cpp
#include "mp4file.h"
#include "wavpackfile.h"
#include "speexfile.h"
+#include "opusfile.h"
#include "trueaudiofile.h"
#include "aifffile.h"
#include "wavfile.h"
return new WavPack::File(fileName, readAudioProperties, audioPropertiesStyle);
if(ext == "SPX")
return new Ogg::Speex::File(fileName, readAudioProperties, audioPropertiesStyle);
+ if(ext == "OPUS")
+ return new Ogg::Opus::File(fileName, readAudioProperties, audioPropertiesStyle);
if(ext == "TTA")
return new TrueAudio::File(fileName, readAudioProperties, audioPropertiesStyle);
if(ext == "M4A" || ext == "M4R" || ext == "M4B" || ext == "M4P" || ext == "MP4" || ext == "3G2")
--- /dev/null
+/***************************************************************************
+ copyright : (C) 2012 by Lukáš Lalinský
+ email : lalinsky@gmail.com
+
+ copyright : (C) 2002 - 2008 by Scott Wheeler
+ email : wheeler@kde.org
+ (original Vorbis implementation)
+ ***************************************************************************/
+
+/***************************************************************************
+ * 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 <bitset>
+
+#include <tstring.h>
+#include <tdebug.h>
+
+#include "opusfile.h"
+
+using namespace TagLib;
+using namespace TagLib::Ogg;
+
+class Opus::File::FilePrivate
+{
+public:
+ FilePrivate() :
+ comment(0),
+ properties(0) {}
+
+ ~FilePrivate()
+ {
+ delete comment;
+ delete properties;
+ }
+
+ Ogg::XiphComment *comment;
+ Properties *properties;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// public members
+////////////////////////////////////////////////////////////////////////////////
+
+Opus::File::File(FileName file, bool readProperties,
+ Properties::ReadStyle propertiesStyle) : Ogg::File(file)
+{
+ d = new FilePrivate;
+ read(readProperties, propertiesStyle);
+}
+
+Opus::File::File(IOStream *stream, bool readProperties,
+ Properties::ReadStyle propertiesStyle) : Ogg::File(stream)
+{
+ d = new FilePrivate;
+ read(readProperties, propertiesStyle);
+}
+
+Opus::File::~File()
+{
+ delete d;
+}
+
+Ogg::XiphComment *Opus::File::tag() const
+{
+ return d->comment;
+}
+
+Opus::Properties *Opus::File::audioProperties() const
+{
+ return d->properties;
+}
+
+bool Opus::File::save()
+{
+ if(!d->comment)
+ d->comment = new Ogg::XiphComment;
+
+ setPacket(1, ByteVector("OpusTags", 8) + d->comment->render(false));
+
+ return Ogg::File::save();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// private members
+////////////////////////////////////////////////////////////////////////////////
+
+void Opus::File::read(bool readProperties, Properties::ReadStyle propertiesStyle)
+{
+ ByteVector opusHeaderData = packet(0);
+
+ if(!opusHeaderData.startsWith("OpusHead")) {
+ setValid(false);
+ debug("Opus::File::read() -- invalid Opus identification header");
+ return;
+ }
+
+ ByteVector commentHeaderData = packet(1);
+
+ if(!commentHeaderData.startsWith("OpusTags")) {
+ setValid(false);
+ debug("Opus::File::read() -- invalid Opus tags header");
+ return;
+ }
+
+ debug("starts with OpusTags");
+
+ d->comment = new Ogg::XiphComment(commentHeaderData.mid(8));
+
+ if(readProperties)
+ d->properties = new Properties(this, propertiesStyle);
+}
--- /dev/null
+/***************************************************************************
+ copyright : (C) 2012 by Lukáš Lalinský
+ email : lalinsky@gmail.com
+
+ copyright : (C) 2002 - 2008 by Scott Wheeler
+ email : wheeler@kde.org
+ (original Vorbis implementation)
+***************************************************************************/
+
+/***************************************************************************
+ * 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_OPUSFILE_H
+#define TAGLIB_OPUSFILE_H
+
+#include "oggfile.h"
+#include "xiphcomment.h"
+
+#include "opusproperties.h"
+
+namespace TagLib {
+
+ namespace Ogg {
+
+ //! A namespace containing classes for Opus metadata
+
+ namespace Opus {
+
+ //! An implementation of Ogg::File with Opus specific methods
+
+ /*!
+ * This is the central class in the Ogg Opus metadata processing collection
+ * of classes. It's built upon Ogg::File which handles processing of the Ogg
+ * logical bitstream and breaking it down into pages which are handled by
+ * the codec implementations, in this case Opus specifically.
+ */
+
+ class TAGLIB_EXPORT File : public Ogg::File
+ {
+ public:
+ /*!
+ * Contructs a Opus 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 a Opus 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.
+ *
+ * \note TagLib will *not* take ownership of the stream, the caller is
+ * responsible for deleting it after the File object.
+ */
+ File(IOStream *stream, bool readProperties = true,
+ Properties::ReadStyle propertiesStyle = Properties::Average);
+
+ /*!
+ * Destroys this instance of the File.
+ */
+ virtual ~File();
+
+ /*!
+ * Returns the XiphComment for this file. XiphComment implements the tag
+ * interface, so this serves as the reimplementation of
+ * TagLib::File::tag().
+ */
+ virtual Ogg::XiphComment *tag() const;
+
+ /*!
+ * Returns the Opus::Properties for this file. If no audio properties
+ * were read then this will return a null pointer.
+ */
+ virtual Properties *audioProperties() const;
+
+ virtual bool save();
+
+ private:
+ File(const File &);
+ File &operator=(const File &);
+
+ void read(bool readProperties, Properties::ReadStyle propertiesStyle);
+
+ class FilePrivate;
+ FilePrivate *d;
+ };
+ }
+ }
+}
+
+#endif
--- /dev/null
+/***************************************************************************
+ copyright : (C) 2012 by Lukáš Lalinský
+ email : lalinsky@gmail.com
+
+ copyright : (C) 2002 - 2008 by Scott Wheeler
+ email : wheeler@kde.org
+ (original Vorbis implementation)
+ ***************************************************************************/
+
+/***************************************************************************
+ * 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 <tstring.h>
+#include <tdebug.h>
+
+#include <oggpageheader.h>
+
+#include "opusproperties.h"
+#include "opusfile.h"
+
+using namespace TagLib;
+using namespace TagLib::Ogg;
+
+class Opus::Properties::PropertiesPrivate
+{
+public:
+ PropertiesPrivate(File *f, ReadStyle s) :
+ file(f),
+ style(s),
+ length(0),
+ inputSampleRate(0),
+ channels(0),
+ opusVersion(0) {}
+
+ File *file;
+ ReadStyle style;
+ int length;
+ int inputSampleRate;
+ int channels;
+ int opusVersion;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// public members
+////////////////////////////////////////////////////////////////////////////////
+
+Opus::Properties::Properties(File *file, ReadStyle style) : AudioProperties(style)
+{
+ d = new PropertiesPrivate(file, style);
+ read();
+}
+
+Opus::Properties::~Properties()
+{
+ delete d;
+}
+
+int Opus::Properties::length() const
+{
+ return d->length;
+}
+
+int Opus::Properties::bitrate() const
+{
+ return 0;
+}
+
+int Opus::Properties::sampleRate() const
+{
+ // Opus can decode any stream at a sample rate of 8, 12, 16, 24, or 48 kHz,
+ // so there is no single sample rate. Let's assume it's the highest
+ // possible.
+ return 48000;
+}
+
+int Opus::Properties::channels() const
+{
+ return d->channels;
+}
+
+int Opus::Properties::inputSampleRate() const
+{
+ return d->inputSampleRate;
+}
+
+int Opus::Properties::opusVersion() const
+{
+ return d->opusVersion;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// private members
+////////////////////////////////////////////////////////////////////////////////
+
+void Opus::Properties::read()
+{
+ // Get the identification header from the Ogg implementation.
+
+ // http://tools.ietf.org/html/draft-terriberry-oggopus-01#section-5.1
+
+ ByteVector data = d->file->packet(0);
+
+ // *Magic Signature*
+ int pos = 8;
+
+ // *Version* (8 bits, unsigned)
+ d->opusVersion = uchar(data.at(pos));
+ pos += 1;
+
+ // *Output Channel Count* 'C' (8 bits, unsigned)
+ d->channels = uchar(data.at(pos));
+ pos += 1;
+
+ // *Pre-skip* (16 bits, unsigned, little endian)
+ ushort preSkip = data.mid(pos, 2).toUShort(false);
+ pos += 2;
+
+ // *Input Sample Rate* (32 bits, unsigned, little endian)
+ d->inputSampleRate = data.mid(pos, 4).toUInt(false);
+ pos += 4;
+
+ // *Output Gain* (16 bits, signed, little endian)
+ pos += 2;
+
+ // *Channel Mapping Family* (8 bits, unsigned)
+ pos += 1;
+
+ const Ogg::PageHeader *first = d->file->firstPageHeader();
+ const Ogg::PageHeader *last = d->file->lastPageHeader();
+
+ if(first && last) {
+ long long start = first->absoluteGranularPosition();
+ long long end = last->absoluteGranularPosition();
+
+ if(start >= 0 && end >= 0)
+ d->length = (int) ((end - start - preSkip) / 48000);
+ else {
+ debug("Opus::Properties::read() -- The PCM values for the start or "
+ "end of this file was incorrect.");
+ }
+ }
+ else
+ debug("Opus::Properties::read() -- Could not find valid first and last Ogg pages.");
+}
--- /dev/null
+/***************************************************************************
+ copyright : (C) 2012 by Lukáš Lalinský
+ email : lalinsky@gmail.com
+
+ copyright : (C) 2002 - 2008 by Scott Wheeler
+ email : wheeler@kde.org
+ (original Vorbis implementation)
+***************************************************************************/
+
+/***************************************************************************
+ * 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_OPUSPROPERTIES_H
+#define TAGLIB_OPUSPROPERTIES_H
+
+#include "audioproperties.h"
+
+namespace TagLib {
+
+ namespace Ogg {
+
+ namespace Opus {
+
+ class File;
+
+ //! An implementation of audio property reading for Ogg Opus
+
+ /*!
+ * This reads the data from an Ogg Opus stream found in the AudioProperties
+ * API.
+ */
+
+ class TAGLIB_EXPORT Properties : public AudioProperties
+ {
+ public:
+ /*!
+ * Create an instance of Opus::Properties with the data read from the
+ * Opus::File \a file.
+ */
+ Properties(File *file, ReadStyle style = Average);
+
+ /*!
+ * Destroys this Opus::Properties instance.
+ */
+ virtual ~Properties();
+
+ // Reimplementations.
+
+ virtual int length() const;
+ virtual int bitrate() const;
+ virtual int sampleRate() const;
+ virtual int channels() const;
+
+ /*!
+ * The Opus codec supports decoding at multiple sample rates, there is no
+ * single sample rate of the encoded stream. This returns the sample rate
+ * of the original audio stream.
+ */
+ int inputSampleRate() const;
+
+ /*!
+ * Returns the Opus version, currently "0" (as specified by the spec).
+ */
+ int opusVersion() const;
+
+ private:
+ Properties(const Properties &);
+ Properties &operator=(const Properties &);
+
+ void read();
+
+ class PropertiesPrivate;
+ PropertiesPrivate *d;
+ };
+ }
+ }
+}
+
+#endif
#include "mp4file.h"
#include "wavpackfile.h"
#include "speexfile.h"
+#include "opusfile.h"
#include "trueaudiofile.h"
#include "aifffile.h"
#include "wavfile.h"
return dynamic_cast<const Ogg::FLAC::File* >(this)->properties();
if(dynamic_cast<const Ogg::Speex::File* >(this))
return dynamic_cast<const Ogg::Speex::File* >(this)->properties();
+ if(dynamic_cast<const Ogg::Opus::File* >(this))
+ return dynamic_cast<const Ogg::Opus::File* >(this)->properties();
if(dynamic_cast<const Ogg::Vorbis::File* >(this))
return dynamic_cast<const Ogg::Vorbis::File* >(this)->properties();
if(dynamic_cast<const RIFF::AIFF::File* >(this))
dynamic_cast<Ogg::FLAC::File* >(this)->removeUnsupportedProperties(properties);
else if(dynamic_cast<Ogg::Speex::File* >(this))
dynamic_cast<Ogg::Speex::File* >(this)->removeUnsupportedProperties(properties);
+ else if(dynamic_cast<Ogg::Opus::File* >(this))
+ dynamic_cast<Ogg::Opus::File* >(this)->removeUnsupportedProperties(properties);
else if(dynamic_cast<Ogg::Vorbis::File* >(this))
dynamic_cast<Ogg::Vorbis::File* >(this)->removeUnsupportedProperties(properties);
else if(dynamic_cast<RIFF::AIFF::File* >(this))
return dynamic_cast<Ogg::FLAC::File* >(this)->setProperties(properties);
else if(dynamic_cast<Ogg::Speex::File* >(this))
return dynamic_cast<Ogg::Speex::File* >(this)->setProperties(properties);
+ else if(dynamic_cast<Ogg::Opus::File* >(this))
+ return dynamic_cast<Ogg::Opus::File* >(this)->setProperties(properties);
else if(dynamic_cast<Ogg::Vorbis::File* >(this))
return dynamic_cast<Ogg::Vorbis::File* >(this)->setProperties(properties);
else if(dynamic_cast<RIFF::AIFF::File* >(this))
${CMAKE_CURRENT_SOURCE_DIR}/../taglib/ogg
${CMAKE_CURRENT_SOURCE_DIR}/../taglib/ogg/vorbis
${CMAKE_CURRENT_SOURCE_DIR}/../taglib/ogg/flac
+ ${CMAKE_CURRENT_SOURCE_DIR}/../taglib/ogg/speex
+ ${CMAKE_CURRENT_SOURCE_DIR}/../taglib/ogg/opus
${CMAKE_CURRENT_SOURCE_DIR}/../taglib/flac
${CMAKE_CURRENT_SOURCE_DIR}/../taglib/wavpack
${CMAKE_CURRENT_SOURCE_DIR}/../taglib/mod
test_it.cpp
test_xm.cpp
test_mpc.cpp
+ test_opus.cpp
)
INCLUDE_DIRECTORIES(${CPPUNIT_INCLUDE_DIR})
--- /dev/null
+#include <cppunit/extensions/HelperMacros.h>
+#include <string>
+#include <stdio.h>
+#include <tag.h>
+#include <tbytevectorlist.h>
+#include <opusfile.h>
+#include "utils.h"
+
+using namespace std;
+using namespace TagLib;
+
+class TestOpus : public CppUnit::TestFixture
+{
+ CPPUNIT_TEST_SUITE(TestOpus);
+ CPPUNIT_TEST(testProperties);
+ CPPUNIT_TEST(testReadComments);
+ CPPUNIT_TEST(testWriteComments);
+ CPPUNIT_TEST_SUITE_END();
+
+public:
+
+ void testProperties()
+ {
+ Ogg::Opus::File f(TEST_FILE_PATH_C("correctness_gain_silent_output.opus"));
+ CPPUNIT_ASSERT_EQUAL(7, f.audioProperties()->length());
+ CPPUNIT_ASSERT_EQUAL(0, f.audioProperties()->bitrate());
+ CPPUNIT_ASSERT_EQUAL(1, f.audioProperties()->channels());
+ CPPUNIT_ASSERT_EQUAL(48000, f.audioProperties()->sampleRate());
+ CPPUNIT_ASSERT_EQUAL(48000, ((Ogg::Opus::Properties *)f.audioProperties())->inputSampleRate());
+ }
+
+ void testReadComments()
+ {
+ Ogg::Opus::File f(TEST_FILE_PATH_C("correctness_gain_silent_output.opus"));
+ CPPUNIT_ASSERT_EQUAL(StringList("Xiph.Org Opus testvectormaker"), f.tag()->fieldListMap()["ENCODER"]);
+ CPPUNIT_ASSERT(f.tag()->fieldListMap().contains("TESTDESCRIPTION"));
+ CPPUNIT_ASSERT(!f.tag()->fieldListMap().contains("ARTIST"));
+ CPPUNIT_ASSERT_EQUAL(String("libopus 0.9.11-66-g64c2dd7"), f.tag()->vendorID());
+ }
+
+ void testWriteComments()
+ {
+ ScopedFileCopy copy("correctness_gain_silent_output", ".opus");
+ string filename = copy.fileName();
+
+ Ogg::Opus::File *f = new Ogg::Opus::File(filename.c_str());
+ f->tag()->setArtist("Your Tester");
+ f->save();
+ delete f;
+
+ f = new Ogg::Opus::File(filename.c_str());
+ CPPUNIT_ASSERT_EQUAL(StringList("Xiph.Org Opus testvectormaker"), f->tag()->fieldListMap()["ENCODER"]);
+ CPPUNIT_ASSERT(f->tag()->fieldListMap().contains("TESTDESCRIPTION"));
+ CPPUNIT_ASSERT_EQUAL(StringList("Your Tester"), f->tag()->fieldListMap()["ARTIST"]);
+ CPPUNIT_ASSERT_EQUAL(String("libopus 0.9.11-66-g64c2dd7"), f->tag()->vendorID());
+ delete f;
+ }
+
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(TestOpus);