9 Commits
v2.1 ... v2.1.1

Author SHA1 Message Date
Urs Fleisch
7d86716194 Version 2.1.1 2025-06-29 17:35:45 +02:00
Urs Fleisch
eb749ac55b Use ldexpl() instead of ldexp()
This will fix "warning: conversion from ‘long double’ to
‘double’ may change value [-Wfloat-conversion]".
2025-06-25 22:08:09 +02:00
Urs Fleisch
148cc9a921 Fix conversion compiler warnings
Using a release build with GCC 14.2.0 and -Wextra -Wconversion -Wall.
The generated binaries are not changed by this commit.
2025-06-25 22:08:09 +02:00
Urs Fleisch
88d6b18b4f Convert IPLS to TIPL and TMCL (#1274)
The involvement/involvee pairs which are supported for TIPL properties
(ARRANGER, ENGINEER, PRODUCER, DJ-MIX, MIX) are left in the TIPL
frame, other pairs are moved to a TMCL frame. This will result in a
consistent behavior for both ID3v2.3 and ID3v2.4 tags produced by
MusicBrainz Picard.
2025-06-21 10:45:43 +02:00
Urs Fleisch
d61a333f27 Map lowercase MusicBrainz TIPL keys to properties (#1274) 2025-06-21 10:45:43 +02:00
Urs Fleisch
e73517d058 CMake targets with TAGLIB_STATIC compile definition (#1277) 2025-06-20 10:19:01 +02:00
Urs Fleisch
6563ceaafa C bindings: Include missing wchar header in tag_c.h (#1273)
In C++, wchar_t is a built-in fundamental type, but in C, it is not.
Compilation with MinGW will fail without a header included which
defines wchar_t (wchar.h or stddef.h). The other compilers used in our
CI (gcc on Ubuntu, clang on macOS and MSVC on Windows) seem to
know wchar_t without an include.
2025-06-14 08:53:30 +02:00
Urs Fleisch
c57431e903 GitHub Actions: Build with MinGW 2025-06-14 08:53:30 +02:00
Urs Fleisch
42dcfec86b Use tag_c.h as first include in build with C compiler (#1273)
This will fail on MinGW because its gcc does not have wchar_t without
including wchar.h. With g++ as used with test_tag_c.cpp, the problem is
undetected because g++ supports wchar_t without including wchar.h.
2025-06-14 08:53:30 +02:00
36 changed files with 191 additions and 71 deletions

View File

@@ -9,10 +9,14 @@ jobs:
build:
strategy:
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
os: [ubuntu-latest, macos-latest, windows-latest, windows-2025]
include:
- os: windows-latest
cmake_extra_args: '-DCMAKE_TOOLCHAIN_FILE="$env:VCPKG_INSTALLATION_ROOT/scripts/buildsystems/vcpkg.cmake"'
- os: windows-2025
cmake_extra_args: '-G "MinGW Makefiles" -DCPPUNIT_INCLUDE_DIR="$env:GITHUB_WORKSPACE/pkg/usr/local/include" -DCPPUNIT_LIBRARIES="$($env:GITHUB_WORKSPACE -replace "\\", "/")/pkg/usr/local/lib/libcppunit.dll.a" -DCPPUNIT_INSTALLED_VERSION="1.15.1"'
# The windows-2025 runner is used for MinGW.
name: ${{ matrix.os == 'windows-2025' && 'mingw' || matrix.os }}
runs-on: ${{ matrix.os }}
steps:
- name: Checkout
@@ -32,6 +36,24 @@ jobs:
run: vcpkg install cppunit utfcpp zlib --triplet x64-windows
if: matrix.os == 'windows-latest'
- name: Set up MinGW
shell: bash
run: |
# Fetch utf8cpp, build cppunit
git submodule update --init
# Fails with fatal error: cppunit/config-auto.h: No such file or directory
# vcpkg install cppunit utfcpp zlib --triplet x64-mingw-dynamic
# Probably not working with CMake and MinGW, so we have to build it ourselves.
curl -sL "https://dev-www.libreoffice.org/src/cppunit-1.15.1.tar.gz" | tar xz
mkdir -p build_cppunit
(cd build_cppunit && MAKE=mingw32-make ../cppunit-1.15.1/configure \
--enable-shared=yes --enable-static=no \
--disable-dependency-tracking --disable-doxygen)
find build_cppunit -name Makefile -exec sed -i 's/\($[({]SHELL[)}]\)/"\1"/' {} \;
sed -i 's/^\(SUBDIRS.*\) examples\(.*\)$/\1\2/' build_cppunit/Makefile
(cd build_cppunit && mingw32-make -j$(nproc) install DESTDIR=$(cd .. && pwd)/pkg)
if: matrix.os == 'windows-2025'
- name: Configure
run: >
cmake -B${{github.workspace}}/build
@@ -41,12 +63,12 @@ jobs:
${{ matrix.cmake_extra_args }}
- name: Build
run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}}
run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} --parallel
- name: Test
working-directory: ${{github.workspace}}/build
run: ctest -C ${{env.BUILD_TYPE}} -V --no-tests=error
if: matrix.os != 'windows-latest'
if: matrix.os != 'windows-latest' && matrix.os != 'windows-2025'
- name: Test Windows
working-directory: ${{github.workspace}}/build
@@ -57,3 +79,10 @@ jobs:
$env:Path += ";$env:VCPKG_INSTALLATION_ROOT\packages\zlib_x64-windows\bin"
ctest -C ${{env.BUILD_TYPE}} -V --no-tests=error
if: matrix.os == 'windows-latest'
- name: Test MinGW
working-directory: ${{github.workspace}}/build
run: |
$env:Path += ";$PWD/taglib;$PWD/bindings/c;${{github.workspace}}/pkg/usr/local/bin"
ctest -C ${{env.BUILD_TYPE}} -V --no-tests=error
if: matrix.os == 'windows-2025'

View File

@@ -1,3 +1,10 @@
TagLib 2.1.1 (June 30, 2025)
============================
* Map ID3v2.3 IPLS frames to both ID3v2.4 TIPL and TMCL to have a consistent
behavior when using MusicBrainz tags with the property map interface.
* Fix missing include for `wchar_t` when using C bindings with MinGW.
TagLib 2.1 (May 31, 2025)
=========================

View File

@@ -19,9 +19,6 @@ if(APPLE)
set(FRAMEWORK_INSTALL_DIR "/Library/Frameworks" CACHE STRING "Directory to install frameworks to.")
endif()
endif()
if(NOT BUILD_SHARED_LIBS)
add_definitions(-DTAGLIB_STATIC)
endif()
option(ENABLE_STATIC_RUNTIME "Visual Studio, link with runtime statically" OFF)
option(ENABLE_CCACHE "Use ccache when building libtag" OFF)
@@ -96,7 +93,7 @@ endif()
# Patch version: increase it for bug fix releases.
set(TAGLIB_SOVERSION_MAJOR 2)
set(TAGLIB_SOVERSION_MINOR 1)
set(TAGLIB_SOVERSION_PATCH 0)
set(TAGLIB_SOVERSION_PATCH 1)
include(ConfigureChecks.cmake)

View File

@@ -112,6 +112,9 @@ set_target_properties(tag_c PROPERTIES
DEFINE_SYMBOL MAKE_TAGLIB_C_LIB
INSTALL_NAME_DIR ${CMAKE_INSTALL_FULL_LIBDIR}
)
if(NOT BUILD_SHARED_LIBS)
target_compile_definitions(tag_c PUBLIC TAGLIB_STATIC)
endif()
if(TAGLIB_INSTALL_SUFFIX)
if(BUILD_SHARED_LIBS)

View File

@@ -43,6 +43,7 @@ extern "C" {
#define TAGLIB_C_EXPORT
#endif
#include <wchar.h>
#ifdef _MSC_VER
/* minwindef.h contains typedef int BOOL */
#include <windows.h>

View File

@@ -22,11 +22,11 @@
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "tag_c.h"
#include <stdio.h>
#include <string.h>
#include "tag_c.h"
#ifndef FALSE
#define FALSE 0
#endif

View File

@@ -461,6 +461,9 @@ set_target_properties(tag PROPERTIES
INTERFACE_LINK_LIBRARIES "${ZLIB_INTERFACE_LINK_LIBRARIES}"
PUBLIC_HEADER "${tag_HDRS}"
)
if(NOT BUILD_SHARED_LIBS)
target_compile_definitions(tag PUBLIC TAGLIB_STATIC)
endif()
if(VISIBILITY_HIDDEN)
set_target_properties(tag PROPERTIES C_VISIBILITY_PRESET hidden)
set_target_properties(tag PROPERTIES CXX_VISIBILITY_PRESET hidden)

View File

@@ -139,7 +139,7 @@ void APE::Properties::read(File *file, offset_t streamLength)
if(d->sampleFrames > 0 && d->sampleRate > 0) {
const auto length = static_cast<double>(d->sampleFrames) * 1000.0 / d->sampleRate;
d->length = static_cast<int>(length + 0.5);
d->bitrate = static_cast<int>(streamLength * 8.0 / length + 0.5);
d->bitrate = static_cast<int>(static_cast<double>(streamLength) * 8.0 / length + 0.5);
}
}

View File

@@ -302,16 +302,16 @@ ByteVector ASF::Attribute::render(const String &name, int kind) const
if(kind == 0) {
data = renderString(name, true) +
ByteVector::fromShort(static_cast<int>(d->type), false) +
ByteVector::fromShort(data.size(), false) +
ByteVector::fromShort(static_cast<short>(d->type), false) +
ByteVector::fromShort(static_cast<short>(data.size()), false) +
data;
}
else {
ByteVector nameData = renderString(name);
data = ByteVector::fromShort(kind == 2 ? d->language : 0, false) +
ByteVector::fromShort(d->stream, false) +
ByteVector::fromShort(nameData.size(), false) +
ByteVector::fromShort(static_cast<int>(d->type), false) +
data = ByteVector::fromShort(static_cast<short>(kind == 2 ? d->language : 0), false) +
ByteVector::fromShort(static_cast<short>(d->stream), false) +
ByteVector::fromShort(static_cast<short>(nameData.size()), false) +
ByteVector::fromShort(static_cast<short>(d->type), false) +
ByteVector::fromUInt(data.size(), false) +
nameData +
data;

View File

@@ -221,7 +221,8 @@ void ASF::File::FilePrivate::FilePropertiesObject::parse(ASF::File *file, long l
const long long duration = data.toLongLong(40, false);
const long long preroll = data.toLongLong(56, false);
file->d->properties->setLengthInMilliseconds(static_cast<int>(duration / 10000.0 - preroll + 0.5));
file->d->properties->setLengthInMilliseconds(
static_cast<int>(static_cast<double>(duration) / 10000.0 - static_cast<double>(preroll) + 0.5));
}
ByteVector ASF::File::FilePrivate::StreamPropertiesObject::guid() const
@@ -271,11 +272,11 @@ ByteVector ASF::File::FilePrivate::ContentDescriptionObject::render(ASF::File *f
const ByteVector v4 = renderString(file->d->tag->comment());
const ByteVector v5 = renderString(file->d->tag->rating());
data.clear();
data.append(ByteVector::fromShort(v1.size(), false));
data.append(ByteVector::fromShort(v2.size(), false));
data.append(ByteVector::fromShort(v3.size(), false));
data.append(ByteVector::fromShort(v4.size(), false));
data.append(ByteVector::fromShort(v5.size(), false));
data.append(ByteVector::fromShort(static_cast<short>(v1.size()), false));
data.append(ByteVector::fromShort(static_cast<short>(v2.size()), false));
data.append(ByteVector::fromShort(static_cast<short>(v3.size()), false));
data.append(ByteVector::fromShort(static_cast<short>(v4.size()), false));
data.append(ByteVector::fromShort(static_cast<short>(v5.size()), false));
data.append(v1);
data.append(v2);
data.append(v3);
@@ -302,7 +303,7 @@ void ASF::File::FilePrivate::ExtendedContentDescriptionObject::parse(ASF::File *
ByteVector ASF::File::FilePrivate::ExtendedContentDescriptionObject::render(ASF::File *file)
{
data.clear();
data.append(ByteVector::fromShort(attributeData.size(), false));
data.append(ByteVector::fromShort(static_cast<short>(attributeData.size()), false));
data.append(attributeData.toByteVector(""));
return BaseObject::render(file);
}
@@ -325,7 +326,7 @@ void ASF::File::FilePrivate::MetadataObject::parse(ASF::File *file, long long /*
ByteVector ASF::File::FilePrivate::MetadataObject::render(ASF::File *file)
{
data.clear();
data.append(ByteVector::fromShort(attributeData.size(), false));
data.append(ByteVector::fromShort(static_cast<short>(attributeData.size()), false));
data.append(attributeData.toByteVector(""));
return BaseObject::render(file);
}
@@ -348,7 +349,7 @@ void ASF::File::FilePrivate::MetadataLibraryObject::parse(ASF::File *file, long
ByteVector ASF::File::FilePrivate::MetadataLibraryObject::render(ASF::File *file)
{
data.clear();
data.append(ByteVector::fromShort(attributeData.size(), false));
data.append(ByteVector::fromShort(static_cast<short>(attributeData.size()), false));
data.append(attributeData.toByteVector(""));
return BaseObject::render(file);
}

View File

@@ -90,7 +90,7 @@ namespace TagLib
{
ByteVector data = str.data(String::UTF16LE) + ByteVector::fromShort(0, false);
if(includeLength) {
data = ByteVector::fromShort(data.size(), false) + data;
data = ByteVector::fromShort(static_cast<short>(data.size()), false) + data;
}
return data;
}

View File

@@ -58,7 +58,7 @@ DSDIFF::Properties::Properties(unsigned int sampleRate,
d->sampleRate = sampleRate;
d->bitrate = bitrate;
d->length = d->sampleRate > 0
? static_cast<int>(d->sampleCount * 1000.0 / d->sampleRate + 0.5)
? static_cast<int>(static_cast<double>(d->sampleCount) * 1000.0 / d->sampleRate + 0.5)
: 0;
}

View File

@@ -59,7 +59,7 @@ bool DSF::File::isSupported(IOStream *stream)
return id.startsWith("DSD ");
}
DSF::File::File(FileName file, bool readProperties,
DSF::File::File(FileName file, bool,
AudioProperties::ReadStyle propertiesStyle,
ID3v2::FrameFactory *frameFactory) :
TagLib::File(file),
@@ -69,7 +69,7 @@ DSF::File::File(FileName file, bool readProperties,
read(propertiesStyle);
}
DSF::File::File(IOStream *stream, bool readProperties,
DSF::File::File(IOStream *stream, bool,
AudioProperties::ReadStyle propertiesStyle,
ID3v2::FrameFactory *frameFactory) :
TagLib::File(stream),

View File

@@ -129,6 +129,6 @@ void DSF::Properties::read(const ByteVector &data)
d->bitrate = static_cast<unsigned int>(
d->samplingFrequency * d->bitsPerSample * d->channelNum / 1000.0 + 0.5);
d->length = d->samplingFrequency > 0
? static_cast<unsigned int>(d->sampleCount * 1000.0 / d->samplingFrequency + 0.5)
? static_cast<unsigned int>(static_cast<double>(d->sampleCount) * 1000.0 / d->samplingFrequency + 0.5)
: 0;
}

View File

@@ -321,7 +321,7 @@ public:
return !file || !file->isValid();
}
bool isNullWithDebugMessage(const String &methodName) const
bool isNullWithDebugMessage([[maybe_unused]] const String &methodName) const
{
if(isNull()) {
debug("FileRef::" + methodName + "() - Called without a valid file.");

View File

@@ -274,7 +274,7 @@ bool FLAC::File::save()
it = d->blocks.erase(it);
continue;
}
blockHeader[0] = (*it)->code();
blockHeader[0] = static_cast<char>((*it)->code());
data.append(blockHeader);
data.append(blockData);
++it;

View File

@@ -134,7 +134,7 @@ void FLAC::Properties::read(const ByteVector &data, offset_t streamLength)
if(d->sampleFrames > 0 && d->sampleRate > 0) {
const auto length = static_cast<double>(d->sampleFrames) * 1000.0 / d->sampleRate;
d->length = static_cast<int>(length + 0.5);
d->bitrate = static_cast<int>(streamLength * 8.0 / length + 0.5);
d->bitrate = static_cast<int>(static_cast<double>(streamLength) * 8.0 / length + 0.5);
}
if(data.size() >= pos + 16)

View File

@@ -167,7 +167,7 @@ bool IT::File::save()
if(messageOffset + messageLength >= fileSize) {
// append new message
seek(54);
writeU16L(message.size());
writeU16L(static_cast<unsigned short>(message.size()));
writeU32L(messageOffset);
seek(messageOffset);
writeBlock(message);

View File

@@ -400,7 +400,7 @@ Map<ByteVector, String> ItemFactory::namePropertyMap() const
}
MP4::AtomDataList ItemFactory::parseData2(
const MP4::Atom *atom, const ByteVector &data, int expectedFlags,
const MP4::Atom *, const ByteVector &data, int expectedFlags,
bool freeForm)
{
AtomDataList result;
@@ -663,7 +663,7 @@ ByteVector ItemFactory::renderInt(
const ByteVector &name, const MP4::Item &item)
{
ByteVectorList data;
data.append(ByteVector::fromShort(item.toInt()));
data.append(ByteVector::fromShort(static_cast<short>(item.toInt())));
return renderData(name, TypeInteger, data);
}
@@ -703,8 +703,8 @@ ByteVector ItemFactory::renderIntPair(
{
ByteVectorList data;
data.append(ByteVector(2, '\0') +
ByteVector::fromShort(item.toIntPair().first) +
ByteVector::fromShort(item.toIntPair().second) +
ByteVector::fromShort(static_cast<short>(item.toIntPair().first)) +
ByteVector::fromShort(static_cast<short>(item.toIntPair().second)) +
ByteVector(2, '\0'));
return renderData(name, TypeImplicit, data);
}
@@ -714,8 +714,8 @@ ByteVector ItemFactory::renderIntPairNoTrailing(
{
ByteVectorList data;
data.append(ByteVector(2, '\0') +
ByteVector::fromShort(item.toIntPair().first) +
ByteVector::fromShort(item.toIntPair().second));
ByteVector::fromShort(static_cast<short>(item.toIntPair().first)) +
ByteVector::fromShort(static_cast<short>(item.toIntPair().second)));
return renderData(name, TypeImplicit, data);
}

View File

@@ -196,7 +196,7 @@ MP4::Properties::read(File *file, const Atoms *atoms)
}
}
if(unit > 0 && length > 0)
d->length = static_cast<int>(length * 1000.0 / unit + 0.5);
d->length = static_cast<int>(static_cast<double>(length) * 1000.0 / static_cast<double>(unit) + 0.5);
MP4::Atom *atom = trak->find("mdia", "minf", "stbl", "stsd");
if(!atom) {

View File

@@ -229,7 +229,7 @@ void MPC::Properties::readSV8(File *file, offset_t streamLength)
frameCount > 0 && d->sampleRate > 0) {
const auto length = static_cast<double>(frameCount) * 1000.0 / d->sampleRate;
d->length = static_cast<int>(length + 0.5);
d->bitrate = static_cast<int>(streamLength * 8.0 / length + 0.5);
d->bitrate = static_cast<int>(static_cast<double>(streamLength) * 8.0 / length + 0.5);
}
}
else if (packetType == "RG") {
@@ -331,6 +331,6 @@ void MPC::Properties::readSV7(const ByteVector &data, offset_t streamLength)
d->length = static_cast<int>(length + 0.5);
if(d->bitrate == 0)
d->bitrate = static_cast<int>(streamLength * 8.0 / length + 0.5);
d->bitrate = static_cast<int>(static_cast<double>(streamLength) * 8.0 / length + 0.5);
}
}

View File

@@ -176,7 +176,7 @@ void ID3v1::Tag::setComment(const String &s)
void ID3v1::Tag::setGenre(const String &s)
{
d->genre = ID3v1::genreIndex(s);
d->genre = static_cast<unsigned char>(ID3v1::genreIndex(s));
}
void ID3v1::Tag::setYear(unsigned int i)
@@ -186,7 +186,7 @@ void ID3v1::Tag::setYear(unsigned int i)
void ID3v1::Tag::setTrack(unsigned int i)
{
d->track = i < 256 ? i : 0;
d->track = static_cast<unsigned char>(i < 256 ? i : 0);
}
unsigned int ID3v1::Tag::genreNumber() const
@@ -196,7 +196,7 @@ unsigned int ID3v1::Tag::genreNumber() const
void ID3v1::Tag::setGenreNumber(unsigned int i)
{
d->genre = i < 256 ? i : 255;
d->genre = static_cast<unsigned char>(i < 256 ? i : 255);
}
void ID3v1::Tag::setStringHandler(const StringHandler *handler)

View File

@@ -315,7 +315,7 @@ PropertyMap TextIdentificationFrame::makeTIPLProperties() const
const StringList l = fieldList();
for(auto it = l.begin(); it != l.end(); ++it) {
auto found = std::find_if(involvedPeople.begin(), involvedPeople.end(),
[=](const auto &person) { return *it == person.first; });
[=](const auto &person) { return it->upper() == person.first; });
if(found != involvedPeople.end()) {
map.insert(found->second, (++it)->split(","));
}

View File

@@ -357,6 +357,49 @@ void FrameFactory::rebuildAggregateFrames(ID3v2::Tag *tag) const
}
}
}
if(tag->header()->majorVersion() < 4 &&
tag->frameList("TIPL").size() == 1 &&
tag->frameList("TMCL").size() == 0)
{
// FrameFactory::updateFrame() has mapped IPLS (ID3v2.3)/ IPL (ID3v2.2)
// to TIPL (ID3v2.4). However, the musicians should be rather in TMCL.
// Move all involvement/involvee pairs which are not supported by the
// TIPL property map interface to a TMCL frame.
if(auto tipl =
dynamic_cast<TextIdentificationFrame *>(tag->frameList("TIPL").front())) {
if(StringList tiplValues = tipl->toStringList(); tiplValues.size() % 2 == 0) {
static StringList tiplKeys;
if(tiplKeys.isEmpty()) {
for(const auto &kv : TextIdentificationFrame::involvedPeopleMap()) {
tiplKeys.append(kv.second);
}
}
StringList tmclValues;
for(auto it = tiplValues.begin(); it != tiplValues.end();) {
const String involvement = *it;
if(!tiplKeys.contains(involvement.upper())) {
tmclValues.append(involvement);
it = tiplValues.erase(it);
tmclValues.append(*it);
it = tiplValues.erase(it);
} else {
++it;
++it;
}
}
if(!tmclValues.isEmpty()) {
auto tmcl = new TextIdentificationFrame("TMCL");
tmcl->setText(tmclValues);
tag->addFrame(tmcl);
if(!tiplValues.isEmpty()) {
tipl->setText(tiplValues);
} else {
tag->removeFrame(tipl);
}
}
}
}
}
}
String::Type FrameFactory::defaultTextEncoding() const

View File

@@ -240,7 +240,7 @@ void MPEG::Properties::read(File *file, ReadStyle readStyle)
const Header lastHeader(file, lastFrameOffset, false);
if(const offset_t streamLength = lastFrameOffset - firstFrameOffset + lastHeader.frameLength();
streamLength > 0)
d->length = static_cast<int>(streamLength * 8.0 / d->bitrate + 0.5);
d->length = static_cast<int>(static_cast<double>(streamLength) * 8.0 / d->bitrate + 0.5);
}
}
}

View File

@@ -147,7 +147,7 @@ void Opus::Properties::read(File *file)
fileLengthWithoutOverhead -= file->packet(i).size();
}
d->length = static_cast<int>(length + 0.5);
d->bitrate = static_cast<int>(fileLengthWithoutOverhead * 8.0 / length + 0.5);
d->bitrate = static_cast<int>(static_cast<double>(fileLengthWithoutOverhead) * 8.0 / length + 0.5);
}
}
else {

View File

@@ -163,7 +163,7 @@ void Speex::Properties::read(File *file)
fileLengthWithoutOverhead -= file->packet(i).size();
}
d->length = static_cast<int>(length + 0.5);
d->bitrate = static_cast<int>(fileLengthWithoutOverhead * 8.0 / length + 0.5);
d->bitrate = static_cast<int>(static_cast<double>(fileLengthWithoutOverhead) * 8.0 / length + 0.5);
}
}
else {

View File

@@ -166,7 +166,7 @@ void Vorbis::Properties::read(File *file)
fileLengthWithoutOverhead -= file->packet(i).size();
}
d->length = static_cast<int>(length + 0.5);
d->bitrate = static_cast<int>(fileLengthWithoutOverhead * 8.0 / length + 0.5);
d->bitrate = static_cast<int>(static_cast<double>(fileLengthWithoutOverhead) * 8.0 / length + 0.5);
}
}
else {

View File

@@ -196,7 +196,7 @@ bool Shorten::File::isSupported(IOStream *stream)
return id.startsWith("ajkg");
}
Shorten::File::File(FileName file, bool readProperties,
Shorten::File::File(FileName file, bool,
AudioProperties::ReadStyle propertiesStyle) :
TagLib::File(file),
d(std::make_unique<FilePrivate>())
@@ -205,7 +205,7 @@ Shorten::File::File(FileName file, bool readProperties,
read(propertiesStyle);
}
Shorten::File::File(IOStream *stream, bool readProperties,
Shorten::File::File(IOStream *stream, bool,
AudioProperties::ReadStyle propertiesStyle) :
TagLib::File(stream),
d(std::make_unique<FilePrivate>())

View File

@@ -65,7 +65,7 @@ Shorten::Properties::Properties(const PropertyValues *values, ReadStyle style) :
d->bitrate = static_cast<int>(d->sampleRate * d->bitsPerSample * d->channelCount / 1000.0 + 0.5);
if(d->sampleRate > 0)
d->length = static_cast<int>(d->sampleFrames * 1000.0 / d->sampleRate + 0.5);
d->length = static_cast<int>(static_cast<double>(d->sampleFrames) * 1000.0 / d->sampleRate + 0.5);
}
}

View File

@@ -28,7 +28,7 @@
#define TAGLIB_MAJOR_VERSION 2
#define TAGLIB_MINOR_VERSION 1
#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)

View File

@@ -120,7 +120,8 @@ T toNumber(const ByteVector &v, size_t offset, size_t length, bool mostSignifica
T sum = 0;
for(size_t i = 0; i < length; i++) {
const size_t shift = (mostSignificantByteFirst ? length - 1 - i : i) * 8;
sum |= static_cast<T>(static_cast<unsigned char>(v[static_cast<int>(offset + i)])) << shift;
sum |= static_cast<T>(
static_cast<T>(static_cast<unsigned char>(v[static_cast<int>(offset + i)])) << shift);
}
return sum;
@@ -236,7 +237,7 @@ long double toFloat80(const ByteVector &v, size_t offset)
debug("toFloat80() - can't handle the infinity or NaN. Returning 0.");
return 0.0;
}
val = ::ldexp(static_cast<long double>(fraction), exponent - 16383 - 63);
val = ::ldexpl(static_cast<long double>(fraction), exponent - 16383 - 63);
}
if(negative)

View File

@@ -145,7 +145,7 @@ void TrueAudio::Properties::read(const ByteVector &data, offset_t streamLength)
if(d->sampleFrames > 0 && d->sampleRate > 0) {
const auto length = static_cast<double>(d->sampleFrames) * 1000.0 / d->sampleRate;
d->length = static_cast<int>(length + 0.5);
d->bitrate = static_cast<int>(streamLength * 8.0 / length + 0.5);
d->bitrate = static_cast<int>(static_cast<double>(streamLength) * 8.0 / length + 0.5);
}
}
}

View File

@@ -288,7 +288,7 @@ void WavPack::Properties::read(File *file, offset_t streamLength)
if(d->version < MIN_STREAM_VERS || d->version > MAX_STREAM_VERS)
break;
d->bitsPerSample = ((flags & BYTES_STORED) + 1) * 8 - ((flags & SHIFT_MASK) >> SHIFT_LSB);
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->sampleFrames = smplFrames;
@@ -308,7 +308,7 @@ void WavPack::Properties::read(File *file, offset_t streamLength)
if(d->sampleFrames > 0 && d->sampleRate > 0) {
const auto length = static_cast<double>(d->sampleFrames) * 1000.0 / d->sampleRate;
d->length = static_cast<int>(length + 0.5);
d->bitrate = static_cast<int>(streamLength * 8.0 / length + 0.5);
d->bitrate = static_cast<int>(static_cast<double>(streamLength) * 8.0 / length + 0.5);
}
}

View File

@@ -560,7 +560,7 @@ public:
// reverse
{
for(int i = 0; i < 256; i++){
all[i]=static_cast<unsigned char>(255)-i;
all[i]=static_cast<unsigned char>(255 - i);
}
ByteVector b64 = all.toBase64();
ByteVector original = ByteVector::fromBase64(b64);

View File

@@ -1035,7 +1035,7 @@ public:
tf->setText(StringList().append("Guitar").append("Artist 1").append("Drums").append("Artist 2"));
foo.ID3v2Tag()->addFrame(tf);
tf = new ID3v2::TextIdentificationFrame("TIPL", String::Latin1);
tf->setText(StringList().append("Producer").append("Artist 3").append("Mastering").append("Artist 4"));
tf->setText(StringList().append("Producer").append("Artist 3").append("Engineer").append("Artist 4"));
foo.ID3v2Tag()->addFrame(tf);
tf = new ID3v2::TextIdentificationFrame("TCON", String::Latin1);
tf->setText(StringList().append("51").append("Noise").append("Power Noise"));
@@ -1062,15 +1062,18 @@ public:
CPPUNIT_ASSERT_EQUAL(String("2012-04-17T12:01"), tf->fieldList().front());
tf = dynamic_cast<ID3v2::TextIdentificationFrame *>(bar.ID3v2Tag()->frameList("TIPL").front());
CPPUNIT_ASSERT(tf);
CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(8), tf->fieldList().size());
CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(4), tf->fieldList().size());
CPPUNIT_ASSERT_EQUAL(String("Producer"), tf->fieldList()[0]);
CPPUNIT_ASSERT_EQUAL(String("Artist 3"), tf->fieldList()[1]);
CPPUNIT_ASSERT_EQUAL(String("Engineer"), tf->fieldList()[2]);
CPPUNIT_ASSERT_EQUAL(String("Artist 4"), tf->fieldList()[3]);
tf = dynamic_cast<ID3v2::TextIdentificationFrame *>(bar.ID3v2Tag()->frameList("TMCL").front());
CPPUNIT_ASSERT(tf);
CPPUNIT_ASSERT_EQUAL(static_cast<unsigned int>(4), tf->fieldList().size());
CPPUNIT_ASSERT_EQUAL(String("Guitar"), tf->fieldList()[0]);
CPPUNIT_ASSERT_EQUAL(String("Artist 1"), tf->fieldList()[1]);
CPPUNIT_ASSERT_EQUAL(String("Drums"), tf->fieldList()[2]);
CPPUNIT_ASSERT_EQUAL(String("Artist 2"), tf->fieldList()[3]);
CPPUNIT_ASSERT_EQUAL(String("Producer"), tf->fieldList()[4]);
CPPUNIT_ASSERT_EQUAL(String("Artist 3"), tf->fieldList()[5]);
CPPUNIT_ASSERT_EQUAL(String("Mastering"), tf->fieldList()[6]);
CPPUNIT_ASSERT_EQUAL(String("Artist 4"), tf->fieldList()[7]);
tf = dynamic_cast<ID3v2::TextIdentificationFrame *>(bar.ID3v2Tag()->frameList("TCON").front());
CPPUNIT_ASSERT(tf);
CPPUNIT_ASSERT_EQUAL(3U, tf->fieldList().size());
@@ -1090,7 +1093,7 @@ public:
}
{
const ByteVector expectedId3v23Data(
"ID3" "\x03\x00\x00\x00\x00\x09\x49"
"ID3" "\x03\x00\x00\x00\x00\x09\x48"
"TSOA" "\x00\x00\x00\x01\x00\x00\x00"
"TSOT" "\x00\x00\x00\x01\x00\x00\x00"
"TSOP" "\x00\x00\x00\x01\x00\x00\x00"
@@ -1098,10 +1101,10 @@ public:
"TYER" "\x00\x00\x00\x05\x00\x00\x00" "2012"
"TDAT" "\x00\x00\x00\x05\x00\x00\x00" "1704"
"TIME" "\x00\x00\x00\x05\x00\x00\x00" "1201"
"IPLS" "\x00\x00\x00\x44\x00\x00\x00" "Guitar" "\x00"
"IPLS" "\x00\x00\x00\x43\x00\x00\x00" "Guitar" "\x00"
"Artist 1" "\x00" "Drums" "\x00" "Artist 2" "\x00" "Producer" "\x00"
"Artist 3" "\x00" "Mastering" "\x00" "Artist 4"
"TCON" "\x00\x00\x00\x14\x00\x00\x00" "(51)(39)Power Noise", 211);
"Artist 3" "\x00" "Engineer" "\x00" "Artist 4"
"TCON" "\x00\x00\x00\x14\x00\x00\x00" "(51)(39)Power Noise", 210);
const ByteVector actualId3v23Data =
PlainFile(newname.c_str()).readBlock(expectedId3v23Data.size());
CPPUNIT_ASSERT_EQUAL(expectedId3v23Data, actualId3v23Data);
@@ -1265,6 +1268,38 @@ public:
CPPUNIT_ASSERT_EQUAL(static_cast<ID3v2::UserTextIdentificationFrame *>(nullptr), ID3v2::UserTextIdentificationFrame::find(&tag, "non existing"));
CPPUNIT_ASSERT_EQUAL(frame10, ID3v2::CommentsFrame::findByDescription(&tag, "iTunNORM"));
CPPUNIT_ASSERT_EQUAL(static_cast<ID3v2::CommentsFrame *>(nullptr), ID3v2::CommentsFrame::findByDescription(&tag, "non existing"));
// Check if the allowed TIPL keys are correctly mapped to properties.
// These are the keys used by MusicBrainz: arranger, engineer, producer, DJ-mix, mix.
// See https://picard-docs.musicbrainz.org/downloads/MusicBrainz_Picard_Tag_Map.html.
// MusicBrainz Picard uses lowercase keys whereas TagLib used uppercase keys.
auto frame11 = new ID3v2::TextIdentificationFrame("TIPL");
StringList tiplData;
tiplData.append("arranger");
tiplData.append("an arranger");
tiplData.append("ENGINEER");
tiplData.append("an engineer");
tiplData.append("producer");
tiplData.append("a producer");
tiplData.append("DJ-mix");
tiplData.append("a DJ mixer");
tiplData.append("mix");
tiplData.append("a mixer");
frame11->setText(tiplData);
tag.addFrame(frame11);
properties = tag.properties();
CPPUNIT_ASSERT_EQUAL(0u, properties.unsupportedData().size());
CPPUNIT_ASSERT(properties.contains("ARRANGER"));
CPPUNIT_ASSERT(properties.contains("ENGINEER"));
CPPUNIT_ASSERT(properties.contains("PRODUCER"));
CPPUNIT_ASSERT(properties.contains("DJMIXER"));
CPPUNIT_ASSERT(properties.contains("MIXER"));
CPPUNIT_ASSERT_EQUAL(String("an arranger"), properties["ARRANGER"].front());
CPPUNIT_ASSERT_EQUAL(String("an engineer"), properties["ENGINEER"].front());
CPPUNIT_ASSERT_EQUAL(String("a producer"), properties["PRODUCER"].front());
CPPUNIT_ASSERT_EQUAL(String("a DJ mixer"), properties["DJMIXER"].front());
CPPUNIT_ASSERT_EQUAL(String("a mixer"), properties["MIXER"].front());
}
void testPropertiesMovement()