6 Commits

Author SHA1 Message Date
Urs Fleisch
208964e0bb Version 2.2.1 2026-02-27 06:27:25 +01:00
Urs Fleisch
3db0d48f4b Support edition, chapter and attachment UIDs in Matroska simple tags (#1311) 2026-02-24 06:53:23 +01:00
Urs Fleisch
de6e2b15c8 Version 2.2 2026-02-18 05:15:14 +01:00
Urs Fleisch
b7f0225f0d Do not allow unknown frames embedded in CTOC and CHAP (#1306)
This prevents the parsing of frames with specially constructed recursive
frame hierarchies from taking an extremely long time.
2026-02-15 08:38:14 +01:00
tsdgeos
f4117f873c wavpack: Fix infinite loop when reading broken files (#1304)
A crafted file can have blockSamples set to 0 and a blockSize so big
that when adding 8 it overflows and offset is 0 so it goes back to the
same position and loops forever
2026-02-04 17:31:59 +01:00
Damien Plisson
cf86f20b36 Add DSD information to WavPack properties (#1303) 2026-02-04 17:31:03 +01:00
19 changed files with 313 additions and 38 deletions

View File

@@ -1,8 +1,14 @@
TagLib 2.2.1 (Mar 7, 2026)
==========================
* Support edition, chapter and attachment UIDs in Matroska simple tags.
TagLib 2.2 (Feb 18, 2026)
=========================
* Support for Matroska (MKA, MKV) and WebM files.
* Support for NI STEM in MP4 files.
* New method isDsd() in WavPack Properties.
* Stricter verification of ID3v2 frames.
* Fix setting the last header flag in Ogg FLAC files.
* Fix reading of the last page in Ogg streams.

View File

@@ -12,7 +12,7 @@ include(CMakePackageConfigHelpers)
option(BUILD_SHARED_LIBS "Build shared libraries" OFF)
if(APPLE)
option(BUILD_FRAMEWORK "Build an OS X framework" OFF)
option(BUILD_FRAMEWORK "Build a macOS framework" OFF)
if(BUILD_FRAMEWORK)
set(BUILD_SHARED_LIBS ON)
#set(CMAKE_MACOSX_RPATH 1)
@@ -93,7 +93,7 @@ endif()
# Patch version: increase it for bug fix releases.
set(TAGLIB_SOVERSION_MAJOR 2)
set(TAGLIB_SOVERSION_MINOR 2)
set(TAGLIB_SOVERSION_PATCH 0)
set(TAGLIB_SOVERSION_PATCH 1)
include(ConfigureChecks.cmake)

View File

@@ -63,13 +63,13 @@ and ID3 tags cannot be disabled. The following CMake options are available:
| `WITH_APE` | Build with APE, MPC, WavPack (default ON) |
| `WITH_ASF` | Build with ASF (default ON) |
| `WITH_DSF` | Build with DSF (default ON) |
| `WITH_MATROSKA` | Build with Matroska (default ON) |
| `WITH_MATROSKA` | Build with Matroska, WebM (default ON) |
| `WITH_MOD` | Build with Tracker modules (default ON) |
| `WITH_MP4` | Build with MP4 (default ON) |
| `WITH_RIFF` | Build with AIFF, RIFF, WAV (default ON) |
| `WITH_SHORTEN` | Build with Shorten (default ON) |
| `WITH_TRUEAUDIO` | Build with TrueAudio (default ON) |
| `WITH_VORBIS` | Build with Vorbis, FLAC, Ogg, Opus (default ON) |
| `WITH_VORBIS` | Build with FLAC, Ogg, Opus, Speex (default ON) |
Note that disabling formats will remove exported symbols from the library and
thus break binary compatibility. These options should therefore only be used

View File

@@ -7,10 +7,11 @@
https://taglib.org/
TagLib is a library for reading and editing the metadata of several
popular audio formats. Currently, it supports both ID3v1 and ID3v2
for MP3 files, [Ogg Vorbis][] comments and ID3 tags
in [FLAC][], MPC, Speex, WavPack, TrueAudio, WAV, AIFF, MP4, APE, ASF,
DSF, DFF and AAC files.
popular audio formats. Currently, it supports various metadata containers such
as [ID3v1][], [ID3v2][] and [Vorbis][] comments for MP3, MP4, AAC,
[Ogg][], [Opus][], [FLAC][], [Speex][], [APE][], [MPC][], [WavPack][],
[WAV][], [AIFF][], [TrueAudio][], [Matroska][], [WebM][], ASF, WMA, DSF, DFF and
tracker (MOD, XM, S3M, IT) files.
TagLib is distributed under the [GNU Lesser General Public License][]
(LGPL) and [Mozilla Public License][] (MPL). Essentially that means that
@@ -18,7 +19,20 @@ it may be used in proprietary applications, but if changes are made to
TagLib they must be contributed back to the project. Please review the
licenses if you are considering using TagLib in your project.
[Ogg Vorbis]: https://xiph.org/vorbis/
[ID3v1]: https://id3.org/ID3v1
[ID3v2]: https://id3.org/Home
[Vorbis]: https://xiph.org/vorbis/
[Ogg]: https://www.xiph.org/ogg/
[Opus]: https://opus-codec.org/
[FLAC]: https://xiph.org/flac/
[Speex]: https://www.speex.org/
[APE]: https://www.monkeysaudio.com/
[MPC]: https://musepack.net/
[WavPack]: https://www.wavpack.com/
[WAV]: https://www.mmsp.ece.mcgill.ca/Documents/AudioFormats/WAVE/WAVE.html
[AIFF]: https://www.mmsp.ece.mcgill.ca/Documents/AudioFormats/AIFF/AIFF.html
[TrueAudio]: https://sourceforge.net/projects/tta/
[Matroska]: https://www.matroska.org/
[WebM]: https://www.webmproject.org/
[GNU Lesser General Public License]: https://www.gnu.org/licenses/lgpl.html
[Mozilla Public License]: https://www.mozilla.org/MPL/MPL-1.1.html

View File

@@ -51,6 +51,21 @@ int main(int argc, char *argv[])
TagLib::Utils::formatString("%llu",trackUid).toCString(false)
);
}
if(auto editionUid = t.editionUid()) {
PRINT_PRETTY("Edition UID",
TagLib::Utils::formatString("%llu",editionUid).toCString(false)
);
}
if(auto chapterUid = t.chapterUid()) {
PRINT_PRETTY("Chapter UID",
TagLib::Utils::formatString("%llu",chapterUid).toCString(false)
);
}
if(auto attachmentUid = t.attachmentUid()) {
PRINT_PRETTY("Attachment UID",
TagLib::Utils::formatString("%llu",attachmentUid).toCString(false)
);
}
const TagLib::String &language = t.language();
PRINT_PRETTY("Language", !language.isEmpty() ? language.toCString(false) : "Not set");

View File

@@ -84,6 +84,9 @@ std::unique_ptr<EBML::Element> EBML::Element::factory(File &file)
RETURN_ELEMENT_FOR_CASE(Id::MkCodecID);
RETURN_ELEMENT_FOR_CASE(Id::MkTagTargetTypeValue);
RETURN_ELEMENT_FOR_CASE(Id::MkTagTrackUID);
RETURN_ELEMENT_FOR_CASE(Id::MkTagEditionUID);
RETURN_ELEMENT_FOR_CASE(Id::MkTagChapterUID);
RETURN_ELEMENT_FOR_CASE(Id::MkTagAttachmentUID);
RETURN_ELEMENT_FOR_CASE(Id::MkTagsLanguageDefault);
RETURN_ELEMENT_FOR_CASE(Id::MkAttachedFileUID);
RETURN_ELEMENT_FOR_CASE(Id::MkSeekPosition);

View File

@@ -48,6 +48,9 @@ namespace TagLib
MkTagTargets = 0x63C0,
MkTagTargetTypeValue = 0x68CA,
MkTagTrackUID = 0x63C5,
MkTagEditionUID = 0x63C9,
MkTagChapterUID = 0x63C4,
MkTagAttachmentUID = 0x63C6,
MkSimpleTag = 0x67C8,
MkTagName = 0x45A3,
MkTagLanguage = 0x447A,
@@ -181,6 +184,9 @@ namespace TagLib
template <> struct GetElementTypeById<Element::Id::MkCodecID> { using type = Latin1StringElement; };
template <> struct GetElementTypeById<Element::Id::MkTagTargetTypeValue> { using type = UIntElement; };
template <> struct GetElementTypeById<Element::Id::MkTagTrackUID> { using type = UIntElement; };
template <> struct GetElementTypeById<Element::Id::MkTagEditionUID> { using type = UIntElement; };
template <> struct GetElementTypeById<Element::Id::MkTagChapterUID> { using type = UIntElement; };
template <> struct GetElementTypeById<Element::Id::MkTagAttachmentUID> { using type = UIntElement; };
template <> struct GetElementTypeById<Element::Id::MkAttachedFileUID> { using type = UIntElement; };
template <> struct GetElementTypeById<Element::Id::MkSeekPosition> { using type = UIntElement; };
template <> struct GetElementTypeById<Element::Id::MkTimestampScale> { using type = UIntElement; };

View File

@@ -69,6 +69,9 @@ std::unique_ptr<Matroska::Tag> EBML::MkTags::parse() const
// Parse the <Targets> element
Matroska::SimpleTag::TargetTypeValue targetTypeValue = Matroska::SimpleTag::TargetTypeValue::None;
unsigned long long trackUid = 0;
unsigned long long edtionUid = 0;
unsigned long long chapterUid = 0;
unsigned long long attachmentUid = 0;
if(targets) {
for(const auto &targetsChild : *targets) {
if(const Id id = targetsChild->getId(); id == Id::MkTagTargetTypeValue
@@ -80,6 +83,15 @@ std::unique_ptr<Matroska::Tag> EBML::MkTags::parse() const
else if(id == Id::MkTagTrackUID) {
trackUid = element_cast<Id::MkTagTrackUID>(targetsChild)->getValue();
}
else if(id == Id::MkTagEditionUID) {
edtionUid = element_cast<Id::MkTagEditionUID>(targetsChild)->getValue();
}
else if(id == Id::MkTagChapterUID) {
chapterUid = element_cast<Id::MkTagChapterUID>(targetsChild)->getValue();
}
else if(id == Id::MkTagAttachmentUID) {
attachmentUid = element_cast<Id::MkTagAttachmentUID>(targetsChild)->getValue();
}
}
}
@@ -109,10 +121,10 @@ std::unique_ptr<Matroska::Tag> EBML::MkTags::parse() const
mTag->addSimpleTag(tagValueString
? Matroska::SimpleTag(tagName, *tagValueString,
targetTypeValue, language, defaultLanguageFlag,
trackUid)
trackUid, edtionUid, chapterUid, attachmentUid)
: Matroska::SimpleTag(tagName, *tagValueBinary,
targetTypeValue, language, defaultLanguageFlag,
trackUid));
trackUid, edtionUid, chapterUid, attachmentUid));
}
}
return mTag;

View File

@@ -25,11 +25,14 @@
#include "tfile.h"
#include "matroskaproperties.h"
//! An implementation of Matroska metadata
namespace TagLib::Matroska {
class Tag;
class Attachments;
class Chapters;
//! An implementation of TagLib::File with Matroska specific methods
/*!
* Implementation of TagLib::File for Matroska.
*/

View File

@@ -30,19 +30,26 @@ class Matroska::SimpleTag::SimpleTagPrivate
public:
SimpleTagPrivate(const String &name, const String &value,
TargetTypeValue targetTypeValue, const String &language, bool defaultLanguage,
unsigned long long trackUid) :
unsigned long long trackUid, unsigned long long editionUid,
unsigned long long chapterUid, unsigned long long attachmentUid) :
value(value), name(name), language(language), trackUid(trackUid),
editionUid(editionUid), chapterUid(chapterUid), attachmentUid(attachmentUid),
targetTypeValue(targetTypeValue), defaultLanguageFlag(defaultLanguage) {}
SimpleTagPrivate(const String &name, const ByteVector &value,
TargetTypeValue targetTypeValue, const String &language, bool defaultLanguage,
unsigned long long trackUid) :
unsigned long long trackUid, unsigned long long editionUid,
unsigned long long chapterUid, unsigned long long attachmentUid) :
value(value), name(name), language(language), trackUid(trackUid),
editionUid(editionUid), chapterUid(chapterUid), attachmentUid(attachmentUid),
targetTypeValue(targetTypeValue), defaultLanguageFlag(defaultLanguage) {}
const std::variant<String, ByteVector> value;
const String name;
const String language;
const unsigned long long trackUid;
const unsigned long long editionUid;
const unsigned long long chapterUid;
const unsigned long long attachmentUid;
const TargetTypeValue targetTypeValue;
const bool defaultLanguageFlag;
};
@@ -56,7 +63,19 @@ Matroska::SimpleTag::SimpleTag(const String &name, const String &value,
const String &language, bool defaultLanguage,
unsigned long long trackUid) :
d(std::make_unique<SimpleTagPrivate>(name, value, targetTypeValue,
language, defaultLanguage, trackUid))
language, defaultLanguage, trackUid, 0, 0, 0))
{
}
Matroska::SimpleTag::SimpleTag(const String &name, const String &value,
TargetTypeValue targetTypeValue,
const String &language, bool defaultLanguage,
unsigned long long trackUid,
unsigned long long editionUid,
unsigned long long chapterUid,
unsigned long long attachmentUid) :
d(std::make_unique<SimpleTagPrivate>(name, value, targetTypeValue,
language, defaultLanguage, trackUid, editionUid, chapterUid, attachmentUid))
{
}
@@ -65,7 +84,19 @@ Matroska::SimpleTag::SimpleTag(const String &name, const ByteVector &value,
const String &language, bool defaultLanguage,
unsigned long long trackUid) :
d(std::make_unique<SimpleTagPrivate>(name, value, targetTypeValue,
language, defaultLanguage, trackUid))
language, defaultLanguage, trackUid, 0, 0, 0))
{
}
Matroska::SimpleTag::SimpleTag(const String &name, const ByteVector &value,
TargetTypeValue targetTypeValue,
const String &language, bool defaultLanguage,
unsigned long long trackUid,
unsigned long long editionUid,
unsigned long long chapterUid,
unsigned long long attachmentUid) :
d(std::make_unique<SimpleTagPrivate>(name, value, targetTypeValue,
language, defaultLanguage, trackUid, editionUid, chapterUid, attachmentUid))
{
}
@@ -118,6 +149,21 @@ unsigned long long Matroska::SimpleTag::trackUid() const
return d->trackUid;
}
unsigned long long Matroska::SimpleTag::editionUid() const
{
return d->editionUid;
}
unsigned long long Matroska::SimpleTag::chapterUid() const
{
return d->chapterUid;
}
unsigned long long Matroska::SimpleTag::attachmentUid() const
{
return d->attachmentUid;
}
Matroska::SimpleTag::ValueType Matroska::SimpleTag::type() const
{
return std::holds_alternative<ByteVector>(d->value) ? BinaryType : StringType;

View File

@@ -60,6 +60,14 @@ namespace TagLib {
const String &language = String(), bool defaultLanguage = true,
unsigned long long trackUid = 0);
/*!
* Construct a string simple tag.
*/
// BIC: merge with constructor above
SimpleTag(const String& name, const String& value, TargetTypeValue targetTypeValue, const String& language,
bool defaultLanguage, unsigned long long trackUid, unsigned long long editionUid,
unsigned long long chapterUid = 0, unsigned long long attachmentUid = 0);
/*!
* Construct a binary simple tag.
*/
@@ -68,6 +76,14 @@ namespace TagLib {
const String &language = String(), bool defaultLanguage = true,
unsigned long long trackUid = 0);
/*!
* Construct a binary simple tag.
*/
// BIC: merge with constructor above
SimpleTag(const String& name, const ByteVector& value, TargetTypeValue targetTypeValue, const String& language,
bool defaultLanguage, unsigned long long trackUid, unsigned long long editionUid,
unsigned long long chapterUid = 0, unsigned long long attachmentUid = 0);
/*!
* Construct a simple tag as a copy of \a other.
*/
@@ -124,6 +140,24 @@ namespace TagLib {
*/
unsigned long long trackUid() const;
/*!
* Returns the UID that identifies the edition that the tags belong to,
* zero if not defined, the tag applies to all editions
*/
unsigned long long editionUid() const;
/*!
* Returns the UID that identifies the chapter that the tags belong to,
* zero if not defined, the tag applies to all chapters
*/
unsigned long long chapterUid() const;
/*!
* Returns the UID that identifies the attachment that the tags belong to,
* zero if not defined, the tag applies to all attachments
*/
unsigned long long attachmentUid() const;
/*!
* Returns the type of the value.
*/

View File

@@ -121,11 +121,21 @@ void Matroska::Tag::removeSimpleTag(unsigned int index)
void Matroska::Tag::removeSimpleTag(const String &name,
SimpleTag::TargetTypeValue targetTypeValue,
unsigned long long trackUid)
{
removeSimpleTag(name, targetTypeValue, trackUid, 0, 0, 0);
}
void Matroska::Tag::removeSimpleTag(const String& name,
SimpleTag::TargetTypeValue targetTypeValue,
unsigned long long trackUid, unsigned long long editionUid,
unsigned long long chapterUid, unsigned long long attachmentUid)
{
const auto it = std::find_if(d->tags.begin(), d->tags.end(),
[&name, targetTypeValue, trackUid](const SimpleTag &t) {
[&name, targetTypeValue, trackUid, editionUid, chapterUid, attachmentUid](
const SimpleTag &t) {
return t.name() == name && t.targetTypeValue() == targetTypeValue &&
t.trackUid() == trackUid;
t.trackUid() == trackUid && t.editionUid() == editionUid &&
t.chapterUid() == chapterUid && t.attachmentUid() == attachmentUid;
}
);
if(it != d->tags.end()) {
@@ -260,12 +270,18 @@ ByteVector Matroska::Tag::renderInternal()
for(const auto &tag : std::as_const(d->tags)) {
auto targetTypeValue = tag.targetTypeValue();
auto trackUid = tag.trackUid();
auto editionUid = tag.editionUid();
auto chapterUid = tag.chapterUid();
auto attachmentUid = tag.attachmentUid();
auto it = std::find_if(targetList.begin(),
targetList.end(),
[&](const auto &list) {
const auto &simpleTag = list.front();
return simpleTag.targetTypeValue() == targetTypeValue &&
simpleTag.trackUid() == trackUid;
simpleTag.trackUid() == trackUid &&
simpleTag.editionUid() == editionUid &&
simpleTag.chapterUid() == chapterUid &&
simpleTag.attachmentUid() == attachmentUid;
}
);
if(it == targetList.end()) {
@@ -280,6 +296,9 @@ ByteVector Matroska::Tag::renderInternal()
const auto &frontTag = list.front();
const auto targetTypeValue = frontTag.targetTypeValue();
const auto trackUid = frontTag.trackUid();
const auto editionUid = frontTag.editionUid();
const auto chapterUid = frontTag.chapterUid();
const auto attachmentUid = frontTag.attachmentUid();
auto tag = EBML::make_unique_element<EBML::Element::Id::MkTag>();
// Build <Tag Targets> element
@@ -294,6 +313,21 @@ ByteVector Matroska::Tag::renderInternal()
element->setValue(trackUid);
targets->appendElement(std::move(element));
}
if(editionUid != 0) {
auto element = EBML::make_unique_element<EBML::Element::Id::MkTagEditionUID>();
element->setValue(editionUid);
targets->appendElement(std::move(element));
}
if(chapterUid != 0) {
auto element = EBML::make_unique_element<EBML::Element::Id::MkTagChapterUID>();
element->setValue(chapterUid);
targets->appendElement(std::move(element));
}
if(attachmentUid != 0) {
auto element = EBML::make_unique_element<EBML::Element::Id::MkTagAttachmentUID>();
element->setValue(attachmentUid);
targets->appendElement(std::move(element));
}
tag->appendElement(std::move(targets));
// Build <Simple Tag> element
@@ -446,7 +480,8 @@ String Matroska::Tag::TagPrivate::getTag(const String &key) const
&& t.type() == SimpleTag::StringType
&& (t.targetTypeValue() == targetTypeValue ||
(t.targetTypeValue() == SimpleTag::TargetTypeValue::None && !strict))
&& t.trackUid() == 0;
&& t.trackUid() == 0 && t.editionUid() == 0 && t.chapterUid() == 0
&& t.attachmentUid() == 0;
}
);
return it != tags.end() ? it->toString() : String();
@@ -456,7 +491,9 @@ PropertyMap Matroska::Tag::properties() const
{
PropertyMap properties;
for(const auto &simpleTag : std::as_const(d->tags)) {
if(simpleTag.type() == SimpleTag::StringType && simpleTag.trackUid() == 0) {
if(simpleTag.type() == SimpleTag::StringType && simpleTag.trackUid() == 0 &&
simpleTag.editionUid() == 0 && simpleTag.chapterUid() == 0 &&
simpleTag.attachmentUid() == 0) {
if(String key = translateTag(simpleTag.name(), simpleTag.targetTypeValue());
!key.isEmpty())
properties[key].append(simpleTag.toString());
@@ -472,7 +509,8 @@ PropertyMap Matroska::Tag::setProperties(const PropertyMap &propertyMap)
// Remove all simple tags which would be returned in properties()
for(auto it = d->tags.begin(); it != d->tags.end();) {
if(it->type() == SimpleTag::StringType &&
it->trackUid() == 0 &&
it->trackUid() == 0 && it->editionUid() == 0 &&
it->chapterUid() == 0 && it->attachmentUid() == 0 &&
!translateTag(it->name(), it->targetTypeValue()).isEmpty()) {
it = d->tags.erase(it);
setNeedsRender(true);
@@ -514,7 +552,8 @@ StringList Matroska::Tag::complexPropertyKeys() const
StringList keys;
for(const SimpleTag &t : std::as_const(d->tags)) {
if(t.type() != SimpleTag::StringType ||
t.trackUid() != 0 ||
t.trackUid() != 0 || t.editionUid() != 0 ||
t.chapterUid() != 0 || t.attachmentUid() != 0 ||
translateTag(t.name(), t.targetTypeValue()).isEmpty()) {
keys.append(t.name());
}
@@ -529,7 +568,8 @@ List<VariantMap> Matroska::Tag::complexProperties(const String &key) const
for(const SimpleTag &t : std::as_const(d->tags)) {
if(t.name() == key &&
(t.type() != SimpleTag::StringType ||
t.trackUid() != 0 ||
t.trackUid() != 0 || t.editionUid() != 0 ||
t.chapterUid() != 0 || t.attachmentUid() != 0 ||
translateTag(t.name(), t.targetTypeValue()).isEmpty())) {
VariantMap property;
if(t.type() != SimpleTag::StringType) {
@@ -545,6 +585,15 @@ List<VariantMap> Matroska::Tag::complexProperties(const String &key) const
if(t.trackUid()) {
property.insert("trackUid", t.trackUid());
}
if(t.editionUid()) {
property.insert("editionUid", t.editionUid());
}
if(t.chapterUid()) {
property.insert("chapterUid", t.chapterUid());
}
if(t.attachmentUid()) {
property.insert("attachmentUid", t.attachmentUid());
}
property.insert("language", t.language());
property.insert("defaultLanguage", t.defaultLanguageFlag());
props.append(property);
@@ -564,7 +613,8 @@ bool Matroska::Tag::setComplexProperties(const String &key, const List<VariantMa
[&key](const SimpleTag &t) {
return t.name() == key &&
(t.type() != SimpleTag::StringType ||
t.trackUid() != 0 ||
t.trackUid() != 0 || t.editionUid() != 0 ||
t.chapterUid() != 0 || t.attachmentUid() != 0 ||
translateTag(t.name(), t.targetTypeValue()).isEmpty());
}) > 0) {
setNeedsRender(true);
@@ -591,11 +641,16 @@ bool Matroska::Tag::setComplexProperties(const String &key, const List<VariantMa
auto language = property.value("language").value<String>();
const bool defaultLanguage = property.value("defaultLanguage", true).value<bool>();
const auto trackUid = property.value("trackUid", 0ULL).value<unsigned long long>();
const auto editionUid = property.value("editionUid", 0ULL).value<unsigned long long>();
const auto chapterUid = property.value("chapterUid", 0ULL).value<unsigned long long>();
const auto attachmentUid = property.value("attachmentUid", 0ULL).value<unsigned long long>();
d->tags.append(property.contains("data")
? SimpleTag(key, property.value("data").value<ByteVector>(),
targetTypeValue, language, defaultLanguage, trackUid)
targetTypeValue, language, defaultLanguage, trackUid,
editionUid, chapterUid, attachmentUid)
: SimpleTag(key, property.value("value").value<String>(),
targetTypeValue, language, defaultLanguage, trackUid));
targetTypeValue, language, defaultLanguage, trackUid,
editionUid, chapterUid, attachmentUid));
setNeedsRender(true);
result = true;
}

View File

@@ -120,6 +120,14 @@ namespace TagLib {
void removeSimpleTag(const String &name, SimpleTag::TargetTypeValue targetTypeValue,
unsigned long long trackUid = 0);
/*!
* Remove a tag attribute.
*/
// BIC: Merge with the method above
void removeSimpleTag(const String &name, SimpleTag::TargetTypeValue targetTypeValue,
unsigned long long trackUid, unsigned long long editionUid,
unsigned long long chapterUid = 0, unsigned long long attachmentUid = 0);
/*!
* Remove all tag attributes.
*/

View File

@@ -30,6 +30,7 @@
#include "tbytevectorlist.h"
#include "tdebug.h"
#include "tpropertymap.h"
#include "unknownframe.h"
using namespace TagLib;
using namespace ID3v2;
@@ -259,7 +260,7 @@ void ChapterFrame::parseFields(const ByteVector &data)
return;
// Checks to make sure that frame parsed correctly.
if(frame->size() <= 0) {
if(frame->size() <= 0 || dynamic_cast<UnknownFrame *>(frame)) {
delete frame;
return;
}

View File

@@ -29,6 +29,7 @@
#include "tpropertymap.h"
#include "tdebug.h"
#include "unknownframe.h"
using namespace TagLib;
using namespace ID3v2;
@@ -269,7 +270,7 @@ void TableOfContentsFrame::parseFields(const ByteVector &data)
return;
// Checks to make sure that frame parsed correctly.
if(frame->size() <= 0) {
if(frame->size() <= 0 || dynamic_cast<UnknownFrame *>(frame)) {
delete frame;
return;
}

View File

@@ -28,7 +28,7 @@
#define TAGLIB_MAJOR_VERSION 2
#define TAGLIB_MINOR_VERSION 2
#define TAGLIB_PATCH_VERSION 0
#define TAGLIB_PATCH_VERSION 1
#if (defined(_MSC_VER) && _MSC_VER >= 1600)
#define TAGLIB_CONSTRUCT_BITSET(x) static_cast<unsigned long long>(x)
@@ -77,7 +77,8 @@ namespace TagLib {
* - A clean, high level, C++ API for handling audio metadata.
* - Format specific APIs for advanced API users.
* - ID3v1, ID3v2, APE, FLAC, Xiph, iTunes-style MP4 and WMA tag formats.
* - MP3, MPC, FLAC, MP4, ASF, AIFF, WAV, DSF, DFF, TrueAudio, WavPack, Ogg FLAC, Ogg Vorbis, Speex and Opus file formats.
* - MP3, MPC, FLAC, MP4, ASF, AIFF, WAV, DSF, DFF, TrueAudio, WavPack, Ogg FLAC, Ogg Vorbis, Speex,
* Opus, Matroska and WebM file formats.
* - Basic audio file properties such as length, sample rate, etc.
* - Long term binary and source compatibility.
* - Extensible design, notably the ability to add other formats or extend current formats as a library user.

View File

@@ -51,6 +51,7 @@ public:
int version { 0 };
int bitsPerSample { 0 };
bool lossless { false };
bool dsd { false };
unsigned int sampleFrames { 0 };
};
@@ -102,6 +103,11 @@ bool WavPack::Properties::isLossless() const
return d->lossless;
}
bool WavPack::Properties::isDsd() const
{
return d->dsd;
}
unsigned int WavPack::Properties::sampleFrames() const
{
return d->sampleFrames;
@@ -254,16 +260,16 @@ void WavPack::Properties::read(File *file, offset_t streamLength)
const unsigned int flags = data.toUInt(24, false);
unsigned int smplRate = sampleRates[(flags & SRATE_MASK) >> SRATE_LSB];
if(!blockSamples) { // ignore blocks with no samples
offset += blockSize + 8;
continue;
}
if(blockSize < 24 || blockSize > 1048576) {
debug("WavPack::Properties::read() -- Invalid block header found.");
break;
}
if(!blockSamples) { // ignore blocks with no samples
offset += blockSize + 8;
continue;
}
// For non-standard sample rates or DSD audio files, we must read and parse the block
// to actually determine the sample rate.
@@ -291,6 +297,7 @@ void WavPack::Properties::read(File *file, offset_t streamLength)
d->bitsPerSample = static_cast<int>(((flags & BYTES_STORED) + 1) * 8 - ((flags & SHIFT_MASK) >> SHIFT_LSB));
d->sampleRate = static_cast<int>(smplRate);
d->lossless = !(flags & HYBRID_FLAG);
d->dsd = (flags & DSD_FLAG) != 0;
d->sampleFrames = smplFrames;
}

View File

@@ -96,6 +96,11 @@ namespace TagLib {
*/
bool isLossless() const;
/*!
* Returns whether or not the file is DSD (not PCM)
*/
bool isDsd() const;
/*!
* Returns the total number of audio samples in file.
*/

View File

@@ -220,7 +220,8 @@ public:
CPPUNIT_ASSERT(tag->isEmpty());
tag->addSimpleTag(Matroska::SimpleTag(
"Test Name 2", String("Test Value 2"),
Matroska::SimpleTag::TargetTypeValue::Album));
Matroska::SimpleTag::TargetTypeValue::Album,
{}, true, 0x72ac, 0xed17, 0xca97, 0xa7ac));
tag->insertSimpleTag(0, Matroska::SimpleTag(
"Test Name 1", String("Test Value 1"),
Matroska::SimpleTag::TargetTypeValue::Track, "en"));
@@ -263,6 +264,9 @@ public:
CPPUNIT_ASSERT_EQUAL(true, simpleTags[0].defaultLanguageFlag());
CPPUNIT_ASSERT_EQUAL(Matroska::SimpleTag::TargetTypeValue::Track, simpleTags[0].targetTypeValue());
CPPUNIT_ASSERT_EQUAL(0ULL, simpleTags[0].trackUid());
CPPUNIT_ASSERT_EQUAL(0ULL, simpleTags[0].editionUid());
CPPUNIT_ASSERT_EQUAL(0ULL, simpleTags[0].chapterUid());
CPPUNIT_ASSERT_EQUAL(0ULL, simpleTags[0].attachmentUid());
CPPUNIT_ASSERT_EQUAL(Matroska::SimpleTag::ValueType::StringType, simpleTags[0].type());
CPPUNIT_ASSERT_EQUAL(String("und"), simpleTags[1].language());
@@ -272,6 +276,9 @@ public:
CPPUNIT_ASSERT_EQUAL(true, simpleTags[1].defaultLanguageFlag());
CPPUNIT_ASSERT_EQUAL(Matroska::SimpleTag::TargetTypeValue::Track, simpleTags[1].targetTypeValue());
CPPUNIT_ASSERT_EQUAL(0ULL, simpleTags[1].trackUid());
CPPUNIT_ASSERT_EQUAL(0ULL, simpleTags[1].editionUid());
CPPUNIT_ASSERT_EQUAL(0ULL, simpleTags[1].chapterUid());
CPPUNIT_ASSERT_EQUAL(0ULL, simpleTags[1].attachmentUid());
CPPUNIT_ASSERT_EQUAL(Matroska::SimpleTag::ValueType::StringType, simpleTags[1].type());
CPPUNIT_ASSERT_EQUAL(String("und"), simpleTags[2].language());
@@ -281,6 +288,9 @@ public:
CPPUNIT_ASSERT_EQUAL(true, simpleTags[2].defaultLanguageFlag());
CPPUNIT_ASSERT_EQUAL(Matroska::SimpleTag::TargetTypeValue::Track, simpleTags[2].targetTypeValue());
CPPUNIT_ASSERT_EQUAL(0ULL, simpleTags[2].trackUid());
CPPUNIT_ASSERT_EQUAL(0ULL, simpleTags[2].editionUid());
CPPUNIT_ASSERT_EQUAL(0ULL, simpleTags[2].chapterUid());
CPPUNIT_ASSERT_EQUAL(0ULL, simpleTags[2].attachmentUid());
CPPUNIT_ASSERT_EQUAL(Matroska::SimpleTag::ValueType::StringType, simpleTags[2].type());
CPPUNIT_ASSERT_EQUAL(String("und"), simpleTags[3].language());
@@ -290,6 +300,9 @@ public:
CPPUNIT_ASSERT_EQUAL(true, simpleTags[3].defaultLanguageFlag());
CPPUNIT_ASSERT_EQUAL(Matroska::SimpleTag::TargetTypeValue::Track, simpleTags[3].targetTypeValue());
CPPUNIT_ASSERT_EQUAL(0ULL, simpleTags[3].trackUid());
CPPUNIT_ASSERT_EQUAL(0ULL, simpleTags[3].editionUid());
CPPUNIT_ASSERT_EQUAL(0ULL, simpleTags[3].chapterUid());
CPPUNIT_ASSERT_EQUAL(0ULL, simpleTags[3].attachmentUid());
CPPUNIT_ASSERT_EQUAL(Matroska::SimpleTag::ValueType::StringType, simpleTags[3].type());
CPPUNIT_ASSERT_EQUAL(String("und"), simpleTags[4].language());
@@ -298,7 +311,10 @@ public:
CPPUNIT_ASSERT_EQUAL(ByteVector(), simpleTags[4].toByteVector());
CPPUNIT_ASSERT_EQUAL(true, simpleTags[4].defaultLanguageFlag());
CPPUNIT_ASSERT_EQUAL(Matroska::SimpleTag::TargetTypeValue::Album, simpleTags[4].targetTypeValue());
CPPUNIT_ASSERT_EQUAL(0ULL, simpleTags[4].trackUid());
CPPUNIT_ASSERT_EQUAL(0x72acULL, simpleTags[4].trackUid());
CPPUNIT_ASSERT_EQUAL(0xed17ULL, simpleTags[4].editionUid());
CPPUNIT_ASSERT_EQUAL(0xca97ULL, simpleTags[4].chapterUid());
CPPUNIT_ASSERT_EQUAL(0xa7acULL, simpleTags[4].attachmentUid());
CPPUNIT_ASSERT_EQUAL(Matroska::SimpleTag::ValueType::StringType, simpleTags[4].type());
const auto &attachedFiles = attachments->attachedFileList();
@@ -315,7 +331,6 @@ public:
expectedProps["DATE"] = StringList("1969");
expectedProps["TEST NAME 1"] = StringList("Test Value 1");
expectedProps["TITLE"] = StringList("Test title");
expectedProps.addUnsupportedData("Test Name 2");
auto props = f.properties();
if (expectedProps != props) {
CPPUNIT_ASSERT_EQUAL(expectedProps.toString(), props.toString());
@@ -341,6 +356,10 @@ public:
{"name", "Test Name 2"},
{"value", "Test Value 2"},
{"targetTypeValue", 50},
{"trackUid", 0x72acULL},
{"editionUid", 0xed17ULL},
{"chapterUid", 0xca97ULL},
{"attachmentUid", 0xa7acULL},
};
auto complexProps = f.complexProperties("Test Name 2");
CPPUNIT_ASSERT(List<VariantMap>({expectedComplexProps}) == complexProps);
@@ -577,6 +596,9 @@ public:
CPPUNIT_ASSERT_EQUAL(true, simpleTags[0].defaultLanguageFlag());
CPPUNIT_ASSERT_EQUAL(Matroska::SimpleTag::TargetTypeValue::None, simpleTags[0].targetTypeValue());
CPPUNIT_ASSERT_EQUAL(9584013959154292683ULL, simpleTags[0].trackUid());
CPPUNIT_ASSERT_EQUAL(0ULL, simpleTags[0].editionUid());
CPPUNIT_ASSERT_EQUAL(0ULL, simpleTags[0].chapterUid());
CPPUNIT_ASSERT_EQUAL(0ULL, simpleTags[0].attachmentUid());
CPPUNIT_ASSERT_EQUAL(Matroska::SimpleTag::ValueType::StringType, simpleTags[0].type());
CPPUNIT_ASSERT_EQUAL(String("und"), simpleTags[1].language());
@@ -586,6 +608,9 @@ public:
CPPUNIT_ASSERT_EQUAL(true, simpleTags[1].defaultLanguageFlag());
CPPUNIT_ASSERT_EQUAL(Matroska::SimpleTag::TargetTypeValue::Track, simpleTags[1].targetTypeValue());
CPPUNIT_ASSERT_EQUAL(0ULL, simpleTags[1].trackUid());
CPPUNIT_ASSERT_EQUAL(0ULL, simpleTags[1].editionUid());
CPPUNIT_ASSERT_EQUAL(0ULL, simpleTags[1].chapterUid());
CPPUNIT_ASSERT_EQUAL(0ULL, simpleTags[1].attachmentUid());
CPPUNIT_ASSERT_EQUAL(Matroska::SimpleTag::ValueType::StringType, simpleTags[1].type());
CPPUNIT_ASSERT_EQUAL(String("und"), simpleTags[2].language());
@@ -595,6 +620,9 @@ public:
CPPUNIT_ASSERT_EQUAL(true, simpleTags[2].defaultLanguageFlag());
CPPUNIT_ASSERT_EQUAL(Matroska::SimpleTag::TargetTypeValue::Track, simpleTags[2].targetTypeValue());
CPPUNIT_ASSERT_EQUAL(0ULL, simpleTags[2].trackUid());
CPPUNIT_ASSERT_EQUAL(0ULL, simpleTags[2].editionUid());
CPPUNIT_ASSERT_EQUAL(0ULL, simpleTags[2].chapterUid());
CPPUNIT_ASSERT_EQUAL(0ULL, simpleTags[2].attachmentUid());
CPPUNIT_ASSERT_EQUAL(Matroska::SimpleTag::ValueType::StringType, simpleTags[2].type());
CPPUNIT_ASSERT_EQUAL(String("und"), simpleTags[3].language());
@@ -604,6 +632,9 @@ public:
CPPUNIT_ASSERT_EQUAL(true, simpleTags[3].defaultLanguageFlag());
CPPUNIT_ASSERT_EQUAL(Matroska::SimpleTag::TargetTypeValue::Track, simpleTags[3].targetTypeValue());
CPPUNIT_ASSERT_EQUAL(0ULL, simpleTags[3].trackUid());
CPPUNIT_ASSERT_EQUAL(0ULL, simpleTags[3].editionUid());
CPPUNIT_ASSERT_EQUAL(0ULL, simpleTags[3].chapterUid());
CPPUNIT_ASSERT_EQUAL(0ULL, simpleTags[3].attachmentUid());
CPPUNIT_ASSERT_EQUAL(Matroska::SimpleTag::ValueType::StringType, simpleTags[3].type());
CPPUNIT_ASSERT_EQUAL(String("und"), simpleTags[4].language());
@@ -613,6 +644,9 @@ public:
CPPUNIT_ASSERT_EQUAL(true, simpleTags[4].defaultLanguageFlag());
CPPUNIT_ASSERT_EQUAL(Matroska::SimpleTag::TargetTypeValue::Track, simpleTags[4].targetTypeValue());
CPPUNIT_ASSERT_EQUAL(0ULL, simpleTags[4].trackUid());
CPPUNIT_ASSERT_EQUAL(0ULL, simpleTags[4].editionUid());
CPPUNIT_ASSERT_EQUAL(0ULL, simpleTags[4].chapterUid());
CPPUNIT_ASSERT_EQUAL(0ULL, simpleTags[4].attachmentUid());
CPPUNIT_ASSERT_EQUAL(Matroska::SimpleTag::ValueType::StringType, simpleTags[4].type());
CPPUNIT_ASSERT_EQUAL(String("und"), simpleTags[5].language());
@@ -622,6 +656,9 @@ public:
CPPUNIT_ASSERT_EQUAL(true, simpleTags[5].defaultLanguageFlag());
CPPUNIT_ASSERT_EQUAL(Matroska::SimpleTag::TargetTypeValue::Track, simpleTags[5].targetTypeValue());
CPPUNIT_ASSERT_EQUAL(0ULL, simpleTags[5].trackUid());
CPPUNIT_ASSERT_EQUAL(0ULL, simpleTags[5].editionUid());
CPPUNIT_ASSERT_EQUAL(0ULL, simpleTags[5].chapterUid());
CPPUNIT_ASSERT_EQUAL(0ULL, simpleTags[5].attachmentUid());
CPPUNIT_ASSERT_EQUAL(Matroska::SimpleTag::ValueType::StringType, simpleTags[5].type());
CPPUNIT_ASSERT_EQUAL(String("und"), simpleTags[6].language());
@@ -631,6 +668,9 @@ public:
CPPUNIT_ASSERT_EQUAL(true, simpleTags[6].defaultLanguageFlag());
CPPUNIT_ASSERT_EQUAL(Matroska::SimpleTag::TargetTypeValue::Track, simpleTags[6].targetTypeValue());
CPPUNIT_ASSERT_EQUAL(0ULL, simpleTags[6].trackUid());
CPPUNIT_ASSERT_EQUAL(0ULL, simpleTags[6].editionUid());
CPPUNIT_ASSERT_EQUAL(0ULL, simpleTags[6].chapterUid());
CPPUNIT_ASSERT_EQUAL(0ULL, simpleTags[6].attachmentUid());
CPPUNIT_ASSERT_EQUAL(Matroska::SimpleTag::ValueType::StringType, simpleTags[6].type());
CPPUNIT_ASSERT_EQUAL(String("und"), simpleTags[7].language());
@@ -640,6 +680,9 @@ public:
CPPUNIT_ASSERT_EQUAL(true, simpleTags[7].defaultLanguageFlag());
CPPUNIT_ASSERT_EQUAL(Matroska::SimpleTag::TargetTypeValue::Track, simpleTags[7].targetTypeValue());
CPPUNIT_ASSERT_EQUAL(0ULL, simpleTags[7].trackUid());
CPPUNIT_ASSERT_EQUAL(0ULL, simpleTags[7].editionUid());
CPPUNIT_ASSERT_EQUAL(0ULL, simpleTags[7].chapterUid());
CPPUNIT_ASSERT_EQUAL(0ULL, simpleTags[7].attachmentUid());
CPPUNIT_ASSERT_EQUAL(Matroska::SimpleTag::ValueType::StringType, simpleTags[7].type());
CPPUNIT_ASSERT_EQUAL(String("und"), simpleTags[8].language());
@@ -649,6 +692,9 @@ public:
CPPUNIT_ASSERT_EQUAL(true, simpleTags[8].defaultLanguageFlag());
CPPUNIT_ASSERT_EQUAL(Matroska::SimpleTag::TargetTypeValue::Album, simpleTags[8].targetTypeValue());
CPPUNIT_ASSERT_EQUAL(0ULL, simpleTags[8].trackUid());
CPPUNIT_ASSERT_EQUAL(0ULL, simpleTags[8].editionUid());
CPPUNIT_ASSERT_EQUAL(0ULL, simpleTags[8].chapterUid());
CPPUNIT_ASSERT_EQUAL(0ULL, simpleTags[8].attachmentUid());
CPPUNIT_ASSERT_EQUAL(Matroska::SimpleTag::ValueType::StringType, simpleTags[8].type());
auto props = f.properties();
@@ -817,6 +863,9 @@ public:
CPPUNIT_ASSERT_EQUAL(trackUidTag.value("name").value<String>(), simpleTags[0].name());
CPPUNIT_ASSERT_EQUAL(trackUidTag.value("defaultLanguage").value<bool>(), simpleTags[0].defaultLanguageFlag());
CPPUNIT_ASSERT_EQUAL(trackUidTag.value("trackUid").value<unsigned long long>(), simpleTags[0].trackUid());
CPPUNIT_ASSERT_EQUAL(trackUidTag.value("editionUid").value<unsigned long long>(), simpleTags[0].editionUid());
CPPUNIT_ASSERT_EQUAL(trackUidTag.value("chapterUid").value<unsigned long long>(), simpleTags[0].chapterUid());
CPPUNIT_ASSERT_EQUAL(trackUidTag.value("attachmentUid").value<unsigned long long>(), simpleTags[0].attachmentUid());
CPPUNIT_ASSERT_EQUAL(Matroska::SimpleTag::TargetTypeValue::None, simpleTags[0].targetTypeValue());
CPPUNIT_ASSERT_EQUAL(ByteVector(), simpleTags[0].toByteVector());
CPPUNIT_ASSERT_EQUAL(Matroska::SimpleTag::ValueType::StringType, simpleTags[0].type());
@@ -844,6 +893,9 @@ public:
CPPUNIT_ASSERT_EQUAL(trackUidTag.value("name").value<String>(), simpleTags[0].name());
CPPUNIT_ASSERT_EQUAL(trackUidTag.value("defaultLanguage").value<bool>(), simpleTags[0].defaultLanguageFlag());
CPPUNIT_ASSERT_EQUAL(trackUidTag.value("trackUid").value<unsigned long long>(), simpleTags[0].trackUid());
CPPUNIT_ASSERT_EQUAL(trackUidTag.value("editionUid").value<unsigned long long>(), simpleTags[0].editionUid());
CPPUNIT_ASSERT_EQUAL(trackUidTag.value("chapterUid").value<unsigned long long>(), simpleTags[0].chapterUid());
CPPUNIT_ASSERT_EQUAL(trackUidTag.value("attachmentUid").value<unsigned long long>(), simpleTags[0].attachmentUid());
CPPUNIT_ASSERT_EQUAL(Matroska::SimpleTag::TargetTypeValue::None, simpleTags[0].targetTypeValue());
CPPUNIT_ASSERT_EQUAL(ByteVector(), simpleTags[0].toByteVector());
CPPUNIT_ASSERT_EQUAL(Matroska::SimpleTag::ValueType::StringType, simpleTags[0].type());
@@ -853,6 +905,9 @@ public:
CPPUNIT_ASSERT_EQUAL(targetTypeTag.value("name").value<String>(), simpleTags[1].name());
CPPUNIT_ASSERT_EQUAL(targetTypeTag.value("defaultLanguage").value<bool>(), simpleTags[1].defaultLanguageFlag());
CPPUNIT_ASSERT_EQUAL(targetTypeTag.value("trackUid").value<unsigned long long>(), simpleTags[1].trackUid());
CPPUNIT_ASSERT_EQUAL(targetTypeTag.value("editionUid").value<unsigned long long>(), simpleTags[1].editionUid());
CPPUNIT_ASSERT_EQUAL(targetTypeTag.value("chapterUid").value<unsigned long long>(), simpleTags[1].chapterUid());
CPPUNIT_ASSERT_EQUAL(targetTypeTag.value("attachmentUid").value<unsigned long long>(), simpleTags[1].attachmentUid());
CPPUNIT_ASSERT_EQUAL(Matroska::SimpleTag::TargetTypeValue::Subtrack, simpleTags[1].targetTypeValue());
CPPUNIT_ASSERT_EQUAL(ByteVector(), simpleTags[1].toByteVector());
CPPUNIT_ASSERT_EQUAL(Matroska::SimpleTag::ValueType::StringType, simpleTags[1].type());
@@ -862,6 +917,9 @@ public:
CPPUNIT_ASSERT_EQUAL(binaryTag.value("name").value<String>(), simpleTags[2].name());
CPPUNIT_ASSERT_EQUAL(binaryTag.value("defaultLanguage").value<bool>(), simpleTags[2].defaultLanguageFlag());
CPPUNIT_ASSERT_EQUAL(binaryTag.value("trackUid").value<unsigned long long>(), simpleTags[2].trackUid());
CPPUNIT_ASSERT_EQUAL(binaryTag.value("editionUid").value<unsigned long long>(), simpleTags[2].editionUid());
CPPUNIT_ASSERT_EQUAL(binaryTag.value("chapterUid").value<unsigned long long>(), simpleTags[2].chapterUid());
CPPUNIT_ASSERT_EQUAL(binaryTag.value("attachmentUid").value<unsigned long long>(), simpleTags[2].attachmentUid());
CPPUNIT_ASSERT_EQUAL(Matroska::SimpleTag::TargetTypeValue::Track, simpleTags[2].targetTypeValue());
CPPUNIT_ASSERT_EQUAL(String(), simpleTags[2].toString());
CPPUNIT_ASSERT_EQUAL(Matroska::SimpleTag::ValueType::BinaryType, simpleTags[2].type());