671 Commits

Author SHA1 Message Date
Urs Fleisch
78298769de Version 2.2.1 2026-03-07 06:41:13 +01:00
Urs Fleisch
c43d2b3fc1 Avoid duplicates in StringList Matroska::Tag::complexPropertyKeys()
When using for example

examples/tagwriter -C GENRE \
"name=GENRE,targetTypeValue=50,value=Soft Rock;name=GENRE,targetTypeValue=50,value=Classic Rock" \
path/to/file.mka

the GENRE key was included twice and tagreader displayed the two genre
tags twice.
2026-02-28 07:51:04 +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
Urs Fleisch
74d93db166 Update changelog for v2.2beta 2026-02-01 12:08:59 +01:00
Stephen Booth
397b6c1de3 Add check for ID3v2 frame data length (#1300)
Also fix some wrong frame sizes in the unit tests.

---------

Co-authored-by: Urs Fleisch <ufleisch@users.sourceforge.net>
2026-01-31 08:24:54 +01:00
Stephen Booth
51f431c96a Verify the ID3v2 version and revision are not 0xFF (#1301) 2026-01-31 08:21:17 +01:00
Antoine Colombier
11e3eb05bd MP4: Add support for NI STEM (#1299) 2026-01-25 13:33:54 +01:00
Urs Fleisch
2c01b63433 Merge pull request #1149 from complexlogic/matroska
Another Matroska Attempt
2026-01-24 15:27:22 +01:00
Urs Fleisch
d83d751443 Matroska: Fix build with older macOS and 32-bit Android compilers
When building for macOS < 10.14, the API for std::optional and
std::variant is restricted

    error: 'value' is unavailable: introduced in macOS 10.14
    error: 'get<..>' is unavailable: introduced in macOS 10.14

There was also an issue with Android armeabi-v7a where long is not
64 bit and a static assertion failed.
2026-01-24 14:59:10 +01:00
Urs Fleisch
f4e7a742c3 Improve Matroska API
Make AttachedFile immutable. This is consistent with SimpleTag and
Chapter and avoids using attached files which do not have all required
attributes.
Provide methods to insert and remove a single simple tag, so that
they can be modified without setting all of them while still not
exposing internal lists to the API.
Use DATE_RECORDED instead of DATE_RELEASED for year() and the "DATE"
property. This is more consistent with other tag formats, e.g. for ID3v2
"TDRC" is used, which is the recording time.
2026-01-24 14:59:10 +01:00
Urs Fleisch
68a514f4a0 Matroska: Avoid inlined header methods, only install public headers 2026-01-24 14:59:10 +01:00
Urs Fleisch
607cd5bdf4 Matroska: Fix issues reported by static analysis, formatting, docs 2026-01-24 14:59:10 +01:00
Urs Fleisch
b625be5690 Support for Matroska chapters 2026-01-24 14:59:10 +01:00
Urs Fleisch
d2bf7e72d2 Unit tests for Matroska 2026-01-24 14:59:10 +01:00
Urs Fleisch
280d6306d2 Update property mapping documentation for Matroska 2026-01-24 14:59:10 +01:00
Urs Fleisch
aef78068b3 Render when tags are modified 2026-01-24 14:59:10 +01:00
Urs Fleisch
81815e1091 Include doc type and version in properties 2026-01-24 14:59:10 +01:00
Urs Fleisch
b9122afaca Preserve track UID in simple tags
Some encoders write track specific DURATION tags, which should not be
removed.
2026-01-24 14:59:10 +01:00
Urs Fleisch
1d3a375765 Use segment title as fallback for returned tag title
Some applications like handbrake store the title only in the segment
info element.
2026-01-24 14:59:10 +01:00
Urs Fleisch
241b3b2921 Remove offset listeners, they are not used 2026-01-24 14:59:10 +01:00
Urs Fleisch
48104959b2 Integrate cues, fix element rendering and resizing 2026-01-24 14:59:10 +01:00
Urs Fleisch
c817cc0a47 tagwriter: Support setting of complex properties
A complex property can be set with

-C <complex-property-key> <key1=val1,key2=val2,...>

The second parameter can be set to "" to delete complex properties with the
given key. The set complex property values, a simple shorthand syntax can be
used. Multiple maps are separated by ';', values within a map are assigned
with key=value and separated by a ','. Types are automatically detected,
double quotes can be used to force a string. A ByteVector can be constructed
from the contents of a file with the path is given after "file://". There is
no escape, but hex codes are supported, e.g. "\x2C" to include a ',' and \x3B
to include a ';'.

Examples:

Set a GEOB frame in an ID3v2 tag:
examples/tagwriter -C GENERALOBJECT \
  'data=file://file.bin,description=My description,fileName=file.bin,mimeType=application/octet-stream' \
  file.mp3

Set an APIC frame in an ID3v2 tag (same as -p file.jpg 'My description'):
examples/tagwriter -C PICTURE \
  'data=file://file.jpg,description=My description,pictureType=Front Cover,mimeType=image/jpeg' \
  file.mp3

Set an attached file in a Matroska file:
examples/tagwriter -C file.bin \
  'fileName=file.bin,data=file://file.bin,mimeType=application/octet-stream' \
  file.mka

Set simple tag with target type in a Matroska file:
examples/tagwriter -C PART_NUMBER \
  name=PART_NUMBER,targetTypeValue=20,value=2 file.mka

Set simple tag with binary value in a Matroska file:
examples/tagwriter -C BINARY \
  name=BINARY,data=file://file.bin,targetTypeValue=60 file.mka
2026-01-24 14:59:10 +01:00
Urs Fleisch
7a5a10102e Set Matroska tags which are not in the property map using complex properties
Also all attached files can be accessed and modified using complex properties.
2026-01-24 14:59:10 +01:00
Urs Fleisch
d47d28f0f8 Fix and simplify Matroska simple tag
Avoid use of raw pointers, fix property interface.
2026-01-24 14:59:10 +01:00
Urs Fleisch
3566b00596 Clean up attachments
Avoid use of raw pointers.
2026-01-24 14:59:10 +01:00
Urs Fleisch
98bc68d16e Implement complex properties for Matroska 2026-01-24 14:59:10 +01:00
Urs Fleisch
8d02484804 Replace raw pointers by smart pointers
Also improve type safety and consistency.
2026-01-24 14:59:10 +01:00
Urs Fleisch
cfade6d082 Implement Matroska audio properties 2026-01-24 14:59:10 +01:00
Urs Fleisch
f7ab162514 Make tags and properties more flexible 2026-01-24 14:59:10 +01:00
Urs Fleisch
5a6f1f96f8 Apply TagLib code formatting, fix static analysis issues 2026-01-24 14:59:10 +01:00
Urs Fleisch
6fa3db1e51 C bindings, fileref, fix warnings 2026-01-24 14:59:10 +01:00
Urs Fleisch
4546c00417 Compile time configuration WITH_MATROSKA 2026-01-24 14:59:10 +01:00
complexlogic
f94843614f Initial cues work 2026-01-24 14:59:10 +01:00
complexlogic
975eaac3ca Fix seekhead 2026-01-24 14:59:10 +01:00
complexlogic
6d019a894c Initial attachments support 2026-01-24 14:59:10 +01:00
complexlogic
6342f00e8b Support tag language 2026-01-24 14:59:10 +01:00
complexlogic
b4e79a4a27 Fix Clang build 2026-01-24 14:59:10 +01:00
complexlogic
80837485cf Added write support 2026-01-24 14:59:10 +01:00
complexlogic
47e9b9a17c Initial matroska support 2026-01-24 14:59:10 +01:00
Stefan Brüns
8c7d3368da Fix reading of last page in ogg stream
When trying to read a packet from the last page the readPages() method
would return false for all packets on the last page.

Move the lastPage check just before trying to create the next page, and
after the packetIndex check of the last fetched page.
2026-01-18 20:08:09 +01:00
Stefan Brüns
b4a4b42254 Avoid corrupting an (invalid) FLAC Ogg file without Vorbis comment
FLAC in Ogg must have a Vorbis comment Metadata block, but in case
the file still has none avoid overwriting the first packet, i.e.
the Streaminfo Metadata block.
2026-01-18 20:08:09 +01:00
Stefan Brüns
70c6a0c75f Set last header flag in FLAC Metadata block type field
In case the Vorbis comment block is the last one the last block flag
has to be set.

As the other metadata blocks are kept as is, and in original order,
and no other metadata blocks are added/inserted, these can be kept
as is.

Also warn if the header scan loop detects a frame sync sequence (which
is the head of the first non-header packet). Previously, this was
detected as the last header packet, but streamStart and streamLength
are unused, and all packets but the vorbis comment packet are just copied
in order.

See taglib#1297
2026-01-18 20:08:09 +01:00
Stefan Brüns
c67e434939 Do not warn when seeing FLAC picture block in Ogg files 2026-01-18 20:08:09 +01:00
Urs Fleisch
5d44f3eeac MP4: Support 64-bit atoms on Windows
The check for the size of long is no longer needed because since
TagLib 2.0 the related fields are 64-bit on Windows too.

Related to https://bugs.kde.org/show_bug.cgi?id=513531
2026-01-11 20:24:33 +01:00
dependabot[bot]
9c042984d2 Bump actions/checkout from 5 to 6 (#1294)
Bumps [actions/checkout](https://github.com/actions/checkout) from 5 to 6.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v5...v6)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-24 06:57:09 -06:00
Rosen Penev
49be371caa std::accumulate conversion
Signed-off-by: Rosen Penev <rosenp@gmail.com>
2025-10-25 13:00:24 +02:00
Andreas Sturmlechner
cbb3de6836 Fix cmake_minimum_required version range typo
While CMake appears to accept the ".." notation, it does not correctly
apply the policy_max version in that case.

Amends ab31d11c8d

Signed-off-by: Andreas Sturmlechner <asturm@gentoo.org>
2025-10-22 16:37:21 +02:00
Rosen Penev
11efd2dd10 std::none_of conversion
Signed-off-by: Rosen Penev <rosenp@gmail.com>
2025-10-21 07:17:13 +02:00
Andreas Sturmlechner
ab31d11c8d Raise CMake minimum version to 3.10..3.31 range
CMake 3.10 was released in 2017.

Amends 967aaf7af2, fixes warning since CMake-3.31:

CMake Deprecation Warning at CMakeLists.txt:1 (cmake_minimum_required):
  Compatibility with CMake < 3.10 will be removed from a future version of
  CMake.

  Update the VERSION argument <min> value.  Or, use the <min>...<max> syntax
  to tell CMake that the project requires at least <min> but has been updated
  to work with policies introduced by <max> or earlier.

Signed-off-by: Andreas Sturmlechner <asturm@gentoo.org>
2025-10-18 17:00:09 +02:00
Artem Umerov
3c917caf52 Fix std::ostream not defined on Android NDK 29.0.14033849
Fix std::ostream not defined on Android NDK 29.0.14033849
2025-09-04 22:16:59 +02:00
dependabot[bot]
fa96a6458e Bump actions/checkout from 4 to 5 (#1283)
Bumps [actions/checkout](https://github.com/actions/checkout) from 4 to 5.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v4...v5)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: '5'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-18 07:03:51 -05:00
norma
e831f0929f Fix Unicode property keys in C bindings
The C bindings would convert a char* to String using the default
constructor, which uses the Latin1 encoding, breaking when a key
contains a Unicode character (e.g. an ID3v2 comment description).
2025-07-04 09:43:34 +02:00
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
Urs Fleisch
d48f02030d Version 2.1 2025-05-21 21:05:30 +02:00
Jamie
3ccc390155 Add file_new and file_new_type wchar variants for C binding (#1271) 2025-05-16 17:01:23 +02:00
Urs Fleisch
fbbead3efd Support custom temp and tests directories (#1268) (#1270)
The following user-settable values for CMake are supported:

- TESTS_DIR: Tests directory, is path to unit test data when 'data' is
  appended. Can be used to run the unit tests on a target.
- TESTS_TMPDIR: Directory for temporary files created during unit tests,
  system tmpdir is used if undefined. Has to be defined on systems
  without global temporary directory.
2025-05-01 19:55:46 +02:00
Urs Fleisch
ee1931b811 Compile time configuration of supported formats (#1262)
CMake options WITH_APE, WITH_ASF, WITH_DSF, WITH_MOD, WITH_MP4,
WITH_RIFF, WITH_SHORTEN, WITH_TRUEAUDIO, WITH_VORBIS, by default,
they are all ON.
2025-02-02 12:24:26 +01:00
Stephen Booth
648f5e5882 Add Shorten (SHN) support (#1257)
* Add Shorten (SHN) support

* Add `<cmath>` include and use `std::log2`

* Use `uintptr_t` for buffer size calculations

* Work around `byteSwap` not using fixed width types

* Remove four-character codes

* Attempt to fix `static_assert`

* Revert previous commit

* Update `read_uint`* functions

* Use ByteVector for byte swaps

* Use different ByteVector ctor

* Rework variable-length input to use ByteVector

* Rename some variables

* Naming and formatting cleanup

* Add basic Shorten tests

* Rename a constant

* Rename `internalFileType` to `fileType`

* Add documentation on `fileType` meaning

* Add DO_NOT_DOCUMENT guard

* Fix shadowVariable issues reported by cppcheck

cppcheck --enable=all --inline-suppr \
  --suppress=noExplicitConstructor --suppress=unusedFunction \
  --suppress=missingIncludeSystem --project=compile_commands.json

* Formatting cleanup

* More explicit types

Reason for these changes: getRiceGolombCode(k, uInt32CodeSize) was
called with int k for uint32_t& argument.
There was also a warning from MSVC for line 299:
warning C4267: 'argument': conversion from 'size_t' to 'int'

* Additional explicit types

* Rename `SHN` namespace to `Shorten`

Also rename files to match

---------

Co-authored-by: Urs Fleisch <ufleisch@users.sourceforge.net>
2024-12-30 07:23:11 -06:00
Stephen Booth
c1c60ebeea Use fixed width types (#1258)
Use fixed width types in `byteSwap` functions
2024-12-22 07:02:05 -06:00
Urs Fleisch
3bc0ea0ecb Preserve unicode encoding when downgrading to ID3v2.3 (#1259) (#1260) 2024-12-22 12:07:25 +01:00
Christian Schmitz
225c73e181 Fixed warning about shadowing variable (#1254)
You can't name parameter and structure field the same as complier frequently complain about this.
2024-11-22 06:55:34 +01:00
Urs Fleisch
90f62a3c94 Do not store too large FLAC metadata blocks (#1249) (#1250)
The size of FLAC metadata blocks is stored in only 24 bits. Remove
blocks exceeding this limit when saving FLAC and Ogg FLAC files.
2024-11-14 17:43:18 +01:00
Urs Fleisch
5b6f9ef848 Fix segfaults with String and ByteVector nullptr arguments (#1247) (#1248) 2024-11-02 06:39:21 +01:00
Urs Fleisch
e3de03501f Version 2.0.2 2024-08-24 06:40:41 +02:00
Urs Fleisch
1bd0d711ca Support older utfcpp versions with utf8cpp CMake target (#1243) (#1244)
This affects for example openSUSE Leap 15.6, which installs
utfcpp 3.2.1 in its own folder.
Now not only utf8::cpp, but also utf8cpp is supported as a CMake
target.
2024-08-15 12:44:17 +02:00
nekiwo
7c85fcaa81 Remove 'using namespace std' to avoid potential conflicts in example files (#1241)
Co-authored-by: nekiwo <nekiwo@users.noreply.github.com>
2024-08-05 21:54:33 +02:00
Urs Fleisch
cbe54d2f40 Support free form tags with MP4 properties (#1239) (#1240) 2024-07-29 20:24:33 +02:00
Urs Fleisch
c4ed590032 tagwriter option -p not working properly (#1236) (#1237)
The -p option of tagwriter sample does not work.
This is because the picture file is open in text mode instead of binary.
Also, the isFile function does not work on Windows in 32 bit mode with
large files. Using _stat64 instead of stat solves the problem.
2024-07-19 12:25:46 +02:00
Stephen Booth
f3fb4d83a4 Skip unknown MP4 boxes (#1231) 2024-05-18 06:45:10 +02:00
Urs Fleisch
3d4428726e Fix parsing of ID3v2.2 frames (#1228) 2024-05-18 06:43:00 +02:00
Urs Fleisch
ebf4c5bbb1 Version 2.0.1 2024-04-09 19:55:08 +02:00
Urs Fleisch
20cec27ac0 C bindings: Support UTF-8 for property values 2024-04-01 08:45:52 +02:00
Urs Fleisch
99bc87ccff Fix WASM build by inverting wchar_t size check
When building WASM with emscripten

cmake -DCMAKE_TOOLCHAIN_FILE=/path/to/Emscripten.cmake ...

all SIZEOF_ variables which should be defined in ConfigureChecks.cmake
are empty and the wchar_t check fails with "LESS" "2", the other
checks seem to pass since they start with NOT. Instead of explicitly
skipping the check for "if(NOT EMSCRIPTEN)" as is done in vcpkg's
disable-wchar-t-check-emscripten.patch, the check is inverted to
start with NOT, so the build still has a chance to run for compilers
which behave like emscripten.
2024-03-25 20:14:19 +01:00
Urs Fleisch
7951f572b5 Avoid offset_t conflict on Illumos
Taken from NetBSD patch-taglib_toolkit_taglib.h.
2024-03-25 20:14:19 +01:00
Urs Fleisch
59ff35772e Fix building with -DBUILD_TESTING=ON -DBUILD_BINDINGS=OFF 2024-03-25 20:14:19 +01:00
Urs Fleisch
3784628155 Provide equal operator for MP4::Item
This is needed to generate MP4::ItemMap bindings with SWIG.
2024-03-25 20:13:55 +01:00
Urs Fleisch
e60df53152 Add virtual to abstract overridden destructor
This is redundant, but required by SWIG.
2024-03-25 20:13:55 +01:00
Urs Fleisch
1ae8e18db5 Windows: Suppress yet another MSVC C4251 warning 2024-03-25 20:13:55 +01:00
Urs Fleisch
0896fb9092 Detect utf8cpp by header if cmake config is not found (#1217) 2024-02-03 06:28:40 +01:00
Jonas Kvinge
920d97606b FileStream: Fix opening long paths on Windows (#1216)
To make sure paths longer than MAX_PATH (260) can be opened, prefix local
paths with `\\?\`, and UNC paths with `\\?\UNC\`.

I've tested on Windows 10 22H2 (Build 19045.3930), even when setting
LongPathsEnabled to 1 in the registry, it still won't open files with long
paths without prefixing them.

For more information see:
https://learn.microsoft.com/en-us/windows/win32/fileio/maximum-file-path-limitation?tabs=registry
2024-01-28 16:22:14 +01:00
Urs Fleisch
0d2c31b102 Clarify 2.0 source compatibility, remove obsolete URL (#1214) 2024-01-28 07:17:05 +01:00
Urs Fleisch
c8c4e5faec Fix 'get() != pointer()' assertion copying ByteVectorList/StringList (#1211)
This reverts dfef09f13 but keeps the assignments as a comment so these
functions do not look like they can be defaulted even though they cannot.
2024-01-27 10:56:31 +01:00
Urs Fleisch
0ebb14b855 Version 2.0 2024-01-24 05:10:43 +01:00
Rosen Penev
89af92333c clang-tidy: use dynamic_cast
Found with cppcoreguidelines-pro-type-static-cast-downcast

Signed-off-by: Rosen Penev <rosenp@gmail.com>
2024-01-22 18:35:30 +01:00
Rosen Penev
b356fabe12 clang-tidy: avoid else after return
Found with: readability-else-after-return

Signed-off-by: Rosen Penev <rosenp@gmail.com>
2024-01-22 18:35:30 +01:00
Rosen Penev
8a65068f3b clang-tidy: fix wrong cast
Found with bugprone-misplaced-widening-cast

Signed-off-by: Rosen Penev <rosenp@gmail.com>
2024-01-22 18:35:30 +01:00
Rosen Penev
e1ac724cfe convert const double to const auto
Fixes some Wconversion warnings.

Signed-off-by: Rosen Penev <rosenp@gmail.com>
2024-01-22 18:35:30 +01:00
Urs Fleisch
a08acdcf23 Remove unused types from taglib.h 2024-01-21 20:46:27 +01:00
Urs Fleisch
1799b98c17 Inspection: Table is not correctly formatted 2024-01-21 20:46:27 +01:00
Urs Fleisch
e49390c7c5 Inspection: Type can be replaced with auto 2024-01-21 20:46:27 +01:00
Urs Fleisch
7bcfb96098 Inspection: Code redundancies 2024-01-21 20:46:27 +01:00
Urs Fleisch
580b0b0c82 Inspection: Possibly unused #include directive 2024-01-21 20:46:27 +01:00
Urs Fleisch
5fc3e5c192 Inspection: Possibly uninitialized class member 2024-01-21 20:46:27 +01:00
Urs Fleisch
570b40bdcd Inspection: Polymorphic class with non-virtual public destructor 2024-01-21 20:46:27 +01:00
Urs Fleisch
c3d73a26ff Inspection: Parameter names do not match 2024-01-21 20:46:27 +01:00
Urs Fleisch
790815bcf4 Inspection: Missing include guard 2024-01-21 20:46:27 +01:00
Urs Fleisch
1a5c417558 Inspection: Function is not implemented 2024-01-21 20:46:27 +01:00
Urs Fleisch
d87b2dad48 Inspection: Expression can be simplified 2024-01-21 20:46:27 +01:00
Urs Fleisch
613355665c Inspection: Variable can be moved to inner scope 2024-01-21 20:46:27 +01:00
Urs Fleisch
dfe2aa5253 Inspection: Variable can be moved to init statement 2024-01-21 20:46:27 +01:00
Urs Fleisch
5d921c6325 Inspection: Variable can be made constexpr 2024-01-21 20:46:27 +01:00
Urs Fleisch
710522e6e1 Inspection: Result of a postfix operator is discarded 2024-01-21 20:46:27 +01:00
Urs Fleisch
6b17aa3694 Inspection: Parameter can be made pointer to const 2024-01-21 20:46:27 +01:00
Urs Fleisch
b4f77a4d52 Inspection: Member function can be made const 2024-01-21 20:46:27 +01:00
Urs Fleisch
c907d8b273 Inspection: Functional-style cast is used instead of a C++ cast 2024-01-21 20:46:27 +01:00
Urs Fleisch
98175168f3 Inspection: Declaration and assignment can be joined 2024-01-21 20:46:27 +01:00
Urs Fleisch
73aff544b3 Inspection: C-style cast is used instead of a C++ cast 2024-01-21 20:46:27 +01:00
Rosen Penev
9cbb6615d5 clang-tidy: use using
Found with modernize-use-using

Signed-off-by: Rosen Penev <rosenp@gmail.com>
2024-01-19 21:57:55 +01:00
Rosen Penev
8b7b48cc9b clang-tidy: add ending namespace comments
Found with llvm-namespace
2024-01-19 21:57:55 +01:00
Rosen Penev
be8d71dad8 MSVC: fix signed/unsigned comparison
Signed-off-by: Rosen Penev <rosenp@gmail.com>
2024-01-19 21:57:55 +01:00
Rosen Penev
6abbe579a2 clang-tidy: remove virtual from prot destructors
Found with cppcoreguidelines-virtual-class-destructor

Signed-off-by: Rosen Penev <rosenp@gmail.com>
2024-01-19 21:57:55 +01:00
Rosen Penev
208fc93aaa cppcheck: match function argument names
Found with funcArgNamesDifferent

Signed-off-by: Rosen Penev <rosenp@gmail.com>
2024-01-19 21:57:55 +01:00
Rosen Penev
769feafbe0 cppcheck: include system headers with <>
Found with missingInclude

Signed-off-by: Rosen Penev <rosenp@gmail.com>
2024-01-19 21:57:55 +01:00
Rosen Penev
dfef09f134 cppcheck: assign d to a value
Found with operatorEqVarError, missingMemberCopy

Signed-off-by: Rosen Penev <rosenp@gmail.com>
2024-01-19 21:57:55 +01:00
Rosen Penev
3a003c1229 cppcheck: add const
Found with constVariablePointer, constParameterPointer

Signed-off-by: Rosen Penev <rosenp@gmail.com>
2024-01-19 21:57:55 +01:00
Rosen Penev
8b9a260d18 clang-tidy: fix doubled cases
Found with bugprone-branch-clone

Signed-off-by: Rosen Penev <rosenp@gmail.com>
2024-01-19 21:57:55 +01:00
Rosen Penev
c2eb6b59b5 clang-tidy: avoid endl
Found with performance-avoid-endl

Signed-off-by: Rosen Penev <rosenp@gmail.com>
2024-01-19 21:57:55 +01:00
Rosen Penev
99ba7635be clang-tidy: remove pointless const
Found with cppcoreguidelines-avoid-const-or-ref-data-members

Signed-off-by: Rosen Penev <rosenp@gmail.com>
2024-01-19 21:57:55 +01:00
Rosen Penev
82b5ded8ee tvariant: fix -Wconversion warning
char -> wchar_t. Just directly use int and remove the cast.

Signed-off-by: Rosen Penev <rosenp@gmail.com>
2024-01-19 21:57:55 +01:00
Urs Fleisch
0318201fbd Make classes with destructor as only virtual member non-virtual
These classes are probably not meant to be used polymorphically.
2024-01-17 21:25:44 +01:00
Urs Fleisch
6c1ba88eab Make Frame::Header::size(), Frame::headerSize() const 2024-01-17 21:25:44 +01:00
Urs Fleisch
fb0f7dfa57 More API documentation corrections 2024-01-17 20:54:56 +01:00
Urs Fleisch
c5d798a50d Correct the API documentation 2024-01-04 17:18:23 +01:00
Urs Fleisch
ef013b76db Add checks for the expected sizes of new public classes 2024-01-02 14:16:22 +01:00
Urs Fleisch
a6dbc70644 Fix shadowArgument issue reported by cppcheck 2024-01-02 13:50:11 +01:00
Urs Fleisch
9a026976ae Do not use std::visit() for std::variant
It is not supported for macOS 10.13 and earlier, see
https://stackoverflow.com/questions/53946674/noexcept-visitation-for-stdvariant.
2023-12-29 07:36:03 +01:00
Urs Fleisch
bd4c9cbf97 Support installation alongside TagLib 1 with -DTAGLIB_INSTALL_SUFFIX=-2 2023-12-24 14:16:15 +01:00
Urs Fleisch
7dc8bfc806 Fix property mappings
For MP4 map ENCODEDBY to ©enc instead of ©too, which is now mapped to
ENCODING.
For ASF, add new properties ENCODINGTIME (WM/EncodingTime) and FILEWEBPAGE
(WM/AudioFileURL).
2023-12-24 08:42:15 +01:00
Rosen Penev
1a1ee8b54f remove pointless static in namespace
Use constexpr too

Signed-off-by: Rosen Penev <rosenp@gmail.com>
2023-12-22 13:41:34 +01:00
Rosen Penev
cd044bfc8f clang-tidy: namespace comment
Found with llvm-namespace-comment

Signed-off-by: Rosen Penev <rosenp@gmail.com>
2023-12-22 13:41:34 +01:00
Rosen Penev
7eda31e6ef clang-tidy: use empty()
Found with readability-container-size-empty

Signed-off-by: Rosen Penev <rosenp@gmail.com>
2023-12-22 13:41:34 +01:00
Rosen Penev
0e6f888d4d clang-tidy: pass by value
Found with modernize-pass-by-value

Signed-off-by: Rosen Penev <rosenp@gmail.com>
2023-12-22 13:41:34 +01:00
Rosen Penev
b07db7510f clang-tidy: use const references
Found with performance-unnecessary-value-param

Signed-off-by: Rosen Penev <rosenp@gmail.com>
2023-12-22 13:41:34 +01:00
Rosen Penev
1635d4d563 clang-tidy: use auto
Found with modernize-use-auto

Signed-off-by: Rosen Penev <rosenp@gmail.com>
2023-12-22 13:41:34 +01:00
Rosen Penev
6d0f0ad170 use to_string
Lighterweight function.

Signed-off-by: Rosen Penev <rosenp@gmail.com>
2023-12-22 13:41:34 +01:00
Rosen Penev
eaf7955c63 gcc: remove old style cast
Signed-off-by: Rosen Penev <rosenp@gmail.com>
2023-12-22 13:41:34 +01:00
Rosen Penev
78c489d9cc clang: remove redundant comma
Signed-off-by: Rosen Penev <rosenp@gmail.com>
2023-12-22 13:41:34 +01:00
Urs Fleisch
56fa36934e Unify File constructors with ID3v2::FrameFactory parameter (#1196)
Make constructors consistent so that the FrameFactory is at the
end and optional. Mark the alternative constructors as deprecated.
2023-12-22 13:41:13 +01:00
Urs Fleisch
0dff3150c1 Remove UTF16LE means swap compatibility hack 2023-12-22 13:40:56 +01:00
Urs Fleisch
e414987344 C bindings: Add missing types for taglib_file_new_type() 2023-12-22 13:40:18 +01:00
Urs Fleisch
47184c4447 Update documentation for version 2.0 2023-12-22 13:39:51 +01:00
Urs Fleisch
ab0437db0e Apply VISIBILITY_HIDDEN also to C++ files
It was only used for C files, thereby making symbols of internal classes
like TagUnion visible.
2023-12-20 21:30:15 +01:00
Stephen F. Booth
1ee7493abc Use offset_t in XM::File::save() 2023-12-20 12:05:26 +01:00
Stephen F. Booth
d869f1b3e4 Fix implicit conversion 2023-12-20 12:02:38 +01:00
Stephen F. Booth
2044b31698 Fix chunk ID validation
According to EA IFF 85:

Type IDs

A "type ID", "property name", "FORM type", or any other IFF identifier
is a 32-bit value: the concatenation of four ASCII characters in the
range R S (SP, hex 20) through R~S (hex 7E). Spaces (hex 20) should
not precede printing characters; trailing spaces are ok. Control characters
are forbidden.
2023-12-18 17:29:39 +01:00
Stephen F. Booth
6e0741bcdc Fix incorrect commit 2023-12-18 17:29:39 +01:00
Stephen F. Booth
e9a671476b Remove extraneous ; 2023-12-18 17:29:39 +01:00
Stephen F. Booth
5b19c7aed3 Remove unnecessary comparison 2023-12-18 17:29:39 +01:00
Stephen F. Booth
98a9530d23 Fix potential fallthrough 2023-12-18 17:29:39 +01:00
Stephen F. Booth
4775c83d8d Fix potential fallthrough 2023-12-18 17:29:39 +01:00
Stephen F. Booth
034262c518 Fix implicit conversions
This commit does not address any potential mishandling of
64-bit header sizes
2023-12-18 17:29:39 +01:00
Stephen F. Booth
57c8dbe014 Use long long (QWORD) for sizes 2023-12-18 17:29:39 +01:00
Stephen F. Booth
bfaf1be6a6 Fix MPC tests 2023-12-18 17:29:39 +01:00
Stephen F. Booth
c9f7772198 Use unsigned long for sampleFrames 2023-12-18 17:29:39 +01:00
Stephen F. Booth
549bca3382 Fix implicit long to int conversions 2023-12-18 17:29:39 +01:00
Stephen F. Booth
98961813dc Fix implicit long to int conversion 2023-12-18 17:29:39 +01:00
Stephen F. Booth
9a08678098 Fix implicit long to int conversions 2023-12-18 17:29:39 +01:00
Urs Fleisch
c1d8159a34 Fix MinGW warning 'redeclared without dllimport attribute' 2023-12-16 15:01:11 +01:00
Rosen Penev
fa234fc053 make swap function noexcept
Signed-off-by: Rosen Penev <rosenp@gmail.com>
2023-12-13 12:59:03 +01:00
Rosen Penev
b4fe04f8e0 tests: fix compilation on MSYS2
Something about a deleted operator<<.

Signed-off-by: Rosen Penev <rosenp@gmail.com>
2023-12-13 06:24:39 +01:00
Urs Fleisch
c9486731d9 Fix MSVC warning needs to have dll-interface (#1185)
Unfortunately, MSVC exports everything (not only public members) when
__declspec(dllexport) is set at the class level via TAGLIB_EXPORT, which
leads to many "needs to have dll-interface to be used by clients" 4251
warnings issued by MSVC, because the std::unique_ptr pimpls are
exported too. As it is not possible to "unexport" private members,
such a warning has to be explicitly suppressed for these cases or
all public and protected class member now have to be exported
for classes derived from a template (StringList, ByteVectorList,
PropertyMap).
2023-12-11 06:35:08 +01:00
Urs Fleisch
ec734bbe08 Fix MSVC warning dllexport and extern are incompatible
C4910: '__declspec(dllexport)' and 'extern' are incompatible on an
explicit instantiation
2023-12-11 06:35:08 +01:00
Urs Fleisch
c7babea74a Fix narrowing warning issued by MSVC 2023-12-11 06:35:08 +01:00
Urs Fleisch
8a42e552aa Use private implementations for MP4::Atom, MP4::Atoms 2023-12-11 06:35:08 +01:00
Urs Fleisch
28baa03b23 Improve doc comments for PrivateFrame 2023-12-11 06:35:08 +01:00
Urs Fleisch
10094f66b9 Remove deprecated APE value(), toStringList(), ID3v2 unsycronisation()
- APE::Item::value(): use binaryData()
- APE::Item::toStringList(): use values()
- MPEG::ID3v2::Frame::Header::unsycronisation(): use unsynchronisation()
2023-12-11 06:35:08 +01:00
Urs Fleisch
aca1d0d41c Remove unused file trefcounter.h 2023-12-11 06:35:08 +01:00
Urs Fleisch
491322d1ba Tolerate trailing garbage in M4A files (#1186)
If an M4A file contains trailing garbage (e.g. an APE tag at the
end of the file), it was considered invalid since [e21640b].
It is important to make sure that atoms which affect modifications
written by TagLib are valid, otherwise the file might be damaged.
However, in cases where invalid atoms do not affect modifications
(which are only done inside the root level "moov" and "moof"
containers), they can be safely ignored.

This commit allows trailing garbage in M4A files as it is also
accepted in MP3, WAV and DSF files.
2023-12-08 15:58:01 +01:00
Urs Fleisch
8b3f1a459e Fix issues reported by CppCheck
Many shadowFunction, some useStlAlgorithm, and others.

cppcheck --enable=all --inline-suppr \
  --suppress=noExplicitConstructor --suppress=unusedFunction \
  --suppress=missingIncludeSystem --project=compile_commands.json
2023-12-07 05:00:00 +01:00
Urs Fleisch
131918333b Fix issues reported by Clang Static Analyzer
Some deadcode.DeadStores.

On Debian 12:
CXXFLAGS=-I/usr/include/x86_64-linux-gnu/c++/12 \
cmake -DCMAKE_C_COMPILER=/usr/share/clang/scan-build-14/libexec/ccc-analyzer \
    -DCMAKE_CXX_COMPILER=/usr/share/clang/scan-build-14/libexec/c++-analyzer \
    -DBUILD_SHARED_LIBS=OFF \
    -DCMAKE_EXPORT_COMPILE_COMMANDS=1 \
    -DENABLE_CCACHE=ON -DBUILD_TESTING=ON -DBUILD_EXAMPLES=ON \
    -DCMAKE_BUILD_TYPE=Debug ../taglib
scan-build make
2023-12-07 05:00:00 +01:00
certuna
cf2bdce21d ORIGINALDATE and RELEASEDATE mapppings for MP4 2023-12-05 05:26:55 +01:00
Urs Fleisch
51d63ab285 Detach list when setAutoDelete() is called
When calling setAutoDelete() on the implicitly shared copy of a list,
also auto-deletion of the original container was modified because
it was not detached. On the other hand, detach() was called by some
methods getting an Iterator parameter, which can lead to modification
of other implicitly shared copies but not the object is was called on.
This happens when the method is called on a not-already-detached
container, which is normally not the case because the container is
detached when the iterator is taken (e.g. calling begin()).
In such methods detach() cannot be called, and the client must
make sure that the iterator is taken after making an implicit copy.
This will NOT work:

List<int> l1 = { 1 };
auto it = l1.begin();
List<int> l2 = l1;
l1.erase(it);

This will modify both l1 and l2. The second and the third lines
must be swapped so that l1.begin() will detach l1 from l2.
2023-12-02 08:03:14 +01:00
Urs Fleisch
3f11e0ae2f TestId3v2FrameFactory: Fix memory leak
The frames in a frameList() copy were not deleted because it had the
autoDelete property active. However, removed frames cannot be auto
deleted. Using setAutoDelete() affects the source of the copy, which has
to be investigated further.
2023-12-01 07:55:55 +01:00
Urs Fleisch
bd06012b02 Implement property interface for RIFF Info 2023-12-01 07:55:55 +01:00
Urs Fleisch
9233ff1f5d Support OWNER property for MP4
OWNER ownr
2023-12-01 07:55:55 +01:00
Urs Fleisch
88219f2493 Support additional properties for ASF
ARTISTWEBPAGE WM/AuthorURL
ENCODING WM/EncodingSettings
INITIALKEY WM/InitialKey
ORIGINALALBUM WM/OriginalAlbumTitle
ORIGINALARTIST WM/OriginalArtist
ORIGINALFILENAME WM/OriginalFilename
ORIGINALLYRICIST WM/OriginalLyricist
2023-12-01 07:55:55 +01:00
Urs Fleisch
9df243ef74 Separate multiple values in basic tag with " / " instead of " " 2023-12-01 06:22:35 +01:00
Urs Fleisch
c1bb678695 Remove redundant UserTextIdentificationFrame::fieldList() 2023-12-01 06:22:35 +01:00
Urs Fleisch
56382e8cd4 Return tag union for tag() of WAV file 2023-12-01 06:22:35 +01:00
Urs Fleisch
8bb8fc5fe6 Remove null byte removal for table of contents frame 2023-12-01 06:22:35 +01:00
Urs Fleisch
8b564baf01 Make PropertyMap::unsupportedData() const 2023-12-01 06:22:35 +01:00
Urs Fleisch
3d67b139e4 Fix extensibility of ID3v2 FrameFactory
Because the main extension point of FrameFactory was using a protected
Frame subclass, it was not really possible to implement a custom frame
factory. Existing Frame subclasses also show that access to the frame
header might be needed when implementing a Frame subclass.
2023-11-23 16:41:46 +01:00
Urs Fleisch
59166f6757 ID3v2: Always pass correct FrameFactory from File to Tag 2023-11-23 16:41:46 +01:00
Urs Fleisch
b993e70cf4 Use ID3v2::FrameFactory also for AIFF 2023-11-23 16:41:46 +01:00
Urs Fleisch
b7dc1ab8ac Fail MPEG header check when frame length is zero (#1174) 2023-11-23 16:41:13 +01:00
Urs Fleisch
c86a2fce70 Fix MPC content check in presence of ID3v2 tag 2023-11-23 16:41:13 +01:00
Urs Fleisch
faddc4aa06 Export CMake configuration
This will install a <libdir>/cmake/taglib/ folder with CMake
configuration, so that users can simply use

find_package(TagLib)
add_executable(myproject ...)
target_link_libraries(myproject TagLib::tag)
2023-11-23 16:40:40 +01:00
Urs Fleisch
16326b2479 Restore ZLIB_LIBRARIES_FLAGS, used for .pc files 2023-11-23 16:40:40 +01:00
Urs Fleisch
c083d7ce15 Clients can control supported MP4 atoms using an ItemFactory (#1175) 2023-11-23 16:39:57 +01:00
Urs Fleisch
9679b08120 Verify type of ID3v2 frames used for complex properties (#1173)
A frame with ID "APIC" is not guaranteed to be an AttachedPictureFrame,
it can also be an invalid UnknownFrame with ID "APIC". Check the types
of the frames before accessing them.
2023-11-09 20:59:47 +01:00
Urs Fleisch
0e395f4ec4 Use ID3v2::FrameFactory also for WAV and DSDIFF (#1172)
* Use ID3v2::FrameFactory also for WAV and DSDIFF

* Apply suggestions from code review

Co-authored-by: Kevin André <hyperquantum@gmail.com>

---------

Co-authored-by: Kevin André <hyperquantum@gmail.com>
2023-11-09 18:58:51 +01:00
Urs Fleisch
52b245f015 MP4: Use integer instead of boolean for hdvd atom (#1169) (#1170) 2023-11-05 14:40:49 +01:00
Urs Fleisch
2f4c76b52a MP4: Get duration from mvhd if not present in mdhd (#1165) (#1168) 2023-11-05 14:40:37 +01:00
Urs Fleisch
dfa33bec08 Fix crash with invalid WAV files (#1163) (#1164)
With specially crafted WAV files having the "id3 " chunk as the
only valid chunk, when trying to write the tags, the existing
"id3 " chunk is removed, and then vector::front() is called on
the now empty chunks vector.
Now it is checked if the vector is empty to avoid the crash.
2023-11-05 14:40:18 +01:00
Urs Fleisch
f202fa25c3 GitHub Actions: Update Homebrew utfcpp to 4.0.2 (#1171)
Revert "Fix build on Mac with Homebrew utf8cpp"
This reverts commit 70b4ce79fb.

Is now fixed upstream
https://github.com/nemtrif/utfcpp/pull/113
https://github.com/nemtrif/utfcpp/releases/tag/v4.0.2
https://github.com/Homebrew/homebrew-core/pull/153333
2023-11-05 14:39:33 +01:00
Urs Fleisch
70b4ce79fb Fix build on Mac with Homebrew utf8cpp 2023-11-03 22:53:31 +01:00
Urs Fleisch
8c63c877ad Add "MP2" to supported file extensions 2023-11-03 17:05:29 +01:00
Nick Shaforostoff
3869aa189f Raw AAC (ADTS) support (#508)
Detect ADTS MPEG header to use it also for AAC.

The test file empty1s.aac was generated using
ffmpeg -f lavfi -i anullsrc=r=11025:cl=mono -t 1 -acodec aac empty1s.aac

---------

Co-authored-by: Nick Shaforostov <mshaforostov@airmusictech.com>
Co-authored-by: Urs Fleisch <ufleisch@users.sourceforge.net>
2023-11-03 05:17:39 +01:00
Urs Fleisch
a7c0b53f7a C bindings: taglib_file_new_iostream() to access file from memory (#987)
A ByteVectorStream can be created/freed from C using
taglib_memory_iostream_new()/taglib_iostream_free().
2023-10-27 09:46:54 +02:00
Urs Fleisch
135c0eb647 tagreader: Fix displaying of seconds in length, UTF-8 for ostream
The padding was wrong, 8 seconds were displayed as 0:80 instead of 0:08.
Also make the output of tagreader and tagreader_c the same, including
the encoding, which was still ISO-8859-1 for std::ostream.
2023-10-27 09:46:54 +02:00
Urs Fleisch
0db487bf61 Remove deprecated FileRef::create() method
In order to achieve this, the TagLib_File type of the C bindings is now
a FileRef and no longer a File. The support for the .oga extension has been
ported to FileRef(), so that taglib_file_new() should still behave the
same, but additionally also support content based file type detection.
2023-10-27 09:46:54 +02:00
Urs Fleisch
8d98ebf24b Provide properties methods on FileRef, make FileRef non-virtual
Use of FileRef::file() is discouraged, but was necessary to access the
properties. It was also not clear whether to access the properties via
tag() or file(). With the property methods on FileRef, it should become
the "simple usage" interface it was meant to be.
As the destructor was the only virtual method on FileRef, it is now made
non-virtual. Probably, it is not useful as a virtual base class.
2023-10-27 09:46:54 +02:00
Urs Fleisch
11f94903d0 DSDIFF: Add tests for ID3 in PROP and DIIN chunks 2023-10-23 06:06:05 +02:00
Urs Fleisch
dce8e016b9 DSDIFF: Fix MSVC warnings 2023-10-23 06:06:05 +02:00
Urs Fleisch
6b5f28d56d DSDIFF: Fix adding and removing DIIN chunks
Also modernize and simplify code, fix formatting, support
ID3v2FrameFactory, fix updating the internal chunk bookkeeping
after file modifications, stripping ID3 and DIIN tags.
2023-10-23 06:06:05 +02:00
Stephen F. Booth
19cceab211 DSDIFF support, add tests, fix formatting 2023-10-23 06:06:05 +02:00
Urs Fleisch
182edcd3f9 Add unit tests for C bindings 2023-10-21 06:19:55 +02:00
Urs Fleisch
a3ad2d0aaa C bindings for complex properties like pictures (#953)
Provides a unified API for complex properties in the same way as
the Properties API in the C bindings. Since constructing and
traversing complex properties in C is a bit complicated, there
are convenience functions for the most common use case of getting
or setting a single picture.

    TAGLIB_COMPLEX_PROPERTY_PICTURE(props, data, size, "Written by TagLib",
                                   "image/jpeg", "Front Cover");
    taglib_complex_property_set(file, "PICTURE", props);

and

    TagLib_Complex_Property_Attribute*** properties =
      taglib_complex_property_get(file, "PICTURE");
    TagLib_Complex_Property_Picture_Data picture;
    taglib_picture_from_complex_property(properties, &picture);
2023-10-21 06:19:55 +02:00
complexlogic
1d213b8b98 List: Add Sort Functions (#1160) 2023-10-18 07:54:22 +02:00
complexlogic
b40b834b1b Add ByteVector conversion functions, tests 2023-10-15 08:29:58 +02:00
Urs Fleisch
6be03b7ae1 Unified interface for complex properties like pictures (#94)
Provides a dynamic interface for properties which cannot be represented
with simple strings, e.g. pictures. The keys of such properties can
be queried using `complexPropertyKeys()`, which could return for example
["PICTURE"]. The property can then be read using
`complexProperties("PICTURE")`, which will return a list of variant maps
containing the picture data and attributes. Adding a picture is as
easy as

    t->setComplexProperties("PICTURE", {
      {
        {"data", data},
        {"pictureType", "Front Cover"},
        {"mimeType", "image/jpeg"}
      }
    });
2023-10-14 09:01:59 +02:00
Urs Fleisch
75d4252480 Variant type as container for complex properties 2023-10-14 09:01:59 +02:00
Urs Fleisch
304ab62957 Fix building without zlib 2023-10-14 09:01:59 +02:00
Rosen Penev
89a54863ee std::array conversion (#1157) 2023-10-09 17:09:27 +02:00
nmariusp
3f25d3c342 Proofreading (#1155)
---------

Co-authored-by: Urs Fleisch <ufleisch@users.sourceforge.net>
2023-10-08 05:58:33 +02:00
Urs Fleisch
2e3a66cc31 Fix memory leak in testParsePodcastFrame() 2023-10-07 09:18:22 +02:00
Urs Fleisch
41077aa57e Braced list initialization for StringList and ByteVectorList (#1154)
Additionally, provide operator==() and operator!=() for Map and
operator<<() for ByteVectorList to facilitate the writing of tests.
2023-10-07 08:51:17 +02:00
complexlogic
21b08c0dcb List: Support Braced List Initialization (#1154)
* List: support braced list initialization

* Use swap for assignment

---------

Co-authored-by: complexlogic <complexlogic@users.noreply.github.com>
2023-10-07 07:27:29 +02:00
complexlogic
8a800b8c38 Map: Support Braced List Initialization (#1153)
* Map: support braced list initialization

* Use swap assignment

---------

Co-authored-by: complexlogic <complexlogic@users.noreply.github.com>
2023-10-07 07:27:14 +02:00
Kevin André
c332aa04f2 Add simple runtime version check API (#970)
How to use:

  #include <tversionnumber.h>

  if (TagLib::runtimeVersion() >= TagLib::VersionNumber(1, 11, 1)) {
    // running v1.11.1 or higher
  }

  if (TagLib::runtimeVersion() >= TagLib::VersionNumber(1, 12)) {
    // running v1.12 or higher
  }

---------

Co-authored-by: Urs Fleisch <ufleisch@users.sourceforge.net>
2023-10-06 14:15:30 +02:00
Urs Fleisch
24e0ac7aa4 Do not use tabs in CMake files 2023-09-30 15:12:51 +02:00
evpobr
ad1696ade6 Fix undefined PLATFORM_WINRT CMake option (#870) 2023-09-30 14:34:58 +02:00
Urs Fleisch
c13a42021a Do not scan full MPEG file for ID3v2 tag in Fast read style (#968) (#1151)
This is based on the patch used by VLC, but the full scan is only
skipped when the read style is explicitly set to Fast.
https://code.videolan.org/videolan/vlc/-/blob/master/contrib/src/taglib/0001-Implement-ID3v2-readStyle-avoid-worst-case.patch
2023-09-30 14:25:52 +02:00
Jan Palus
2154078187 cmake: support both relative and absolute paths for pc files (#1152)
cmake supports both absolute and relative to prefix install dirs. #1071
effectively mandated relative paths only. check if install dir is
relative or absolute and either include ${prefix} in pc file or not

Fixes #1098
2023-09-30 14:25:00 +02:00
Urs Fleisch
eaf7ff8b94 DSF: Support frame factory, ID3v2.3, allow trailing garbage
Additionally test coverage and formatting are improved.
2023-09-26 16:23:03 +02:00
Stephen F. Booth
39d1d68237 DSD Stream File (DSF) support 2023-09-26 16:23:03 +02:00
Rosen Penev
e275abb8a3 Convert private raw pointers to unique_ptr (#1127)
Simplifies the code quite a bit.
2023-09-23 06:46:05 +02:00
nmariusp
967aaf7af2 Require CMake version 3.5 (#1148)
Fix issue "CMake Deprecation Warning at CMakeLists.txt:1 (cmake_minimum_required):
  Compatibility with CMake < 3.5 will be removed from a future version of
  CMake.

  Update the VERSION argument <min> value or use a ...<max> suffix to tell
  CMake that the project does not need compatibility with older versions.".
2023-09-16 22:00:22 -05:00
Rosen Penev
48c4bf9c05 change two loops to while (#1147)
and one to for range.

Signed-off-by: Rosen Penev <rosenp@gmail.com>
2023-09-16 13:28:12 +02:00
Rosen Penev
bdf50eda99 use some more auto (#1146) 2023-09-16 13:12:04 +02:00
Rosen Penev
653c2fe9e1 clang-tidy: use using instead of typedef (#1120)
Signed-off-by: Rosen Penev <rosenp@gmail.com>
2023-09-16 09:06:14 +02:00
Rosen Penev
6ed0ca28db add utf8cpp git submodule (#1142)
* add utf8cpp git submodule

More standard. Allows using the system provided utf8cpp.

Signed-off-by: Rosen Penev <rosenp@gmail.com>

* cmake: use standard find_package for zlib

Simpler.

Signed-off-by: Rosen Penev <rosenp@gmail.com>

---------

Signed-off-by: Rosen Penev <rosenp@gmail.com>
2023-09-16 08:41:01 +02:00
Rosen Penev
cf5ad66922 default some more (#1135)
* remove defaulted constructor

It gets implicitly generated.

Signed-off-by: Rosen Penev <rosenp@gmail.com>

* default some more

swap is not needed here as shared_ptr is copyable.

Signed-off-by: Rosen Penev <rosenp@gmail.com>

---------

Signed-off-by: Rosen Penev <rosenp@gmail.com>
2023-09-16 08:25:04 +02:00
Rosen Penev
97a74ca3d8 clang: fix -Wextra-semi warnings (#1134)
Achieved by adding do/while to mandate a semicolon.

Signed-off-by: Rosen Penev <rosenp@gmail.com>
2023-09-16 08:23:45 +02:00
Rosen Penev
7646184d6b unused includes (#1133)
* unused includes

Signed-off-by: Rosen Penev <rosenp@gmail.com>

* Use consistent order of includes

Always include in the following order:
- Own header files
- Standard header files
- System header files
- Project header files (toolkit first)

Exceptions:
- cppunit/extensions/HelperMacros.h must be included after
  header files declaring stream operators
- config.h must be included before its definitions are used

---------

Signed-off-by: Rosen Penev <rosenp@gmail.com>
Co-authored-by: Urs Fleisch <ufleisch@users.sourceforge.net>
2023-09-16 08:22:36 +02:00
dependabot[bot]
2dc1aa4ec9 build(deps): bump actions/checkout from 3 to 4 (#1143)
Bumps [actions/checkout](https://github.com/actions/checkout) from 3 to 4.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v3...v4)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-09-10 12:27:11 +02:00
Rosen Penev
00cfee30b6 github: add dependabot (#1141)
Bot to update CI.

Signed-off-by: Rosen Penev <rosenp@gmail.com>
2023-09-10 07:10:59 +02:00
Rosen Penev
88cb197111 replace vector with array (#1140)
No need for vector. Allows simplifying some code.

Signed-off-by: Rosen Penev <rosenp@gmail.com>
2023-09-10 06:44:13 +02:00
Rosen Penev
110a253ba3 use using declarations (#1139)
Signed-off-by: Rosen Penev <rosenp@gmail.com>
2023-09-10 06:40:24 +02:00
Rosen Penev
d42e8ed3fe remove out of line declaration (#1137)
Seems to have been an oversight at some point.

Signed-off-by: Rosen Penev <rosenp@gmail.com>
2023-09-08 13:41:41 +02:00
Rosen Penev
37ba5a5cc1 convert missed for loop (#1136)
clang-tidy missed it as the declaration is outside of the loop.

Signed-off-by: Rosen Penev <rosenp@gmail.com>
2023-09-07 11:04:02 -05:00
Rosen Penev
23186f24ff remove unused typedefs (#1138)
This was done in C++98 times. No longer needed with auto.

Signed-off-by: Rosen Penev <rosenp@gmail.com>
2023-09-07 11:02:55 -05:00
Rosen Penev
528b84fbde size() to isEmpty() (#1131)
Signed-off-by: Rosen Penev <rosenp@gmail.com>
2023-09-06 21:23:28 +02:00
Rosen Penev
912897cd35 simplify boolean expressions (#1130)
Signed-off-by: Rosen Penev <rosenp@gmail.com>
2023-09-06 21:16:59 +02:00
Rosen Penev
54f84cc924 clang-tidy: use data() (#1129)
Found with readability-container-data-pointer

Signed-off-by: Rosen Penev <rosenp@gmail.com>
2023-09-06 21:15:56 +02:00
Urs Fleisch
47c4e0859c Restore const iterators replaced in #1107 (#1128)
Also make sure that the lines calling std algorithms are not
excessively long.
2023-09-06 20:59:01 +02:00
Rosen Penev
524b588a1e manual range loop conversions (#1126)
* manual range loop conversions

Signed-off-by: Rosen Penev <rosenp@gmail.com>

* Restore const containers where non const temporaries are iterated

* Use std::as_const() instead of const container copies where possible

---------

Signed-off-by: Rosen Penev <rosenp@gmail.com>
Co-authored-by: Urs Fleisch <ufleisch@users.sourceforge.net>
2023-09-06 20:58:13 +02:00
Rosen Penev
303b55fb97 clang-tidy: ending namespace comment (#1132)
Signed-off-by: Rosen Penev <rosenp@gmail.com>
2023-09-03 21:39:49 -05:00
Rosen Penev
ecf1d4fa53 clang: fix documentation (#1111)
* Use [[deprecated]] always

Simplifies the code. Codebase is C++17 anyways.

Signed-off-by: Rosen Penev <rosenp@gmail.com>

* clang: fix documentation

Found with Wdocumentation

Signed-off-by: Rosen Penev <rosenp@gmail.com>

* add deprecated to length() and fix usages

Signed-off-by: Rosen Penev <rosenp@gmail.com>

---------

Signed-off-by: Rosen Penev <rosenp@gmail.com>
2023-09-03 15:14:05 +02:00
Urs Fleisch
d2bd56c519 Fix possible loss of data warning by MSVC 2023-09-02 16:30:45 +02:00
Rosen Penev
040b069957 clang-tidy: fix mismatched variable names (#1109)
Found with readability-inconsistent-declaration-parameter-name

Signed-off-by: Rosen Penev <rosenp@gmail.com>
2023-08-31 06:46:58 +02:00
Urs Fleisch
f44ea9b80b Fixed signedness warning 2023-08-30 17:58:15 +02:00
Rosen Penev
0ba61343a4 std::list and unique_ptr conversions (#1122)
The former is standard C++ and the latter allows getting rid of
autodelete.

Signed-off-by: Rosen Penev <rosenp@gmail.com>
2023-08-30 16:49:31 +02:00
Rosen Penev
cdc87aec10 clang-tidy: init members by default (#1110)
Found with modernize-use-default-member-init

Signed-off-by: Rosen Penev <rosenp@gmail.com>
2023-08-30 16:26:14 +02:00
Rosen Penev
4a86489186 clang-tidy: no else after return (#1119)
Signed-off-by: Rosen Penev <rosenp@gmail.com>
2023-08-21 09:19:53 -05:00
Rosen Penev
85c678f587 Use nullptr (#1117)
Found with modernize-use-nullptr

Signed-off-by: Rosen Penev <rosenp@gmail.com>
2023-08-17 10:36:22 -05:00
Rosen Penev
29e88cfe66 Default FileName copy constructor (#1118)
Found with: modernize-use-equals-default

Signed-off-by: Rosen Penev <rosenp@gmail.com>
2023-08-17 10:33:56 -05:00
Rosen Penev
128c55bc53 Remove dead forward declaration (#1121)
Signed-off-by: Rosen Penev <rosenp@gmail.com>
2023-08-17 10:30:01 -05:00
Rosen Penev
cf352ac7f4 Move temporary into vector instead of copying (#1115)
more efficient.

Signed-off-by: Rosen Penev <rosenp@gmail.com>
2023-08-15 19:13:22 -05:00
Rosen Penev
bbb8221301 Handle self-assignment (#1114)
Found with cert-oop54-cpp

Signed-off-by: Rosen Penev <rosenp@gmail.com>
2023-08-15 16:58:56 -05:00
Rosen Penev
f69c21c8e6 Additional none_of conversion (#1113) 2023-08-15 09:21:43 -05:00
Rosen Penev
574604765f Use std algorithms (#1107)
Found with: readability-use-anyofallof

Signed-off-by: Rosen Penev <rosenp@gmail.com>
2023-08-09 12:09:28 -05:00
Rosen Penev
f2d0e664e7 Add ending namespace comments (#1108)
Found with google-readability-namespace-comments

Signed-off-by: Rosen Penev <rosenp@gmail.com>
2023-08-07 20:44:11 -05:00
Rosen Penev
185bb7042e Use unique_ptr for d-pointers (#1095)
* clang-tidy: make deleted members public

One oversight of modernize-use-equals-delete is that the C++11 way of
doing this is to make it public, which makes the warning still trigger.

Signed-off-by: Rosen Penev <rosenp@gmail.com>

* clang-tidy: add missing deleted functions

Found with cppcoreguidelines-special-member-functions

Signed-off-by: Rosen Penev <rosenp@gmail.com>

* unique_ptr conversions

unique_ptr is a safer and cleaner way to handle d pointers.

Also added missing = default.

Signed-off-by: Rosen Penev <rosenp@gmail.com>

---------

Signed-off-by: Rosen Penev <rosenp@gmail.com>
2023-08-07 15:08:40 -05:00
Rosen Penev
843a8aac80 Replace RefCounter with shared_ptr (#1100)
* clang-tidy: replace RefCounter with shared_ptr

The latter is C++11.

Found with clang-analyzer-webkit.NoUncountedMemberChecker

Signed-off-by: Rosen Penev <rosenp@gmail.com>

* remove trefcounter

It is now unused.

Signed-off-by: Rosen Penev <rosenp@gmail.com>

---------

Signed-off-by: Rosen Penev <rosenp@gmail.com>
2023-08-07 08:40:24 -05:00
Rosen Penev
dcef356e3f Replace raw buffers with std containers (#1101)
Signed-off-by: Rosen Penev <rosenp@gmail.com>
2023-08-06 17:46:59 -05:00
Rosen Penev
868f4eef3d Various cleanups (#1099)
* cmake: remove atomic checks

All this stuff was replaced with std::atomic

Fixes: 9bcba812af

Signed-off-by: Rosen Penev <rosenp@gmail.com>

* cmake: remove vsnprintf checks

Available since C++11

Signed-off-by: Rosen Penev <rosenp@gmail.com>

* clang-tidy: const ref conversions

Signed-off-by: Rosen Penev <rosenp@gmail.com>

* clang-tidy: handle self assignment

Found with bugprone-unhandled-self-assignment,cert-oop54-cpp

Signed-off-by: Rosen Penev <rosenp@gmail.com>

* clang-tidy: remove wrong forward declarations

Found with bugprone-forward-declaration-namespace

Signed-off-by: Rosen Penev <rosenp@gmail.com>

---------

Signed-off-by: Rosen Penev <rosenp@gmail.com>
2023-08-05 10:31:46 -05:00
Rosen Penev
c0e9428218 Fix warnings under clang-cl (#1106)
-Wmicrosoft-unqualified-friend
-Wdllexport-explicit-instantiation-decl
-Wunused-parameter

Signed-off-by: Rosen Penev <rosenp@gmail.com>
2023-08-05 10:07:52 -05:00
Rosen Penev
bec59b4b7b Convert to range-based for (#1104)
Found with MSVC: warning C4456

Signed-off-by: Rosen Penev <rosenp@gmail.com>
2023-08-05 10:00:07 -05:00
Rosen Penev
bd6c3ba174 Use constexpr if (#1103)
Signed-off-by: Rosen Penev <rosenp@gmail.com>
2023-08-05 09:56:23 -05:00
Rosen Penev
ee8124ed7a Replace unnecessary loop (#1105)
Signed-off-by: Rosen Penev <rosenp@gmail.com>
2023-08-05 09:45:32 -05:00
Scott Wheeler
965260e3cb Add a note about how I generated list of classes 2023-07-30 06:55:23 +02:00
Scott Wheeler
f903e1ad71 Add checks for the expected sizes of all public classes
This ensures that d-pointers exist where they're expected, and that
classes are virtual that we expect to be virtual, as well as ensuring
that we get a failure if the class size changes when it shouldn't.

This also fixes the issues that were discovered by adding these tests.
2023-07-30 06:31:48 +02:00
Scott Wheeler
a1bdb0171d Remove virtual destructors from POD toolkit types
This one's been haunting me for a couple decades.  "Make all the things
virtual!" was kind of a strategy I had for forward compatibility when I
wrote this code when I was 20 or so and didn't know better.
2023-07-30 02:47:00 +02:00
Stephen Booth
9bcba812af Use C++11 atomics for RefCounter (#1097) 2023-07-29 06:58:50 +02:00
Max-F-Helm
271bd05afa Improve FrameFactory subclassing
Change the ID3v2 frame factory so it is possible to subclass it for
implementing own frames.  Additionally it splits up the createFrame()
method to make its usage in subclasses a bit more easy.
2023-07-26 07:39:31 +02:00
Urs Fleisch
c54c924333 Use static_cast instead of C-style casts 2023-07-22 07:09:58 +02:00
Urs Fleisch
d314bfa06a Make sure that own header file is included first
Each header corresponding to a .cpp file must be self-contained,
and can be #included without prerequisites.
2023-07-22 07:09:58 +02:00
Urs Fleisch
cc7d23cdf5 Consistently use quote form when including our own headers 2023-07-22 07:09:58 +02:00
Urs Fleisch
2a1fb27735 clang-tidy: Replace C headers with C++ alternatives
run-clang-tidy -header-filter='.*' -checks='-*,modernize-deprecated-headers'

Fixed manually as automatic fix would shuffle headers around.
2023-07-22 07:09:58 +02:00
Urs Fleisch
b5516c9718 clang-tidy: Use default function declarations
run-clang-tidy -header-filter='.*' -checks='-*,modernize-use-equals-default' -fix
2023-07-22 07:09:58 +02:00
Urs Fleisch
231772b2ad clang-tidy: Delete unimplemented private constructors
run-clang-tidy -header-filter='.*' -checks='-*,modernize-use-equals-delete' -fix
2023-07-22 07:09:58 +02:00
Urs Fleisch
77ab5e9689 Narrow the scope of iterators
Also make sure that it is visible at first glance that iterators are
not taken from temporaries.
2023-07-22 07:09:58 +02:00
Urs Fleisch
c2c9e8989c clang-tidy: Use auto where it improves the readability
run-clang-tidy -header-filter='.*' -checks='-*,modernize-use-auto' \
-config="{CheckOptions: [{key: modernize-use-auto.RemoveStars, value: '1'}]}" \
-fix

Manually fixed some wrong `const auto` replacements and verified
that all types are deduced correctly.
2023-07-22 07:09:58 +02:00
Urs Fleisch
63922f2676 Avoid unnecessary detaching of containers
If only a constant iterator is needed, make the container const if
possible, otherwise use cbegin() and cend() to get a constant iterator
without detaching the container.

These fixes are needed so that `auto` can deduce the correct
iterator type.
2023-07-22 07:09:58 +02:00
Urs Fleisch
b273505c28 clang-tidy: static accessed through instance.
run-clang-tidy -header-filter='.*' -checks='-*,readability-static-accessed-through-instance' -fix
2023-07-22 07:09:58 +02:00
Urs Fleisch
abc6c31890 clang-tidy: Use nullptr instead of 0
run-clang-tidy -header-filter='.*' -checks='-*,modernize-use-nullptr' -fix
2023-07-22 07:09:58 +02:00
Urs Fleisch
9867bc947e clang-tidy: Use override keyword
run-clang-tidy -header-filter='.*' -checks='-*,modernize-use-override' -fix
2023-07-22 07:09:58 +02:00
whatdoineed2do
e20a53afbb C bindings - properties i/f (#1091)
Support properties in C bindings

---------

Co-authored-by: whatdoineed2do/Ray <whatdoineed2do@nospam.gmail.com>
Co-authored-by: Urs Fleisch <ufleisch@users.sourceforge.net>
2023-07-09 19:48:27 +02:00
Urs Fleisch
ceb142c9bd Remove functions documented (but not marked) as deprecated
Clean up other stuff with comments about binary incompatibility,
but only as far as to mostly keep source compatibility.
2023-07-09 07:01:37 +02:00
Urs Fleisch
322085f90e Windows: Remove unused FileName member, disable two warnings 2023-07-09 07:01:37 +02:00
Urs Fleisch
250dece2ab Fix warnings about missing virtual destructors 2023-07-09 07:01:37 +02:00
Urs Fleisch
a33cc0635a Remove deprecated stuff 2023-07-09 07:01:37 +02:00
Urs Fleisch
ca8c2e07ec Support large files over 2GB on Windows (#1089)
Backport of 4dcf0b41c6
b01f45e141
https://github.com/taglib/taglib/pull/77

Tested with files larger than 2GB which have been created using

sox -n -r 44100 -C 320 large.mp3 synth 58916 sine 440 channels 2
sox -n -r 44100 -C 0 large.flac synth 25459 sine 440 channels 2
sox -n -r 44100 -C 10 large.ogg synth 229806 sine 440 channels 2
sox -n -r 44100 large.wav synth 6692 sine 440 channels 2
ffmpeg -f lavfi -i "sine=frequency=440:duration=244676" -y large.m4a

The only file which was readable with the tagreader example
before this commit was large.ogg. The problem is that long on
Windows is only 32-bit (also in LLP64 data model of 64-bit
compilation target) and all the file offsets using long are
too small for large files. Now long is replaced by offset_t
(defined to be long long on Windows and off_t on UNIX) for such
cases and some unsigned long are now size_t, which has the
correct size even on Windows.
2023-07-09 07:01:37 +02:00
Urs Fleisch
bc915f5dc8 Use C++17 2023-07-09 07:01:37 +02:00
Urs Fleisch
e5cf30e1e9 Set version and date for TagLib 2.0, simplify SO versioning 2023-07-09 07:01:37 +02:00
Urs Fleisch
c840222a39 Version 1.13.1 2023-07-01 07:43:27 +02:00
Urs Fleisch
39e712796f ID3v2: Map "TSST" to "DISCSUBTITLE" property (#1087) (#1088) 2023-05-26 13:12:19 +02:00
Urs Fleisch
97203503b0 Do not miss frames when an extended header is present (#1081) 2023-03-18 08:08:37 +01:00
Urs Fleisch
e21640bf10 MP4: Detect atoms with invalid length or type (#1077) 2023-03-18 08:07:46 +01:00
Ziemowit Łąski
a31356e330 Update FindCppUnit.cmake (#1076)
* Update FindCppUnit.cmake

Require a more modern cppunit to fix tagib build breakage.

* Update FindCppUnit.cmake
2022-12-21 14:14:35 +01:00
Rosen Penev
9ef9514bfa clang: remove const return (#1074)
Found with readability-const-return-type

Signed-off-by: Rosen Penev <rosenp@gmail.com>

Signed-off-by: Rosen Penev <rosenp@gmail.com>
2022-12-10 11:30:21 +01:00
dabrain34
967c0eefed cmake: generate pc files with the use of prefix (#1071) 2022-12-04 13:27:58 +01:00
Rosen Penev
abbf880872 clang-tidy: use member initializer
Found with cppcoreguidelines-prefer-member-initializer

Signed-off-by: Rosen Penev <rosenp@gmail.com>
2022-11-28 07:04:42 +01:00
Rosen Penev
e49724ae5f clang-tidy: remove unused declaration
Found with bugprone-forward-declaration-namespace

Signed-off-by: Rosen Penev <rosenp@gmail.com>
2022-11-28 07:04:42 +01:00
Rosen Penev
9427d8f3ba clang-tidy: use patentheses in macros
Found with bugprone-macro-parentheses

Signed-off-by: Rosen Penev <rosenp@gmail.com>
2022-11-28 07:04:42 +01:00
Rosen Penev
f40290dcaf clang-tidy: don't assign in if
Found with bugprone-assignment-in-if-condition

Signed-off-by: Rosen Penev <rosenp@gmail.com>
2022-11-28 07:04:42 +01:00
Rosen Penev
983a35f5ae clang-tidy: avoid C casting
Found with google-readability-casting

Signed-off-by: Rosen Penev <rosenp@gmail.com>
2022-11-28 07:04:42 +01:00
Rosen Penev
4dc6bdcd28 clang-tidy: add ending namespace comments
Found with google-readability-namespace-comments

Signed-off-by: Rosen Penev <rosenp@gmail.com>
2022-11-28 07:04:42 +01:00
Rosen Penev
c963d1189a clang-tidy: no else after return
Found with readability-else-after-return

Signed-off-by: Rosen Penev <rosenp@gmail.com>
2022-11-27 06:35:33 +01:00
Rosen Penev
1ac61ffe19 clang-tidy: remove pointless return in void func
Found with readability-redundant-control-flow

Signed-off-by: Rosen Penev <rosenp@gmail.com>
2022-11-27 06:35:33 +01:00
Rosen Penev
a564d743f8 clang-tidy: remove duplicate include
Found with readability-duplicate-include

Signed-off-by: Rosen Penev <rosenp@gmail.com>
2022-11-27 06:35:33 +01:00
Rosen Penev
8c4d663393 clang-tidy: simplify booleans
Found with readability-simplify-boolean-expr

Signed-off-by: Rosen Penev <rosenp@gmail.com>
2022-11-27 06:35:33 +01:00
Urs Fleisch
bc5e56d3eb Fix parsing of TXXX frame without description (#1069) 2022-11-21 21:42:01 +01:00
Ryan Schmidt
8aa7dd81d8 Fix macOS dylib install name to be absolute path
Closes #1065
2022-10-28 18:04:34 +02:00
Urs Fleisch
15ff32b685 Version 1.13 2022-10-26 15:44:33 +02:00
Urs Fleisch
084108a908 Document replacements for deprecated functions (#1001) 2022-10-26 15:42:49 +02:00
Urs Fleisch
decc0fa50a Fix Doxygen API documentation
Since the Doxygen output changed quite a bit since the styling was adapted for
TagLib, the API documentation generated with Doxygen 1.9.1 has several problems:

- In the namespaces and classes lists, the nodes cannot be expanded,
  'N' and 'C' characters are prepended to the namespaces and classes instead
  of displaying icons.
- Class members are only accessible for the letter 'a'.
- No "Deprecated" sections are displayed (only the "TAGLIB_DEPRECATED"
  definition in the signature).
- Some things are strangely styled (e.g. an empty box on the left of the
  "Introduction" title).

Now most of the custom styling is removed to get it running again and to avoid
nasty surprises with doxygen changes.
2022-10-24 07:06:23 +02:00
Urs Fleisch
02c7e34681 MSVC: Disable warnings for internal invocations of API functions
https://raw.githubusercontent.com/microsoft/vcpkg/master/ports/taglib/msvc-disable-deprecated-warnings.patch
from Uwe Klotz (uklotzde)
2022-10-23 15:37:06 +02:00
Urs Fleisch
5f079d6992 Use GitHub Actions instead of Travis CI 2022-10-23 11:01:26 +02:00
Daniel Chabrowski
2e90ec0ef0 FileStream: seek only when length exceeds buffer size 2022-10-09 19:50:12 +02:00
Urs Fleisch
9914519d43 Update NEWS for v1.13beta 2022-09-19 18:07:31 +02:00
Urs Fleisch
f80d11ed2a MP4: Fix heap-buffer-overflow in mp4properties.cpp (#1058) 2022-09-05 06:32:53 +02:00
Urs Fleisch
4e7f844ea6 Correctly parse ID3v2.4.0 multiple strings with single BOM (#1055)
Some ID3v2.4.0 frames such as text information frames support multiple strings
separated by the termination code of the character encoding. If the encoding
is $01 UTF-16 with BOM, all strings shall have the same byte order. In the
multi strings written by TagLib, all string elements of such a multi string
have a BOM. However, I have often seen tags where a BOM exists only at the
beginning, i.e. at the start of the first string. In such a case, TagLib will
only return a list with the first string and a second empty string. This
commit will detect such cases and parse the strings without BOM according to
the BOM of the first string.
2022-08-01 09:27:35 +02:00
Stephen F. Booth
50b89ad19a Fix ID3v2 version handling for embedded frames 2022-07-24 15:28:45 +02:00
Urs Fleisch
0470c2894d Use property "WORK" instead of "CONTENTGROUP" for ID3v2 "TIT1" frame
Also use "WORK" for ASF "WM/ContentGroupDescription", as it corresponds
to "TIT1" according to
https://docs.microsoft.com/en-us/windows/win32/wmformat/id3-tag-support.
Add property "COMPILATION" for ID3v2 "TCMP" to be consistent with
MP4 "cpil".
2022-03-15 17:52:44 +01:00
James D. Smith
10721b4b41 Test for ID3v1 false positives. 2022-03-13 08:11:23 +01:00
James D. Smith
cebaf9a8e2 Utils: Fix potential ID3v1 false positive in the presence of an APE tag. 2022-03-13 08:11:23 +01:00
Urs Fleisch
197d2a684b Correctly parse MP4 non-full meta atoms (#1041)
There are m4a files with regular (non-full) meta atoms. When such
a meta atom is not correctly parsed, the subsequent atoms are not
recognized and offsets will not be adjusted when atoms are added,
which will corrupt the MP4 file.

This change will look behind the meta atom to check if the next
atom follows directly, i.e. without the four bytes with version
and flags as they exist in full atoms. In such a case, these
four bytes will not be skipped.

Witnesses of this strange format specification are
https://leo-van-stee.github.io/
https://github.com/axiomatic-systems/Bento4/blob/v1.6.0-639/Source/C%2B%2B/Core/Ap4ContainerAtom.cpp#L60
2022-03-12 07:33:32 +01:00
Urs Fleisch
e255e749e8 fileref: Adapt formatting to TagLib code style 2022-02-27 07:11:53 +01:00
Hugo Beauzée-Luyssen
a7eb7735ee fileref: Reduce variables scope 2022-02-27 07:11:53 +01:00
Hugo Beauzée-Luyssen
0de8b45612 tests: Fix stream based resolver test 2022-02-27 07:11:53 +01:00
Hugo Beauzée-Luyssen
bdd8ff2af0 fileref: Fall back on file name resolvers
when no stream resolver is available
2022-02-27 07:11:53 +01:00
Hugo Beauzée-Luyssen
82964ba176 detectByResolvers: Don't mandate an actual file to be present
The main point of IOStream is to be able to use taglib with some content
that can't be fopen()'ed, for instance a network file, or a local file
on a system that doesn't support opening a file directly through fopen
& such.
This commit adds an overload for detectByResolvers that will stick to
using an IOStream all the way.
2022-02-27 07:11:53 +01:00
Hugo Beauzée-Luyssen
f7887e7235 FileTypeResolver: Add a StreamTypeResolver interface 2022-02-27 07:11:53 +01:00
Urs Fleisch
8ab618da18 Add tests checking if looking up tags creates map entries (#931) 2022-02-09 20:07:13 +01:00
Urs Fleisch
c98fba7cc4 APE: Do not create map entry if looked up tag does not exist (#931) 2022-02-09 20:07:13 +01:00
whatdoineed2do/Ray
52bf85f8ca FLAC: Do not create map entry if looked up tag does not exist (#931) 2022-02-09 20:07:13 +01:00
Urs Fleisch
a4bb904a01 Map: Add method value() to look up without creating entry 2022-02-09 20:07:13 +01:00
Scott Wheeler
05486d00bf We don't need to export these symbols 2022-01-15 13:52:05 +01:00
Urs Fleisch
c4a0855f42 Add MP4::File::strip() to remove meta atom from file
This changes the modifications from the last commit in order to
achieve the following behavior: MP4::File::save() works in the
same way as before, i.e. it will never shrink the file and will
make space from removed items available as padding in the form of
a "free" atom. To completely remove the "meta" atom from the file,
a new method strip() is introduced, which can be used in the same
way as its MPEG::File::strip() counterpart.
2022-01-06 18:07:15 +01:00
Urs Fleisch
2cb7973162 Remove MP4 meta atom when empty tag is saved
Currently, MP4 tags can only grow. If items are removed, they are
just replaced by padding in the form of "free" atoms. This change
will remove the whole "meta" atom when an MP4 tag without items
is saved. This will make it possible, to bring the file back to
its pristine state without metadata.
2022-01-06 18:07:15 +01:00
Dzmitry Neviadomski
ff8a9ea831 Add -Wall compiler flag to Clang compilers.
Use CMAKE's `CMAKE_<LANG>_COMPILER_ID`, available from 3.0.2.
See: https://cmake.org/cmake/help/latest/variable/CMAKE_LANG_COMPILER_ID.html
2021-12-31 07:51:38 +01:00
ivan tkachenko
fd66b0d3b6 Add compile_commands.json to gitignore
Other entries were copied from KDE/marble repo as well.

See also: https://invent.kde.org/sdk/kdesrc-build/-/issues/86
2021-12-08 19:24:52 +01:00
Rosen Penev
f581615110 clang-tidy: replace static_cast with dynamic_cast (#1021)
* clang-tidy: replace static_cast with dynamic_cast

Found with cppcoreguidelines-pro-type-static-cast-downcast

Signed-off-by: Rosen Penev <rosenp@gmail.com>
2021-07-09 21:47:36 +02:00
Rosen Penev
07eb81e88a add missing const
Avoids using a non const global variable.

Signed-off-by: Rosen Penev <rosenp@gmail.com>
2021-07-08 20:38:27 +02:00
Rosen Penev
c6f1e2750e use std::pair instead of 2D C array (#1020)
* use std::pair instead of 2D C array

Mostly the same. Except there's now an association between both values.

make_pair has to be used since there's no uniform initialization.

Signed-off-by: Rosen Penev <rosenp@gmail.com>
2021-06-21 08:16:46 +02:00
Rosen Penev
a8884c2b17 clang-tidy: don't use else after return
Found with readability-else-after-return

Signed-off-by: Rosen Penev <rosenp@gmail.com>
2021-06-21 06:49:32 +02:00
Rosen Penev
76f00c5a8a clang-tidy: simplify boolean return
Found with readability-simplify-boolean-expr

Signed-off-by: Rosen Penev <rosenp@gmail.com>
2021-06-21 06:49:32 +02:00
Rosen Penev
68ac7c3106 clang-tidy: use C++ casting
Found with google-readability-casting

Signed-off-by: Rosen Penev <rosenp@gmail.com>
2021-06-21 06:49:32 +02:00
Rosen Penev
38d1d4c21c clang-tidy: add ending namespace comments
Found with google-readability-namespace-comments

Signed-off-by: Rosen Penev <rosenp@gmail.com>
2021-06-21 06:49:32 +02:00
Rosen Penev
b77e828d4b clang-tidy: remove redundant initializations
Found with readability-redundant-member-init

Signed-off-by: Rosen Penev <rosenp@gmail.com>
2021-06-21 06:49:32 +02:00
Urs Fleisch
17e299350a Detect by contents if file detected by extension is invalid (#1011)
This is the case for Opus files with an ogg extension.
2021-05-28 20:25:05 +02:00
Oleg Antonyan
1d24bd3399 Fix "error: duplicate ‘volatile’" on systems without HAVE_GCC_ATOMIC in 1.12 (#1012)
* fix error: duplicate volatile

* fix volatile ATOMIC_INT in constructor
2021-05-13 16:41:35 +02:00
bobsayshilol
4971f8fb03 ID3v2: Address review comments
Change the size check to use isEmpty() as requested.
2021-05-02 12:14:27 +02:00
bobsayshilol
d74689cb93 ASF: Bounds check the size of each header object
UBSan spotted an integer overflow on the line `dataPos += size`, so add
a bounds check to the size that we read.
2021-05-02 12:14:27 +02:00
bobsayshilol
51ae5748cb ID3v2: Return early from decode() on invalid data
The while loop in this function assumes that `data.end() - 1` is less
than `data.end()`, which isn't the case if `data` is empty since
`data.end()` can be a nullptr.
2021-05-02 12:14:27 +02:00
bobsayshilol
f2eb331696 MPC: Fix heap-buffer-overflow in readSV7()
If `data` is an allocation of only 3 bytes (MP+) then `data[3]` is a
read past the end of the buffer.
2021-05-02 12:14:27 +02:00
bobsayshilol
5f6bbb20e7 APE: Fix typo in valLength 2021-05-02 12:14:27 +02:00
bobsayshilol
03d03f782e APE: Bounds check the length of values
`pos`, `valLegnth`, and `data.size()` are all unsigned types so we have
to do a little dance to correctly bounds check them without overflow.

Without this we can get stuck in an infinite loop due to 'pos'
overflowing back to the start of the data.
2021-05-02 12:14:27 +02:00
bobsayshilol
1d3e080f04 ID3v2: Bounds check a vector's size
If the provided vector is empty then `data[0]` dereferences a nullptr,
so add a check that this won't be the case before reading the encoding.
2021-05-02 12:14:27 +02:00
bobsayshilol
3391bd80c4 FLAC: Validate the size of a read
Without this we can crash trying to dereference parts of `header`.
2021-05-02 12:14:27 +02:00
Julius Brüggemann
1644c0dd87 Add CMake option for building with ZLib 2021-03-29 20:21:59 +02:00
Scott Wheeler
85cc41082c Update to more modern macOS instructions.
Closes #1005
2021-03-22 11:36:36 +00:00
Jörg Krause
b5cd4c40e2 Drop leftovers WITH_ASF and WITH_MP4
The options WITH_ASF and WITH_MP4 where remove in commit
dd846904cb back in 2011. It's time to
remove the leftovers :-)
2021-03-06 14:54:31 +01:00
Urs Fleisch
932d45259c Fix taglib-config for cross compiling (#998) 2021-03-06 14:52:25 +01:00
Urs Fleisch
844f07d32d Make tests work with BUILD_SHARED_LIBS=ON on Windows 2021-03-06 14:52:25 +01:00
Andreas Sturmlechner
304ab20174 Install taglib.png icon into html folder structure
Signed-off-by: Andreas Sturmlechner <asturm@gentoo.org>
2021-03-06 14:52:25 +01:00
Andreas Sturmlechner
243dd863d7 Install examples after we selected to build them
Signed-off-by: Andreas Sturmlechner <asturm@gentoo.org>
2021-03-06 14:52:25 +01:00
Andreas Sturmlechner
d77e2aee0b Just set CMAKE_MODULE_PATH instead of list(APPEND), drop ENABLE_STATIC err
ENABLE_STATIC error has been there since 2014, that is long enough.

Signed-off-by: Andreas Sturmlechner <asturm@gentoo.org>
2021-03-06 14:52:25 +01:00
Andreas Sturmlechner
1e636957ab Use FeatureSummary for a nice CMake summary output
Signed-off-by: Andreas Sturmlechner <asturm@gentoo.org>
2021-03-06 14:52:25 +01:00
Andreas Sturmlechner
507a42871c Use GNUInstallDirs
Well-established CMake standard for installation directories.

Signed-off-by: Andreas Sturmlechner <asturm@gentoo.org>
2021-03-06 14:52:25 +01:00
Andreas Sturmlechner
01348fb619 Move finding ZLIB to root CMakeLists.txt
Small line decrease, but also easier to read what is happening.
Now all dependencies can be read from the root CMakeLists.txt file.

Signed-off-by: Andreas Sturmlechner <asturm@gentoo.org>
2021-03-06 14:52:25 +01:00
Andreas Sturmlechner
9e0a7f7adb Use CMake's CTest which includes BUILD_TESTING option
BUILD_TESTING is default enabled, which is a good default anyway.
Move the CppUnit check to the root CMakeLists.txt, simpler and clearer.
BUILD_TESTS is obsolete.

The need for BUILD_SHARED_LIBS=OFF for testing is not clear, it works on Linux.
But I kept it in the instructions for now.

Signed-off-by: Andreas Sturmlechner <asturm@gentoo.org>
2021-03-06 14:52:25 +01:00
Scott Wheeler
340ec9932a SVG for API doc diagrams makes a lot more sense in 2021
This also makes things look non-crappy on high-DPI monitors.
2021-02-16 12:00:52 +00:00
Urs Fleisch
c8b39449c3 Version 1.12 2021-02-15 12:23:07 +01:00
Urs Fleisch
bd254654bc Update documentation for version 1.12.0 2021-02-15 10:37:11 +01:00
Urs Fleisch
fbc3f9bbec Fix doxygen warnings 2021-02-14 18:20:29 +01:00
Urs Fleisch
99ad01e12f Fix file name resolver on Windows
The change from "fileref: Use user defined resolvers on streams"
does not work with the Windows FileName implementation, and
TestFileRef::testFileResolver fails.
2021-02-14 18:20:29 +01:00
Urs Fleisch
83aa01c6af Ogg: Set granule position to -1 if no packets finish on page (#864) 2021-02-14 10:04:03 +01:00
Urs Fleisch
4c14571647 Update version to 1.12.0 2021-01-23 09:15:32 +01:00
Urs Fleisch
f4b476a620 Add more unit tests 2021-01-18 20:31:52 +01:00
Urs Fleisch
f8d78a61f7 Merge pull request #990 from ufleisch/ufleisch/more-tests
More unit tests and bug fixes
2021-01-12 18:07:23 +01:00
Urs Fleisch
cf6c68bafc Support a consistent set of MusicBrainz properties where possible
The support for MusicBrainz properties is enhanced with "ARTISTS", "ASIN",
"RELEASECOUNTRY", "RELEASESTATUS", "RELEASETYPE", "MUSICBRAINZ_RELEASETRACKID",
"ORIGINALDATE" on APE, ASF, MP4, ID3v2, and Xiph tags.
2021-01-10 15:19:10 +01:00
Urs Fleisch
f7c24930cd Add missing iTunes properties for MP4 tags 2021-01-10 15:18:59 +01:00
Urs Fleisch
310c3bc043 Add missing 'COMPOSERSORT' property for ID3v2 tags 2021-01-10 15:18:43 +01:00
Urs Fleisch
e6c03c6de8 Create an APE tag when reading a WavPack file without tags
Now it is handled in the same way as for other tags with a
TagUnion. Without this patch, WavPack files without tags cannot
be edited via a FileRef.
2021-01-03 19:06:59 +01:00
Urs Fleisch
2c29fbeabb Use mapped roles instead of property keys for TIPL roles
For an ID3v2 "DJMIXER" property, the "DJ-MIX" TIPL role must be used.
For an ID3v2 "MIXER" property, the "MIX" TIPL role must be used.
Otherwise it will not work when reading the tag and creating
properties from the wrong TIPL roles.
2021-01-03 19:06:59 +01:00
Urs Fleisch
4828a3b925 Do not crash when removing non existing TableOfContentsFrame child 2021-01-03 19:06:59 +01:00
Urs Fleisch
794a2a0b2b Correctly construct PrivateFrame from frame data
Frame::setData() has to be used instead of PrivateFrame::setData().
2021-01-03 19:06:59 +01:00
Urs Fleisch
f32d27973f Use PCST and not TXXX:PODCAST frame for ID3v2 'PODCAST' property 2021-01-03 19:06:59 +01:00
Urs Fleisch
f74b166435 Add missing extensions to FileRef::defaultFileExtensions() 2021-01-03 19:06:59 +01:00
Urs Fleisch
c28dc78060 Add more unit tests
This not only increased the test coverage but also revealed some
bugs which are fixed in the following commits.
2021-01-03 19:06:59 +01:00
Urs Fleisch
89fb62cfb1 Update NEWS 2021-01-02 10:50:52 +01:00
Urs Fleisch
59a2994f4e Fix spelling and typos 2021-01-02 10:50:52 +01:00
Urs Fleisch
f3bdd416da Remove some DSF and DSDIFF leftovers
These can be merged back into master from the branch
add_dsf_and_dsdiff_file_formats.
2021-01-02 10:50:52 +01:00
Urs Fleisch
39f86d02e7 Merge pull request #989 from ufleisch/ufleisch/id3v2-genres
Correctly read and write multiple ID3v2.3.0 genres (#988)
2021-01-01 11:48:35 +01:00
Urs Fleisch
1a2e1d08ac Merge pull request #986 from ufleisch/ufleisch/wav-extensible-subformat
WAV: Support subformat in WAVE_FORMAT_EXTENSIBLE (#850)
2021-01-01 11:48:06 +01:00
Urs Fleisch
3f3b48353c Merge pull request #983 from ufleisch/ufleisch/flac-comment-before-picture
FLAC: Store comment block before picture block (#954)
2021-01-01 11:45:14 +01:00
Urs Fleisch
54f5c66b65 Merge pull request #981 from ufleisch/ufleisch/alac-without-bitrate
Calculate bitrate for ALAC files without it (#961)
2021-01-01 11:45:12 +01:00
Urs Fleisch
3749dd7b75 Write ID3v2.3 genres with multiple references to ID3v1 genres (#988)
As described in id3v2.3.0.txt (4.2.1, TCON), multiple genres are
only possible as references to ID3v1 genres with an optional
refinement as a text. When downgrading multiple genres from
ID3v2.4.0, they are now converted to numbers when possible and
the first genre text without ID3v1 reference is added as a
refinement. The keywords RX and CR are supported too.
2020-12-30 17:38:04 +01:00
Urs Fleisch
d602ae483e Read ID3v2.3 genres with multiple references to ID3v1 genres (#988)
As described in id3v2.3.0.txt (4.2.1, TCON), now multiple genres
are possible, e.g. "(51)(39)". Additionally, support the keywords
RX and CR.
2020-12-30 17:37:54 +01:00
Urs Fleisch
5374cb1ac4 Merge pull request #980 from ufleisch/ufleisch/wav-float-without-fact
Calculate bitrate for IEEE Float WAV files without fact chunk (#959)
2020-12-28 09:16:44 +01:00
Urs Fleisch
8a461ccedc Merge pull request #978 from ufleisch/ufleisch/wavs-with-garbage
Accept WAV files with garbage appended (#973)
2020-12-28 09:07:39 +01:00
Urs Fleisch
17c2220588 Merge pull request #963 from dbry/wavpack-fixes
WavPack fixes with test file verifying issue #962
2020-12-28 09:07:37 +01:00
Urs Fleisch
271c004b30 Merge pull request #942 from uqs/master
Fix spelling of Bebop and update Fusion and Hardcore to match Wikipedia
2020-12-28 09:07:34 +01:00
Urs Fleisch
25d9bd1814 Merge pull request #935 from jiblime/static-config
taglib-config.cmake has static libdir and includedir variables
2020-12-28 09:07:32 +01:00
Urs Fleisch
b0bb7f8c0f Merge pull request #888 from chouquette/use_resolvers_on_streams
fileref: Use user defined resolvers on streams
2020-12-28 09:07:30 +01:00
Urs Fleisch
4d3ab73d2e Merge pull request #855 from shaforostoff/broken_mp3
fix reading audioproperties for broken mp3 files
2020-12-28 09:05:31 +01:00
Urs Fleisch
ae867cbd8c ID3v1: Improve compatibility by mapping renamed genre names to codes
Also added a test to check if the renamed genre names are used and
check if using the old names still works.
2020-12-27 19:53:11 +01:00
Urs Fleisch
563fbaf82a Handle the case when MP4 file header has zero bitrate
This incorporates [6ca536b5] (mp4 properties: handle the case when
mp4 file header has zero bitrate) from PR #899 with a more accurate
bitrate calculation and a unit test.
2020-12-27 10:17:34 +01:00
Urs Fleisch
e5ad041e42 A few more corrections to genre names 2020-12-24 10:08:47 +01:00
Hugo Beauzée-Luyssen
9ca7b0c33a fileref: Use user defined resolvers on streams
Since the resolve only use the filename, there is no reason not to probe
them, as a filename might be available through the IOStream interface.
When using a ByteVectorStream, the filename will be empty and user
defined resolvers won't be probed.
2020-12-23 12:34:54 +01:00
Urs Fleisch
a00b3499b4 WavPack: Add test with non-standard sample rate 2020-12-23 07:04:51 +01:00
Urs Fleisch
d84e86da9c Clean up code to better match the TagLib style 2020-12-23 07:04:51 +01:00
Urs Fleisch
741ed4e4bf Add test for IEEE Float WAV file without fact chunk 2020-12-22 16:02:01 +01:00
Urs Fleisch
e4813f4996 Calculate bitrate for IEEE Float WAV files without fact chunk (#959)
When a WAV file with float format without a fact chunk containing
a totalSamples value is encountered, the bitrate is not calculated.
However, since all samples have the same number of bits, the
number of samples can be calculated from the size of the data chunk
and number of channels and bits per sample, as it is done in the
PCM case.
2020-12-22 15:46:48 +01:00
Urs Fleisch
1332d44ff6 WAV: Test properties of file with WAVE_FORMAT_EXTENSIBLE
uint8we.wav: http://www-mmsp.ece.mcgill.ca/Documents/AudioFormats/WAVE/Samples/AFsp/M1F1-uint8WE-AFsp.wav
2020-12-21 15:42:13 +01:00
Urs Fleisch
3d71ea1ad2 ALAC: Test properties of file without bitrate 2020-12-21 13:31:24 +01:00
Urs Fleisch
02c8995273 Calculate bitrate for ALAC files without it (#961)
If the "alac" atom of an MP4 file has a zero bitrate, it is calculated.
2020-12-21 12:22:25 +01:00
Urs Fleisch
7e92af6e8b FLAC: Test if picture is stored after comment 2020-12-21 11:46:10 +01:00
Urs Fleisch
7ec1127f3e FLAC: Store comment block before picture block (#954)
When the picture block is large and comes before the comment block,
Windows will no longer display the tags. This can be fixed by
storing the comment block before the picture block instead of
appending it at the end.
2020-12-21 11:09:15 +01:00
Urs Fleisch
30d839538d Make PlainFile available to all tests 2020-12-21 08:44:32 +01:00
Scott Wheeler
8f6b6ac055 Don't pull in C++11+ for one class 2020-12-20 18:38:05 +01:00
Scott Wheeler
a5f11697f7 Get things building again with C++98 2020-12-20 18:12:14 +01:00
Scott Wheeler
1721354627 Explicitly set C98 for this target
Sometime I'd like to get the lib over to C++1x, but for now, enforce
the currently in-use standard.
2020-12-20 14:22:20 +00:00
Scott Wheeler
ac7e5303a6 Merge pull request #969 from ujjwalsh/master
Adding support for Linux on power
2020-12-20 15:17:40 +01:00
Scott Wheeler
8ba246cdbe Merge pull request #984 from ufleisch/ufleisch/m4a-empty-strings-remove-items
MP4: Remove item when empty string is set (#929)
2020-12-20 15:13:58 +01:00
Scott Wheeler
6656528f18 Also allow #include <taglib/foo.h> with taglib.pc and friends
Closes #495
2020-12-20 14:09:49 +00:00
Urs Fleisch
bad2cea122 WAV: Support subformat in WAVE_FORMAT_EXTENSIBLE (#850) 2020-12-20 11:47:04 +01:00
Urs Fleisch
61d5bcfd5b MP4: Remove item when empty string is set (#929)
This will make the behavior for MP4 tags compliant to the API
documentation and consistent with other tag formats.
2020-12-13 12:37:20 +01:00
Urs Fleisch
2dd6931eee Accept WAV files with garbage appended (#973)
This will allow editing the tags of WAV files which have data
appended at the end. The corresponding unit test checks that the
original contents are still available after editing the metadata
of such files.
2020-12-06 08:56:35 +01:00
Scott Wheeler
91b00b17b2 Missing const here 2020-10-12 16:52:09 +02:00
Scott Wheeler
e116824380 Downgrade numerical genres back to ID3v2.3 format
Closes #631
2020-10-12 08:46:51 +02:00
Scott Wheeler
2db13ad8cf Add a little sanity to the formatting here 2020-10-12 08:36:57 +02:00
ujjwalsh
cd767738fc adding support for linux on power 2020-09-01 16:55:07 +00:00
David Bryant
43bc11541d correctly read very high sample rates from WavPack files 2020-07-02 17:22:03 -07:00
David Bryant
6806dc4cf2 make WavPack seekFinalIndex() more robust to false triggers 2020-07-02 16:15:51 -07:00
David Bryant
ec9c49b964 add test for WavPack DSD (issue #962) 2020-07-01 08:35:20 -07:00
David Bryant
872cafb0b9 Several fixes for WavPack files, including issue #962
Fixes to properly handle WavPack files with leading and
trailing non-audio blocks, non-standard sampling rates,
and DSD audio (introduced in WavPack 5).
2020-06-30 22:19:38 -07:00
Scott Wheeler
47342f6974 Prefer COMM frames with no description for setComment()
This creates symetry with ID3v2::Tag::comment() in preferring frames with no description
when choosing which COMM frame should be updated.  (Previously setComment() simply updated
the first COMM frame.)

Fixes #950
2020-03-27 12:14:51 +01:00
Ulrich Spörlein
5763d042a6 Fix spelling of Bebop and update Fusion and Hardcore to match Wikipedia
Source: https://en.wikipedia.org/wiki/List_of_ID3v1_Genres

Similar patches will be sent to libid3tag and ffmpeg to harmonize the
genre names and spellings.
2019-12-19 14:53:57 +01:00
jiblime
cd9e6b7502 Changed libdir/includedir variables to change based on a user's system and match syntax 2019-10-19 15:13:28 -07:00
Scott Wheeler
54508df30b Needs to be defined to nothing if none of the #if blocks match 2019-09-20 10:32:33 +02:00
Scott Wheeler
dcf0331eb1 Use newer function signatures 2019-09-19 15:23:34 +02:00
Scott Wheeler
96155f35fa Use new method signatures 2019-09-19 15:19:17 +02:00
Scott Wheeler
ebd3373d6d Correctly decode signed values
In SV7 these are a mix of signed and unsigned shorts; in SV8 they're all
signed.  Storing them as an int is fine for signed or unsigned shorts as
it's wide enough to contain either of them.

Unfortunately there are no explicit tests for SV7 at the moment; that
would be ideal to add before the next release.

CC @carewolf
2019-09-12 12:13:24 +02:00
Scott Wheeler
f3ecfa11bb Completely remove StripAll from the API
I'd imagined it being useful for calls to `strip()`, but it's not
actually used there since that's an OR-ed together set of flags 
representing which tags to strip.
2019-09-12 11:19:57 +02:00
Scott Wheeler
7082b9f66b Unsaved (and incorrect looking) field 2019-09-12 10:46:11 +02:00
Scott Wheeler
de25bc6111 StripAll should be treated as equivalent to StripOthers in save() 2019-09-12 07:57:16 +02:00
Scott Wheeler
074f30e3fa Remove DSF and DSDIFF from master to a feature branch
These can be merged back into master once they're in a more mature state.
2019-09-12 07:55:34 +02:00
Scott Wheeler
f1b40de66b Unify File::save(...) APIs between file formats that support ID3v2
Closes #922
2019-09-11 06:48:27 +02:00
Scott Wheeler
0b99cd9bac Revert switch to other static size method
This was based on a misread of the header:  at present there is no
non-static size() method, so removing the argument makes the behavior
incorrect.
2019-09-11 00:58:18 +02:00
Scott Wheeler
4668cf0f93 Missing header that should have been added in b8dc105 2019-09-11 00:42:15 +02:00
Scott Wheeler
c05fa78406 Mark deprected methods and remove internal usage
This does not put the deprecated marker on methods that will or could resolve
to the same overload, e.g.:

void foo(bool bar = true); // <-- not marked
void foo(Bar bar) // <-- since this will have a default argument in the new version
2019-09-11 00:39:37 +02:00
Scott Wheeler
b8dc105ae3 Deprecate calls to MPEG::File::save(...) that use boolean params
This uses explicit enums for e.g. the ID3v2 version, making calls more
readable:

  file.save(ID3v1 | ID3v2, StripOthers, ID3v2::v4, Duplicate);

Instead of:

  file.save(ID3v1 | ID3v2, true, 4, true);

Needs to be ported to other types, per #922
2019-09-10 22:59:07 +02:00
Scott Wheeler
fced0f46e9 Add docs for this method 2019-09-10 13:31:44 +02:00
Scott Wheeler
dc0f667a4c No tabs in TagLib 2019-09-10 13:08:11 +02:00
Scott Wheeler
085180e9a4 Require at least CMake 3 2019-09-10 12:45:36 +02:00
Scott Wheeler
ef1312d622 Add -lz to taglib.pc and taglib-config when built with zlib
Closes #872
2019-09-10 12:41:11 +02:00
StefanBruens
86c0428475 Clear valid flag for invalid Speex files
This matches the corresponding code in vorbisfile.cpp, opusfile.cpp and flagfile.cpp, and fixes taglib/taglib#902.
2019-09-10 11:44:21 +02:00
Scott Wheeler
3c78c4cfc9 Merge pull request #883 from ufleisch/riff-padding
Do not ignore non zero RIFF padding if leading to parse error (#882)
2019-09-10 11:25:46 +02:00
Scott Wheeler
c8bb6271e5 Merge pull request #917 from ufleisch/ogg-bitrate
Calculate Ogg bitrate without comment size (#874)
2019-09-10 11:14:15 +02:00
Scott Wheeler
84f7462526 Tests need C++11 2019-09-05 17:29:40 +02:00
Urs Fleisch
2f23892182 Calculate Ogg bitrate without overhead size (#874) 2019-09-02 22:14:41 +02:00
Scott Wheeler
2918602ad0 Merge pull request #910 from joerg-krause/patch-1
Drop CMAKE_SYSROOT from taglib-config
2019-08-26 23:37:24 +02:00
Scott Wheeler
c146cd7e92 Merge pull request #909 from williamjcm/patch-1
Make TagLib seach in its source dir for utf8-cpp.
2019-08-26 23:34:47 +02:00
Scott Wheeler
b124e621fe Merge pull request #912 from whatdoineed2do/m4a-track-year-equal0-bugfix
MP4 - setTrack()/setYear() accepts 0 to remove the tag
2019-08-26 23:32:44 +02:00
Scott Wheeler
044fba2921 Merge pull request #919 from jonaski/spelling
Fix spelling and typos
2019-08-26 23:26:55 +02:00
Jonas Kvinge
e72a98903f Fix spelling and typos 2019-08-26 23:23:33 +02:00
whatdoineed2do
79bc9ccf8e Call fflush() before ftruncate() to drop all buffered data (#914)
This avoids stale data presented to caller via a fread()

Current bug due to the buffered data can be seen in stripping mp3s of tags

    f.strip(ID3v1);
    f.strip(APE);

The ID3v1 tag sits at the end of file (strip calls ftruncate()) and the APE
strip performs a readFile() that would return the stream buffered/truncated data
and reinsert
2019-07-25 07:27:49 +04:30
whatdoineed2do/Ray
850a3565a4 setTrack()/setYear() accepts 0 to remove the tag as per
documentation/functionality across other tpyes (mp3/flac/...); m4a do not honour
this and instead sets the underlying value to 0.

This commit fixes this issue (#911)
2019-07-13 16:03:51 +01:00
Jörg Krause
18d424995f Drop CMAKE_SYSROOT from taglib-config
Commit d4c938cbc7 is about fixing taglib-config for proper cross-compilation. The fix is right in principle, but wrong about adding `CMAKE_SYSROOT`. The correct prefix path should be set outside of the config file as some embedded Linux distros like OpenWrt or OpenEmbedded install with a different DESTDIR, and dependent packages see a different sysroot.
2019-06-16 15:33:52 +02:00
Guillaume Jacquemin
d04db70bc6 Make TagLib seach in its source dir for utf8-cpp.
Without this change, TagLib is unusable as part of CMake projects that rely on `add_subdirectory()` for dependencies.
2019-06-01 10:19:27 +02:00
StefanBruens
ba7adc2bc2 Respect atom type when converting rate tag (#896)
* Respect atom type when converting rate tag

TagLib prior to #818 (commit ff28cf276c) read and wrote the "rate" tag as
text, and switched to reading it as integer value even when the atom class
is a text type. This breaks reading existing files, and can be avoided by
taking the atom type into account.

This fixes issue #885.

* Respect MP4::Item type when writing the rate tag

TagLib prior to #818 (commit ff28cf276c) read and wrote the "rate" tag
as text, and switched to writing the integer value of the MP4::Item.
This breaks writing from applications which supply the value as
StringList, which was the previously implemented API. Applications using
an MP4::Item(UInt) are still supported without changes on the application
side.

This is the complementary writing part for issue #885.
2019-05-31 15:51:16 +04:30
Urs Fleisch
79bb1428c0 Support ID3v2 GRP1 frame introduced with iTunes 12.5.4.42, #903. (#904) 2019-05-17 15:45:48 +04:30
Jörg Krause
7470f92a67 fix taglib-config file for cross compiling (#906) 2019-05-17 15:43:35 +04:30
Tim Malseed
6455671ece Update mp4properties.cpp (#893)
When parsing mp4 media header version 1 (mdhd) atoms, the timescale (unit) is parsed as a `LongLong` (8 bytes), but instead should be a `UInt` (4 bytes). This results in an incorrect timescale, and also pushes the offset of the duration (length) off by 4 bytes.

The end result being that the AudioProperties track length for mp4's with mdhd v1 comes back as 0.

See: https://wiki.multimedia.cx/index.php/QuickTime_container

|  Entry | Bytes (v0) | Bytes (v1) |
| :---         |     :---:      | :---: |
| size  | 4  | 4 |
| type  | 4  | 4 |
| version | 1 | 1 |
| flags | 3 | 3 |
| creation time* | 4 | **8** |
| modification time* | 4 | **8** |
| time scale | 4 | 4 |
| duration* | 4 | **8** |
| language | 2 | 2 |
| quality | 2 | 2 |
2019-03-17 08:22:19 -05:00
Tim Malseed
660748210f Minor fix for mp4 media header v0 minimum size check (#895)
Mp4 media header (mdhd) v0 atoms are a minimum of 8 bytes for size & type information, plus 24 bytes for remaining entries (`24 +8`) bytes in total, rather than (`24 + 4`).

See https://developer.apple.com/library/archive/documentation/QuickTime/QTFF/QTFFChap2/qtff2.html#//apple_ref/doc/uid/TP40000939-CH204-25615
2019-03-17 08:20:43 -05:00
Urs Fleisch
1cf176af61 Do not ignore non zero RIFF padding if leading to parse error (#882) 2019-02-10 08:40:20 +01:00
Stephen F. Booth
5cb589a5b8 Updated NEWS for the latest changes 2018-10-28 08:43:45 -05:00
Stephen F. Booth
a4d04e0c40 Added APE, DSF, and DFF formats to the list 2018-10-28 08:42:19 -05:00
Stephen F. Booth
4ab0891646 Added DSF and DSDIFF authors 2018-10-28 08:35:15 -05:00
Jonas Kvinge
d71398c953 Add DSF and DSDIFF file types management (#878)
* Add DSF and DSDIFF file types management

* Fixes for some build chains

* unit64_t replaced by unsigned long long, warning fixes

* Remove C++11 extension incompatible with some build chains (enumeration in a nested name specifier)

* Change typedef types (uint, ulong, ...) to standard types
remove BUILD_FRAMEWORK changes from this pull request

* Replace deprecated String::null and ByteVector::null by String() and ByteVector()
Styling update, thanks to FestusHagen

* Restyling

* Restyling to reduce length of excessively long lines

* Add to detectByExtension

* Added `isSupported(IOStream *stream)` to `DSF::File` and `DSDIFF::File`
2018-10-26 19:45:49 -05:00
evpobr
bfed3797a0 Improve CMake VISIBILITY_HIDDEN option handling (#810)
Use standard CMake's CXX_VISIBILITY_PRESET property.
2018-10-26 19:26:53 -05:00
Ola Nordstrom
e435372146 added OS X built files to .gitignore (#828) 2018-10-26 19:23:44 -05:00
Bert Wesarg
c2f544c9d1 Fill TableOfContentsFrame::toString(). (#852) 2018-10-26 19:21:18 -05:00
Scott Wheeler
8ca75f03b5 Follow TagLib's brace style 2018-10-10 19:24:44 +02:00
safu9
036a0317b9 Add support for file descriptor to FileStream (#832)
Add support for file descriptor
2018-10-10 12:25:02 -04:00
Bert Wesarg
8f6fe0b16c Don't list the description twice in UserTextIdentificationFrame::toString() (#853) 2018-10-09 18:55:02 -05:00
Scott Gayou
2c4ae870ec Fixed OOB read when loading invalid ogg flac file. (#868) (#869)
CVE-2018-11439 is caused by a failure to check the minimum length
of a ogg flac header. This header is detailed in full at:
https://xiph.org/flac/ogg_mapping.html. Added more strict checking
for entire header.
2018-10-09 18:46:55 -05:00
Urs Fleisch
d8d56d3937 Add support for cmID, purl, egid MP4 atoms (#862). (#863) 2018-04-09 08:32:05 -05:00
Nick Shaforostov
2d90d938fe fix reading audioproperties for broken mp3 files 2018-03-02 16:18:10 +01:00
Scott Wheeler
a80093167f Update links 2017-11-20 00:03:52 +01:00
Xijian Yan
249f892455 Fix crash when loading an empty mpeg file (#830)
When loading an empty file (empty.txt -> empty.mp3 ), TagLib will crash.
buffer.size() is 0, then buffer.size() - 1 is undefined (unsigned int)
2017-11-06 09:48:17 -06:00
Stephen F. Booth
cb9f07d9dc Don't assume TDRC is an instance of TextIdentificationFrame (#831)
If TDRC is encrypted, FrameFactory::createFrame() returns UnknownFrame
which causes problems in rebuildAggregateFrames() when it is assumed
that TDRC is a TextIdentificationFrame
2017-09-30 10:15:41 -05:00
Sergei Trofimovich
0b583bafd0 taglib: fix test build failure on powerpc/c++11 (#834)
powerpc is a platform with 'char' == 'unsigned char'.
As a result '-1' is not expressible in char and build fails as:

```
    # '-funsigned-char' to force test build failure on other platforms
    $ cmake .. -DBUILD_TESTS=YES -DCMAKE_CXX_FLAGS="-O2 -funsigned-char" -DCMAKE_C_FLAGS="-O2 -funsigned-char"
    ...
    $ make check
    tests/test_synchdata.cpp: In member function 'void TestID3v2SynchData::testToUIntBroken()':
    tests/test_synchdata.cpp:78:33: error: narrowing conversion of '-1' from 'int' to 'char' inside { } [-Wnarrowing]
     char data[] = { 0, 0, 0, -1 };
                                 ^
```

The fix is to expliticly cast -1 to 'char'.

Signed-off-by: Sergei Trofimovich <slyfox@gentoo.org>
2017-09-18 20:06:55 -05:00
Tsuda Kageyu
4648394841 Check if mandatory header objects are present when opening ASF files.
Also removes some assignments of "this".
It feels too tricky when it is not absolutely necessary.
2017-06-13 17:22:00 +09:00
Tsuda Kageyu
e026d797e0 Use a macro to pretend virtual functions. 2017-06-13 17:01:53 +09:00
Tsuda Kageyu
eeb2f5de09 Fix some typos in debug messages. 2017-06-13 15:05:25 +09:00
Tsuda Kageyu
1792ee9db8 Update NEWS. 2017-06-12 13:07:21 +09:00
Tsuda Kageyu
1fb310ec1f Merge pull request #799 from TsudaKageyu/filetype-detection
Enable FileRef to detect file types by the actual content of a stream.
2017-06-12 13:04:15 +09:00
Tsuda Kageyu
c8bcd153fe TableOfContentsFrame depends on ByteVectorList. 2017-06-09 17:52:56 +09:00
Tsuda Kageyu
662f340f93 Merge pull request #824 from evpobr/fix-createfile2
Fix WinRT configuring
2017-06-09 08:53:25 +09:00
Tsuda Kageyu
48d8c0a808 Merge pull request #823 from TsudaKageyu/isolate-3rdparty-lib
Isolate 3rdparty library for easier maintenance.
2017-06-09 08:53:10 +09:00
evpobr
5ebd3d5276 Fix WinRT configuring
Don't rely on _WIN32_WINNT value to enable WinRT support.

if _WIN32_WINNT is not set manually, it is defaulted to SDK version. So
if you use SDK > 8 you cannot use TagLib under Win7 and lower because of
CreateFile2 function dependency.

PLATFORM_WINRT option (OFF by default) was introduced to enable WinRT
build.

Related issues: https://github.com/Microsoft/vcpkg/issues/1240
2017-06-08 19:00:37 +05:00
Tsuda Kageyu
14af861d24 Isolate 3rdparty library for easier maintenance. 2017-06-07 12:51:44 +09:00
Tsuda Kageyu
3795f277fb Update UTF8-CPP to v2.3.5. 2017-06-07 12:12:31 +09:00
Tsuda Kageyu
6045995e65 Remove an unused include directory from taglib/CMakeLists.txt. 2017-06-07 11:38:46 +09:00
Tsuda Kageyu
c2fe93c12b Restore FileRef::create() in order not to change the previous behavior. 2017-06-06 09:17:34 +09:00
Michael Helmling
ea9202d9ee Update INSTALL.md
Added a note that with MS Visual Studio 2017 one can directly open the CMake project.
2017-06-05 14:49:09 +02:00
Stephen F. Booth
3c657d1a44 Merge pull request #821 from supermihi/master
Fix Markdown syntax of INSTALL file and rename it to *.md
2017-06-05 07:37:12 -04:00
Michael Helmling
adf0f76360 Fix Markdown syntax of INSTALL file and rename it to *.md
This enables proper formatted display of the file on e.g. GitHub, increasing readability.
2017-06-05 11:09:16 +02:00
Tsuda Kageyu
682ea77c2b Mention that FileRef::create no longer works. 2017-05-31 09:41:41 +09:00
Tsuda Kageyu
15c3c756ca Update NEWS. 2017-05-31 09:28:30 +09:00
Tsuda Kageyu
4801fbb927 Merge pull request #818 from LindyBalboa/add_rate_atom_support
Add direct support for "rate" atom
2017-05-31 09:20:27 +09:00
Tsuda Kageyu
22de22b701 Fix memory leaks in a test. 2017-05-26 16:49:36 +09:00
Tsuda Kageyu
48a1a05a88 Fix MSVC warnings about unreferenced variables. 2017-05-23 17:46:16 +09:00
Tsuda Kageyu
6000a19f70 Fix the Travis-CI testing on OS X.
AppleClang 7.3 doesn't get along with CppUnit by default.
2017-05-23 17:33:22 +09:00
Conner R. Phillips
ff28cf276c Add direct support for "rate" atom
Resolves #817
2017-04-22 07:45:29 +02:00
Tsuda Kageyu
4891ee729d Remove an useless UTF-8 BOM. 2017-02-24 15:47:30 +09:00
Tsuda Kageyu
9419dab51b Allow SYLT frames to have a timestamp with no text.
Thanks to lemonboy999.
2017-02-24 15:40:30 +09:00
Tsuda Kageyu
45ee18e206 FilePrivate is responsible to delete a stream pointer instead of File.
Generally, TagLib leaves the Private classes to delete their members.
2017-02-08 17:49:48 +09:00
Tsuda Kageyu
bf7ee62dc6 Merge branch 'filetype-detection' of https://github.com/TsudaKageyu/taglib into filetype-detection 2017-02-07 22:37:13 +09:00
Tsuda Kageyu
f76b1e5429 Rename the functions 'isValidStream' to 'isSupported'.
The name 'isValidStream' is a little misleading because it doesn't check if the stream is really valid. Additionally, 'isSupported' can be naturally overloaded.
2017-02-07 22:36:56 +09:00
Tsuda Kageyu
f7b15fad20 Remove some redundant code. 2017-02-06 10:35:49 +09:00
Tsuda Kageyu
dd4adf94ce Fix wrong endian of boolean values when saving ASF files. 2017-02-06 10:06:10 +09:00
Tsuda Kageyu
d4d8410c08 Restore the layout of the copyright header of test_fileref.cpp. 2017-02-04 23:45:15 +09:00
Tsuda Kageyu
931bb042c3 Enable FileRef to detect file types by the actual content of a stream.
FileRef doesn't work with ByteVectorStream as reported at #796, since ByteVectorStream is not associated with a file name and FileRef detects file types based on file extensions.
This commit makes FileRef to work with ByteVectorStream by enabling it to detect file types based on the actual content of a stream.
2017-02-04 23:31:08 +09:00
Tsuda Kageyu
a5d9e49c49 Remove obsolete comments.
The bug mentioned in the comments are already fixed.
2017-02-04 01:31:20 +09:00
Scott Wheeler
179c175a6c Ignore warnings about OSAtomicIncrement32Barrier
The warnings suggest moving to std::atomic functions, but those are only
available in C++11.  It would be possible to switch to the C versions of
those functions, which are now provided in stdatoic.h (in C11), but
let's wait until we actually hit problems with this function and are a
few more OS versions into C11 headers being included by default.
2017-02-03 13:56:02 +00:00
Tsuda Kageyu
ba98628919 Avoid searching the same area twice in MPEG::File::previousFrameOffset(). 2017-02-01 14:23:03 +09:00
Tsuda Kageyu
87fc4012f4 Add some test cases for invalid UTF-8 sequences. 2017-01-31 14:27:23 +09:00
Tsuda Kageyu
dd5ab2a08f Fix and add some test cases for invalid surrogate pairs. 2017-01-31 14:19:30 +09:00
Tsuda Kageyu
b74ffba4b5 Update NEWS. 2017-01-31 00:21:41 +09:00
Tsuda Kageyu
4552f2c2eb Remove redundant functions in tstring.cpp.
Two versions of copyFromUTF16() are almost the same.
2017-01-30 22:38:08 +09:00
Tsuda Kageyu
6398796f95 Remove function bodies of some non-specialized template functions.
The code won't link when a wrong version is used. It's better than showing a debug message.
2017-01-30 16:11:59 +09:00
Tsuda Kageyu
2c7ac6d6a9 Add a few more test cases for invalid UTF-8 sequences. 2017-01-30 12:56:53 +09:00
Tsuda Kageyu
6a61f02f85 Merge pull request #794 from TsudaKageyu/utf8-library
Replace unicode.h/unicode.cpp by the UTF8-CPP library.
2017-01-30 12:54:43 +09:00
Tsuda Kageyu
038b52ae01 Check an invalid UTF-8 sequence consists of single char.
Single char can be an invalid UTF sequence. For example, { 0x80 } is invalid.
2017-01-30 11:35:39 +09:00
Tsuda Kageyu
598ab752bc Stop assuming that std::wstring has a contiguous and null-terminated buffer. 2017-01-30 00:36:38 +09:00
Tsuda Kageyu
922fd611ae Reduce useless memory reallocation in String::upper(). 2017-01-28 01:17:21 +09:00
Tsuda Kageyu
3d14ff74b1 Remove a duplicate test file. 2017-01-27 22:10:02 +09:00
Tsuda Kageyu
978b822774 Remove some redundant code in tstring.cpp. 2017-01-27 15:11:08 +09:00
Tsuda Kageyu
0c45c63943 Replace unicode.h/unicode.cpp by the UTF8-CPP library.
unicode.h/unicode.cpp are no longer maintained and incompatible with Debian's guideline.
UTF8-CPP is maintained on GitHub and published under the Boost Software License which is compatible with either LGPL or MPL and should go along with Debian's guideline.
2017-01-27 14:47:55 +09:00
Tsuda Kageyu
586c9bd962 Add a test for unpaired surrogate characters in a UTF-16 string. 2017-01-26 17:33:54 +09:00
Tsuda Kageyu
fc38a0e401 Remove some redundant code.
TagUnion::access() does the same thing as FLAC::File::ID3v2Tag().
2017-01-22 00:43:32 +09:00
Tsuda Kageyu
5fc5a2e81a Prefer isEmpty()/empty() to size() == 0. 2017-01-21 19:10:32 +09:00
Tsuda Kageyu
a358e87cc4 Revert useless changes accidentally committed. 2017-01-21 11:13:49 +09:00
Tsuda Kageyu
5ba8b740f9 Add missing consts. 2017-01-21 11:09:05 +09:00
Tsuda Kageyu
c4a3c3ab97 Combine two internal functions which are always used together. 2017-01-21 01:34:50 +09:00
Tsuda Kageyu
6bb92c34fa Ignore fake MPEG frame headers when seeking them. 2017-01-20 22:38:25 +09:00
Tsuda Kageyu
d2e0e55223 Efficient lookup for an ID3v2 tag in MPEG files with garbage.
This looks for an ID3v2 tag until reaching the first valid MPEG frame in O(n) time.
2017-01-20 21:14:38 +09:00
Tsuda Kageyu
d64c833359 Update NEWS. 2017-01-16 01:14:35 +09:00
Tsuda Kageyu
c9c757e0ff Merge pull request #791 from TsudaKageyu/flac-empty-seektable
Be tolerant of empty FLAC seektable blocks.
2017-01-16 01:12:07 +09:00
Tsuda Kageyu
9b548260f5 Initialize d-pointers in class member initializer list. 2017-01-16 01:05:30 +09:00
Tsuda Kageyu
406e872ac3 Always use parentheses with new. It's a bit safer. 2017-01-16 01:00:28 +09:00
Tsuda Kageyu
193cbe3b6b Initialize all the data members of ASF::Attribute. 2017-01-14 23:29:46 +09:00
Tsuda Kageyu
13be28a52c Be tolerant of empty FLAC seektable blocks. 2017-01-10 00:11:13 +09:00
Tsuda Kageyu
56a7656c2e Remove some TODO comments which are no longer necessary. 2017-01-08 01:43:04 +09:00
Michael Helmling
c97be6630e Fix #789 (typo in member doc) 2017-01-06 09:08:02 +01:00
Stephen F. Booth
6fcc690233 Merge pull request #787 from rshanmu/master
Renamed unsupported-extension.xxx and modified test
2016-12-22 12:17:12 -05:00
Ramesh Shanmugasundaram
83c72518ab Renamed unsupported-extension.xxx and modified test
The file name unsupported-extension.xxx causes issue when unpacked
taglib-xxx.gz over an NFS partition. The file extension ".xxx" is the
one NFS uses for its own purpose and hence it will not allow creation
of this file.

Hence renamed the file and modified the test cases that checks for this
file.

Signed-off-by: Ramesh Shanmugasundaram <ramesh.shanmugasundaram@bp.renesas.com>
2016-12-22 15:19:45 +00:00
Tsuda Kageyu
de87cd7736 Remove the CMake check for Boost I missed out on. 2016-12-20 12:06:21 +09:00
Tsuda Kageyu
14c3ce5737 Remove all the optional dependencies on Boost. 2016-12-20 11:54:06 +09:00
Tsuda Kageyu
ffa32b19a7 Fix the CMake check for std::atomic_int.
std::atomic_int of Visual C++ 2012 cannot be constructed with integer.
2016-12-20 11:48:14 +09:00
Tsuda Kageyu
8905e7095a Safer conversion of boolean values in ASF attributes.
Technically, boolean values in Extended Content Description Object is not necessarily be 0 or 1.
2016-12-19 10:37:10 +09:00
Tsuda Kageyu
a19a623d4b Make use of increment/decrement operators of std::atomic. 2016-12-09 10:09:31 +09:00
Tsuda Kageyu
250c59f783 Remove optional dependencies on Boost's dynamic libraries.
Using precompiled Boost libraries can lead to depending on external dynamic libraries.
2016-12-09 09:42:29 +09:00
Tsuda Kageyu
8eda5d5053 Merge pull request #784 from haoxi911/master
Fix #667: Compiled TagLib framework for OS X fails at codesign.
2016-12-09 09:00:04 +09:00
Hao Xi
b5115e3497 Fix #667: Compiled TagLib framework for OS X fails at codesign. 2016-12-08 12:53:40 +08:00
Tsuda Kageyu
36ccad2bd4 Rewrite ByteVector::replace() to run in O(n) time. 2016-12-05 11:02:59 +09:00
Tsuda Kageyu
b00a5c1aab Add a test to check if ByteVector is detached correctly when being replaced. 2016-12-05 10:15:26 +09:00
Tsuda Kageyu
f6a604f54b #include guards in CMake generated headers. 2016-12-02 17:26:43 +09:00
Tsuda Kageyu
489e2e6cbb Update NEWS. 2016-12-01 15:25:30 +09:00
Tsuda Kageyu
9336c82da3 Fix possible Ogg packet losses. 2016-12-01 11:32:01 +09:00
Tsuda Kageyu
cfbaf34597 Prevent the segment table of Ogg pages from exceeding the size limit. 2016-12-01 10:51:59 +09:00
Tsuda Kageyu
046c98230f Remove Utils::floatByteOrder() and use systemByteOrder() instead.
We can safely assume that the integer and float byte orders are the same on IEEE754 compliant systems.
2016-11-29 14:58:39 +09:00
Tsuda Kageyu
4381bd75f3 Add a test for #743/#779. 2016-11-29 10:53:33 +09:00
Tsuda Kageyu
6df61cf2af Small fix in style. 2016-11-29 10:38:11 +09:00
Tsuda Kageyu
e9ef40fe7f Merge pull request #779 from supermihi/master
Fixes #743 by not overwriting existing Xiph comment in FLAC::File::save
2016-11-28 21:03:39 +09:00
Michael Helmling
2786aa7463 Fixes #743 by not overwriting existing Xiph comment in FLAC::File::save 2016-11-27 19:17:13 +01:00
Tsuda Kageyu
d3062f3af4 A bit more tolerant check to return itself in String::substr(). 2016-11-26 13:05:14 +09:00
Stephen F. Booth
7871afec37 Merge pull request #778 from martin-flaska/optimization
String::substr optimization
2016-11-25 16:38:32 -05:00
Martin Flaska
c9a0754e3b tstring: String::substr optimization when returning itself as a substring
Use copy ctor to return in a case whole string is being returned.

The intention was to optimize String::stripWhiteSpace for no-strip case
(without any leading or trailing white space removal).

copyFromUTF16 was used in any case previously and allocated duplicate
buffer for the same string - no implicit sharing.

Signed-off-by: Martin Flaska <martin.flaska@legrand.us>
2016-11-25 15:32:26 +01:00
Martin Flaska
6cfb11bb12 test_string: Make 'stripWhiteSpace' test more complex
No string without leading/trailing spaces was used in the test.

Signed-off-by: Martin Flaska <martin.flaska@legrand.us>
2016-11-25 13:56:39 +01:00
Tsuda Kageyu
ad075a56f9 Suppress MSVC warnings in test. 2016-11-24 14:45:22 +09:00
Stephen F. Booth
f80a7c0d83 Merge pull request #776 from mathbunnyru/small_improvements
Small improvements
2016-11-23 23:31:49 -05:00
mathbunnyru
5e1d9fad31 Small fixes 2016-11-24 02:05:19 +03:00
mathbunnyru
eff28c55bf Increment fixes 2016-11-22 01:10:28 +03:00
Tsuda Kageyu
d5b9d7b8a7 Update NEWS. 2016-11-18 13:55:43 +09:00
Tsuda Kageyu
ce77fbb0e7 Merge pull request #772 from TsudaKageyu/vorbis-fields
Fix handling of lowercase 'metadata_block_picture' field
2016-11-18 13:52:38 +09:00
Tsuda Kageyu
b98a984b66 Fix handling of lowercase 'metadata_block_picture' fields in Vorbis comments.
Also refactored some redundant code for parsing pictures.
2016-11-11 00:07:32 +09:00
Tsuda Kageyu
f9a747dceb Avoid adding fields with invalid keys to Vorbis Comments.
According to the spec, '\x7F' is not allowed.
2016-11-10 23:35:14 +09:00
Tsuda Kageyu
7b8d576bde Don't decode redundant UTF-8 sequences in Win32.
Linux and OS X are working well and won't be affected.
2016-11-10 17:12:58 +09:00
Tsuda Kageyu
2651372291 Separate some tests to make them more specific. 2016-11-09 15:51:33 +09:00
Tsuda Kageyu
499f6db977 Check invalid Unicode APE keys properly. 2016-11-09 00:29:03 +09:00
Tsuda Kageyu
9d58e9f8e8 Removed a utility function which is used only at one place. 2016-11-08 23:27:55 +09:00
Tsuda Kageyu
56cd3695f7 Add README.md. 2016-11-08 22:56:42 +09:00
Tsuda Kageyu
d81d894d41 tolower() depends on the current locale.
It's much easier to write our own function than to use locales properly.
2016-11-08 21:39:53 +09:00
Tsuda Kageyu
e390cbac52 Update NEWS. 2016-11-08 21:17:00 +09:00
Tsuda Kageyu
253c61e37d Merge pull request #765 from TsudaKageyu/zero-length-atom
Proper handling of MP4 atoms with zero length.
2016-11-08 21:12:47 +09:00
Tsuda Kageyu
1848b3bc6f Merge pull request #759 from ufleisch/mp4_classical
Support new classical music frames introduced with iTunes 12.5, #758.
2016-11-08 21:12:31 +09:00
Tsuda Kageyu
aae23f3c07 Initialize all the data members of ID3v2::ChapterFrame. 2016-11-07 14:12:38 +09:00
Tsuda Kageyu
da9df1b2a8 Values of FILE_* macros are guaranteed in Win32. 2016-11-07 00:42:12 +09:00
Tsuda Kageyu
70334edd19 Add List::swap() and Map::swap(). 2016-11-04 16:43:14 +09:00
Tsuda Kageyu
f5ca097379 Proper handling of MP4 atoms with zero length.
If the size of an atom is 0, it designates the last atom which extends to the end of the file.
2016-11-02 15:44:50 +09:00
Tsuda Kageyu
eb6d058ab9 Remove a useless branch.
longLength <= LONG_MAX is always true if sizeof(long) == sizeof(long long).
2016-11-01 16:03:15 +09:00
Tsuda Kageyu
e6a69e24bc Add a common function to generate a long string to test. 2016-10-31 20:01:52 +09:00
Tsuda Kageyu
dcab8ed90e Allow ScopedFileCopy to be const. 2016-10-31 10:29:13 +09:00
Tsuda Kageyu
f5ce498182 Suppress MSVC warnings about narrowing conversions. 2016-10-30 23:51:35 +09:00
Tsuda Kageyu
ee7fa78011 Update NEWS. 2016-10-30 22:51:15 +09:00
Tsuda Kageyu
873c917081 Assume that SetFilePointerEx() and GetFileSizeEx() are always available.
This drops support for Windows 9x and NT 4.0 or older.
2016-10-30 22:36:18 +09:00
Tsuda Kageyu
d3bd8fb7ff Assume that CreateFileW() is always available.
This drops support for Windows 9x.
2016-10-30 22:25:34 +09:00
Tsuda Kageyu
005441faaa Prevent overflows, just in case. 2016-10-28 15:25:50 +09:00
Tsuda Kageyu
935534aa53 Backport a test from taglib2 branch. 2016-10-28 15:19:35 +09:00
Tsuda Kageyu
65a24bbc51 Remove some useless seek()/tell() from RIFF::File. 2016-10-28 14:22:50 +09:00
Tsuda Kageyu
711b35cc6e Encourage compilers to optimize out debug() and debugData().
It's sort of like a throwback, but I found that debug(const String &s) {} doesn't prevent a String from being constructed and the error messages from being embedded.
2016-10-28 11:19:51 +09:00
Tsuda Kageyu
d53ca6f736 Update NEWS. 2016-10-27 15:40:14 +09:00
Tsuda Kageyu
aa5f9bb221 Suppress some warnings in test. 2016-10-27 15:23:24 +09:00
Tsuda Kageyu
d2b3547254 Add a test for File::truncate(). 2016-10-27 15:15:22 +09:00
Tsuda Kageyu
06ca9a099d Check if file size <= LONG_MAX. 2016-10-27 15:10:34 +09:00
Tsuda Kageyu
8d873e4e3e Merge pull request #761 from albertofustinoni/master
WinRT compatibility
2016-10-24 22:06:35 +09:00
Alberto Fustinoni
b2fa124451 formatting 2016-10-24 21:24:53 +09:00
Alberto Fustinoni
ff5b2dc96f Whitespace 2016-10-24 21:22:57 +09:00
Alberto Fustinoni
757f5ebc96 Refactoring 2016-10-24 21:19:31 +09:00
Tsuda Kageyu
e36a9cabb9 Update NEWS. 2016-10-24 12:03:23 +09:00
Alberto Fustinoni
606f6c0e74 Better define guards 2016-10-23 23:25:16 +09:00
Alberto Fustinoni
1cc047c953 Styling 2016-10-23 20:17:49 +09:00
Alberto Fustinoni
cae4f1b804 Merge branch 'master' of https://github.com/albertofustinoni/taglib.git 2016-10-23 20:13:26 +09:00
Alberto Fustinoni
deffe83fd0 Use newer file system calls when in Windows 8+ to allow compilation as WinRT asembly 2016-10-23 12:32:16 +09:00
Tsuda Kageyu
597dcde72a Update the version to v1.11.1. 2016-10-22 02:45:52 +09:00
Tsuda Kageyu
6a96a6426a Replace a possibly non-free file in the test suite. 2016-10-22 02:11:16 +09:00
Tsuda Kageyu
69c65284a5 Update NEWS. 2016-10-22 01:06:57 +09:00
Tsuda Kageyu
97aaa0f979 Restore the ABI breakage by bringing back a removed private static variable. 2016-10-19 15:57:28 +09:00
Urs Fleisch
1b64bb0cb7 Support new classical music frames introduced with iTunes 12.5, #758.
M4A:
    ©wrk: Work (string)
    ©mvn: Movement Name (string)
    ©mvi: Movement Number (number)
    ©mvc: Movement Count (number)
    shwm: Show Work & Movement (0/1)

ID3 (2.3, 2.4; MVN, MVI for 2.2):
    MVNM: Movement Name
    MVIN: Movement Number/Count
2016-10-18 20:45:54 +02:00
Tsuda Kageyu
0dac721ce2 Update NEWS. 2016-09-26 17:48:44 +09:00
Stephen F. Booth
bbeeca6fdb Merge pull request #754 from hyperquantum/master
Fix defect in ByteVectorStream::seek when Position==End.
2016-09-15 21:11:08 -04:00
Kevin André
7e90313690 Fix defect in ByteVectorStream::seek when Position==End. 2016-09-15 16:30:16 +02:00
Tsuda Kageyu
1d3c95f692 Merge pull request #752 from evpobr/cmake-ver
Move cmake_minimum_required at the top
2016-09-12 15:56:45 +09:00
Tsuda Kageyu
8c3801f18d Merge pull request #753 from FestusHagen/fh1.m_AddBuildSharedLibsOption
Add BUILD_SHARED_LIBS option for CMake GUI.
2016-09-12 15:56:33 +09:00
Festus Hagen
c9bdd416ef Add BUILD_SHARED_LIBS option for CMake GUI. 2016-08-25 15:03:44 -04:00
evpobr
9f28e037fe Move cmake_minimum_required at the top 2016-08-20 22:37:53 +05:00
Stephen F. Booth
92c070ba9e Merge pull request #749 from jwelton/fix-typo
Fix Typo
2016-08-14 17:10:18 -04:00
Jake Welton
75e3ec73aa Change string to end offset 2016-08-14 22:02:33 +01:00
Tsuda Kageyu
3e47a036fb Update NEWS. 2016-05-14 10:46:42 +09:00
Tsuda Kageyu
9b995544e4 Fix reading table of contents frames with a lot of children. 2016-05-14 09:58:19 +09:00
Tsuda Kageyu
d8e5077961 Update NEWS. 2016-04-29 17:26:33 +09:00
Tsuda Kageyu
6422054540 Merge pull request #739 from frgm/master
c: fix a typo
2016-04-23 06:54:59 +09:00
Svyatoslav Mishyn
1b878102f0 c: fix a typo
readble => readable
2016-04-22 16:46:15 +03:00
Tsuda Kageyu
0a85f9b227 Revert "Hide a private static variable."
This reverts commit 25ffbcb4b9.

# Conflicts:
#	taglib/mpeg/id3v2/id3v2framefactory.cpp
#	taglib/mpeg/id3v2/id3v2framefactory.h
2016-04-22 09:27:04 +09:00
Stephen F. Booth
31f3109b47 Merge pull request #732 from dirkvdb/master
Also ignore the virtual destructor warning when compiling with clang
2016-04-02 13:21:39 -04:00
Dirk Vanden Boer
76f8ff388f Also ignore the virtual destructor warning when compiling with clang 2016-03-20 20:33:04 +01:00
Tsuda Kageyu
7627ae48ed Change some static_casts to dynamic_casts in test_id3v2.cpp.
static_casts are unsafe and some of following tests doesn't work well with them.
2016-03-17 22:50:34 +09:00
Tsuda Kageyu
b2a6768704 Don't stop parsing an ID3v2 SYLT frame when its description is empty. 2016-03-14 20:35:09 +09:00
Tsuda Kageyu
7d270a7e20 Update NEWS. 2016-03-10 21:38:08 +09:00
Tsuda Kageyu
bf53dc6131 Merge pull request #726 from amethystAnt/ape-keys-bug
Fixed bug #725 - changed the maximum length of an APE key to 255
2016-03-07 06:41:21 +09:00
Karel Patlejch
ff8b6a91e7 Fixed bug #725 - changed the maximum length of an APE key to 255 2016-03-06 21:15:18 +01:00
Tsuda Kageyu
1a82419872 Add myself to maintainers. 2016-03-03 10:06:51 +09:00
426 changed files with 35009 additions and 13478 deletions

View File

@@ -12,7 +12,7 @@ insert_final_newline = true
indent_style = space
indent_size = 2
# Trim traling whitespaces
# Trim trailing whitespaces
[*.{h,cpp,tcc,cmake,yml}]
trim_trailing_whitespace = true

6
.github/dependabot.yml vendored Normal file
View File

@@ -0,0 +1,6 @@
version: 2
updates:
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"

88
.github/workflows/build.yml vendored Normal file
View File

@@ -0,0 +1,88 @@
name: Build
on: [push, pull_request, workflow_dispatch]
env:
BUILD_TYPE: Release
jobs:
build:
strategy:
matrix:
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
uses: actions/checkout@v6
- name: Set up Ubuntu
run: sudo apt install -y libcppunit-dev libutfcpp-dev zlib1g-dev
if: matrix.os == 'ubuntu-latest'
- name: Set up macOS
run: |
brew update
brew install cppunit utf8cpp
if: matrix.os == 'macos-latest'
- name: Set up Windows
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
-DBUILD_SHARED_LIBS=ON -DVISIBILITY_HIDDEN=ON
-DBUILD_TESTING=ON -DBUILD_EXAMPLES=ON -DBUILD_BINDINGS=ON
-DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}}
${{ matrix.cmake_extra_args }}
- name: Build
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' && matrix.os != 'windows-2025'
- name: Test Windows
working-directory: ${{github.workspace}}/build
run: |
$env:Path += ";$PWD\taglib\Release;$PWD\bindings\c\Release"
$env:Path += ";$env:VCPKG_INSTALLATION_ROOT\packages\cppunit_x64-windows\bin"
$env:Path += ";$env:VCPKG_INSTALLATION_ROOT\packages\utfcpp_x64-windows\bin"
$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'

12
.gitignore vendored
View File

@@ -3,6 +3,7 @@ cmake_uninstall.cmake
Makefile
CTestTestfile.cmake
CMakeFiles/
CMakeLists.txt.user*
*.so
*.so.*
*.dylib
@@ -12,6 +13,7 @@ CMakeFiles/
*.suo
*.user
.*
!.github/workflows/
*~
/CMakeCache.txt
/Doxyfile
@@ -19,8 +21,9 @@ CMakeFiles/
/taglib.pc
/tests/test_runner
/tests/Testing
/taglib_config.h
/taglib/libtag.a
/taglib-config
/bindings/c/libtag_c.a
/bindings/c/taglib_c.pc
/bindings/c/Debug
/bindings/c/MinSizeRel
@@ -42,5 +45,12 @@ CMakeFiles/
/taglib/tag.dir/Release
/ALL_BUILD.dir
/ZERO_CHECK.dir
taglib.h.stamp
taglib.xcodeproj
CMakeScripts
/.clang-format
/compile_commands.json
/build/
.clangd
.cache
.idea

3
.gitmodules vendored Normal file
View File

@@ -0,0 +1,3 @@
[submodule "3rdparty/utfcpp"]
path = 3rdparty/utfcpp
url = https://github.com/nemtrif/utfcpp.git

View File

@@ -1,28 +0,0 @@
language: cpp
sudo: false
os:
- linux
- osx
compiler:
- gcc
- clang
addons:
apt:
packages:
- libcppunit-dev
- zlib1g-dev
matrix:
exclude:
- os: osx
compiler: gcc
install:
- if [ "$TRAVIS_OS_NAME" = "osx" ]; then brew install cppunit; fi
script: cmake -DBUILD_TESTS=ON -DBUILD_EXAMPLES=ON -DBUILD_BINDINGS=ON . && make && make check

1
3rdparty/utfcpp vendored Submodule

Submodule 3rdparty/utfcpp added at df857efc5b

10
AUTHORS
View File

@@ -2,6 +2,10 @@ Scott Wheeler <wheeler@kde.org>
Author, maintainer
Lukas Lalinsky <lalinsky@gmail.com>
Implementation of multiple new file formats, many bug fixes, maintainer
Tsuda Kageyu <tsuda.kageyu@gmail.com>
A lot of bug fixes and performance improvements, maintainer.
Stephen F. Booth <me@sbooth.org>
DSF metadata implementation, bug fixes, maintainer.
Ismael Orenstein <orenstein@kde.org>
Xing header implementation
Allan Sandfeld Jensen <kde@carewolf.org>
@@ -10,8 +14,10 @@ Teemu Tervo <teemu.tervo@gmx.net>
Numerous bug reports and fixes
Mathias Panzenböck <grosser.meister.morti@gmx.net>
Mod, S3M, IT and XM metadata implementations
Tsuda Kageyu <tsuda.kageyu@gmail.com>
A lot of fixes and improvements, i.e. memory copy reduction etc.
Damien Plisson <damien78@audirvana.com>
DSDIFF metadata implementation
Urs Fleisch <ufleisch@users.sourceforge.net>
Bug fixes, maintainer.
Please send all patches and questions to taglib-devel@kde.org rather than to
individual developers!

563
CHANGELOG.md Normal file
View File

@@ -0,0 +1,563 @@
TagLib 2.2.1 (Mar 7, 2026)
==========================
* Support edition, chapter and attachment UIDs in Matroska simple tags.
* Avoid duplicates in Matroska complex property keys.
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.
* Avoid corrupting invalid Ogg FLAC files without Vorbis comment.
* Windows: Support MP4 files with 64-bit atoms.
* Fix use of property keys with non-ASCII characters in C bindings.
* Fix building with Android NDK 29.
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)
=========================
* Support for Shorten (SHN) files.
* Compile time configuration of supported formats: WITH_APE, WITH_ASF, ...
* Compile time configuration of data and temporary directories for unit tests:
TESTS_DIR and TESTS_TMPDIR.
* C bindings: Added taglib_file_new_wchar() and taglib_file_new_type_wchar().
* Preserve unicode encoding when downgrading to ID3v2.3.
* Do not store FLAC metadata blocks which are too large.
* Fix segfaults with String and ByteVector nullptr arguments.
TagLib 2.0.2 (Aug 24, 2024)
===========================
* Fix parsing of ID3v2.2 frames.
* Tolerate MP4 files with unknown atom types as generated by Android tools.
* Support setting properties with arbitrary names in MP4 tags.
* Windows: Fix "-p" option in tagwriter example.
* Support building with older utfcpp versions.
TagLib 2.0.1 (Apr 9, 2024)
==========================
* Fix aborting when _GLIBCXX_ASSERTIONS are enabled.
* Fall back to utf8cpp header detection in the case that its CMake
configuration is removed.
* Improve compatibility with the SWIG interface compiler.
* Build system fixes for testing without bindings, Emscripten and Illumos.
* C bindings: Fix setting UTF-8 encoded property values.
* Windows: Fix opening long paths.
TagLib 2.0 (Jan 24, 2024)
=========================
* New major version, binary incompatible, but mostly source-compatible
with the latest 1.x release if no deprecated features are used.
Simple applications should build without changes, more complex
applications (e.g. extending classes of TagLib) will have to be adapted.
* Requires a C++17 compiler and uses features of C++17.
* Major code cleanup, fixed warnings issued by compilers and static analyzers.
* Made methods virtual which should have been virtual but could not be
changed to keep binary compatibility, remove related workarounds.
* Removed deprecated functions:
- APE::Item::Item(const String &, const String &)
- APE::Item::toStringList(): Use values()
- APE::Item::value(): Use binaryData()
- ASF::Properties::setLength()
- ByteVector::checksum()
- ByteVector::isNull(): Use isEmpty()
- ByteVector::null
- FLAC::File::setID3v2FrameFactory()
- FLAC::File::streamInfoData()
- FLAC::File::streamLength()
- FLAC::Properties::Properties(File *, ReadStyle)
- FLAC::Properties::sampleWidth(): Use bitsPerSample()
- File::isReadable(): Use system functions
- File::isWritable(): Use system functions
- FileName::str()
- FileRef::create(): Use constructor
- MP4::Tag::itemListMap(): Use itemMap()
- MPC::File::remove(): Use strip()
- MPC::Properties::Properties(const ByteVector &, long, ReadStyle)
- MPEG::File::save(int, ...): Use overload
- MPEG::File::setID3v2FrameFactory(): Use constructor
- MPEG::ID3v2::Frame::Header::Header(const ByteVector &, bool)
- MPEG::ID3v2::Frame::Header::frameAlterPreservation(): Use
fileAlterPreservation()
- MPEG::ID3v2::Frame::Header::setData(const ByteVector &, bool)
- MPEG::ID3v2::Frame::Header::size(unsigned int): Use size()
- MPEG::ID3v2::Frame::Header::unsycronisation(): use unsynchronisation()
- MPEG::ID3v2::Frame::checkEncoding(const StringList &, String::Type): Use
checkTextEncoding(const StringList &, String::Type)
- MPEG::ID3v2::Frame::headerSize(): Use Header::size()
- MPEG::ID3v2::Frame::headerSize(unsigned int): Use
Header::size(unsigned int)
- MPEG::ID3v2::FrameFactory::createFrame(const ByteVector &, bool)
- MPEG::ID3v2::FrameFactory::createFrame(const ByteVector &, unsigned int):
Use createFrame(const ByteVector &, const Header *)
- MPEG::ID3v2::RelativeVolumeFrame::channelType()
- MPEG::ID3v2::RelativeVolumeFrame::peakVolume(): Use peakVolume(ChannelType)
- MPEG::ID3v2::RelativeVolumeFrame::setChannelType()
- MPEG::ID3v2::RelativeVolumeFrame::setPeakVolume(const PeakVolume &): Use
setPeakVolume(const PeakVolume &, ChannelType)
- MPEG::ID3v2::RelativeVolumeFrame::setVolumeAdjustment(float): Use
setVolumeAdjustment(float, ChannelType)
- MPEG::ID3v2::RelativeVolumeFrame::setVolumeAdjustmentIndex(short): Use
setVolumeAdjustmentIndex(short, ChannelType)
- MPEG::ID3v2::RelativeVolumeFrame::volumeAdjustment(): Use
volumeAdjustment(ChannelType)
- MPEG::ID3v2::RelativeVolumeFrame::volumeAdjustmentIndex(): Use
volumeAdjustmentIndex(ChannelType)
- MPEG::ID3v2::Tag::footer()
- MPEG::ID3v2::Tag::render(int): Use render(Version)
- MPEG::XingHeader::xingHeaderOffset()
- Ogg::Page::getCopyWithNewPageSequenceNumber()
- Ogg::XiphComment::removeField(): Use removeFields()
- PropertyMap::unsupportedData(): Returns now const reference, use
addUnsupportedData() to add keys
- RIFF::AIFF::Properties::Properties(const ByteVector &, ReadStyle)
- RIFF::AIFF::Properties::Properties(const ByteVector &, int, ReadStyle)
- RIFF::AIFF::Properties::sampleWidth(): Use bitsPerSample()
- RIFF::WAV::File::save(TagTypes, bool, int): Use
save(TagTypes, StripTags, Version)
- RIFF::WAV::File::tag(): Returns now a TagUnion, use ID3v2Tag() to get an
ID3v2::Tag
- String::isNull(): Use isEmpty()
- String::null
- TrueAudio::File::setID3v2FrameFactory(): Use constructor
- WavPack::Properties::Properties(const ByteVector &, long, ReadStyle)
* Made methods const: Frame::Header::size(), Frame::headerSize(),
MP4::Atom::findall(), MP4::Atoms::find(), MP4::Atoms::path().
* Made classes non-virtual: APE::Footer, APE::Item, ASF::Attribute,
ASF::Picture, MP4::CoverArt, MP4::Item, ID3v2::ExtendedHeader, ID3v2::Footer,
ID3v2::Header, MPEG::Header, MPEG::XingHeader, Ogg::Page, Ogg::PageHeader.
* Removed type definitions in TagLib namespace: wchar, uchar, ushort, uint,
ulong, ulonglong, wstring: Use the standard types.
* Removed include file taglib_config.h and its defines TAGLIB_WITH_ASF,
TAGLIB_WITH_MP4: They were always 1 since version 1.8.
* Behavioral changes:
- The basic tag methods (e.g. genre()) separate multiple values with " / "
instead of " ".
- The stream operator for String uses UTF-8 instead of ISO-8859-1 encoding.
- MP4 property ORIGINALDATE is mapped to "----:com.apple.iTunes:ORIGINALDATE"
instead of "----:com.apple.iTunes:originaldate".
- MP4 property ENCODEDBY is mapped to "©enc" instead of "©too", which is now
mapped to ENCODING.
* Unified interface for complex properties like pictures.
* Simplified the unified properties interface by providing its methods on
FileRef.
* C bindings: Support for properties (taglib_property_...) and complex
properties like cover art (taglib_complex_property_...), memory I/O streams.
* Support for Direct Stream Digital (DSD) stream files (DSF) and interchange
file format (DSDIFF, DFF), ADTS (AAC) files.
* The runtime version can be queried.
* Additional utility functions ByteVector::fromUShort(),
ByteVector::fromULongLong(), ByteVector::toULongLong(),
ByteVector::toULongLong(), List::sort().
* Fixed List::setAutoDelete() affecting implicitly shared copies.
* Build system: Direct support for CMake, find_package(TagLib) exports target
TagLib::tag.
* Build system: Fixed PackageConfig to support both relative and absolute paths.
* Build system: utf8cpp is no longer included, it can be provided via a system
package or a Git submodule.
* ASF: Support additional properties ARTISTWEBPAGE, ENCODING, ENCODINGTIME,
FILEWEBPAGE, INITIALKEY, ORIGINALALBUM, ORIGINALARTIST, ORIGINALFILENAME,
ORIGINALLYRICIST.
* ID3v2: Fixed extensibility of FrameFactory, use it also for WAV and AIFF
files.
* MP4: Support additional properties OWNER, RELEASEDATE.
* MP4: Introduced ItemFactory allowing clients to support new atom types.
* MP4: Detect duration from mvhd atom if not present in mdhd atom.
* MP4: Fixed type of hdvd atom to be integer instead of boolean.
* MP4: Tolerate trailing garbage in M4A files.
* MPC: Fixed content check in presence of an ID3v2 tag.
* MPEG: Do not scan full file for ID3v2 tag when ReadStyle Fast is used.
* RIFF: Support properties ALBUM, ARRANGER, ARTIST, ARTISTWEBPAGE, BPM,
COMMENT, COMPOSER, COPYRIGHT, DATE, DISCSUBTITLE, ENCODEDBY, ENCODING,
ENCODINGTIME, GENRE, ISRC, LABEL, LANGUAGE, LYRICIST, MEDIA, PERFORMER,
RELEASECOUNTRY, REMIXER, TITLE, TRACKNUMBER.
* WAV: Fixed crash with files having the "id3 " chunk as the only valid chunk.
* Windows: Fixed support for files larger than 2GB.
TagLib 1.13.1 (Jul 1, 2023)
===========================
* Fixed parsing of TXXX frames without description.
* Detect MP4 atoms with invalid length or type.
* Do not miss ID3v2 frames when an extended header is present.
* Use property "DISCSUBTITLE" for ID3v2 "TSST" frame.
* Build system improvements: Use absolute path for macOS dylib install name,
support --define-prefix when using pkg-config, fixed minimum required
CppUnit version.
* Code clean up using clang-tidy.
TagLib 1.13 (Oct 27, 2022)
==========================
* Added interface StreamTypeResolver to support streams which cannot be
fopen()'ed, e.g. network files.
* Added MP4::File::strip() to remove meta atom from MP4 file.
* Added Map::value() to look up without creating entry.
* Use property "WORK" instead of "CONTENTGROUP" for ID3v2 "TIT1" frame,
use property "WORK" for ASF "WM/ContentGroupDescription",
use property "COMPILATION" for ID3v2 "TCMP" frame.
* Build system improvements: option WITH_ZLIB, BUILD_TESTING instead of
BUILD_TESTS, GNUInstallDirs, FeatureSummary, tests with BUILD_SHARED_LIBS,
cross compilation with Buildroot, systems without HAVE_GCC_ATOMIC, Clang.
* Fixed heap-buffer-overflows when handling ASF, APE, FLAC, ID3v2, MP4, MPC
tags.
* Fixed detection of invalid file by extension when correct type can be
detected by contents.
* Fixed unnecessary creation of map entries in APE and FLAC tags if looked up
tag does not exist.
* Fixed parsing of MP4 non-full meta atoms.
* Fixed potential ID3v1 false positive in the presence of an APE tag.
* Fixed ID3v2 version handling for frames embedded in CHAP or CTOC frames.
* Fixed parsing of multiple strings with a single BOM in ID3v2.4.0.
* Fixed several smaller issues reported by clang-tidy.
TagLib 1.12 (Feb 16, 2021)
==========================
* Added support for WinRT.
* Added support for Linux on POWER.
* Added support for classical music tags of iTunes 12.5.
* Added support for file descriptor to FileStream.
* Added support for 'cmID', 'purl', 'egid' MP4 atoms.
* Added support for 'GRP1' ID3v2 frame.
* Added support for extensible WAV subformat.
* Enabled FileRef to detect file types based on the stream content.
* Dropped support for Windows 9x and NT 4.0 or older.
* Check for mandatory header objects in ASF files.
* More tolerant handling of RIFF padding, WAV files, broken MPEG streams.
* Improved calculation of Ogg, Opus, Speex, WAV, MP4 bitrates.
* Improved Windows compatibility by storing FLAC picture after comments.
* Fixed numerical genres in ID3v2.3.0 'TCON' frames.
* Fixed consistency of API removing MP4 items when empty values are set.
* Fixed consistency of API preferring COMM frames with no description.
* Fixed OOB read on invalid Ogg FLAC files (CVE-2018-11439).
* Fixed handling of empty MPEG files.
* Fixed parsing MP4 mdhd timescale.
* Fixed reading MP4 atoms with zero length.
* Fixed reading FLAC files with zero-sized seektables.
* Fixed handling of lowercase field names in Vorbis Comments.
* Fixed handling of 'rate' atoms in MP4 files.
* Fixed handling of invalid UTF-8 sequences.
* Fixed possible file corruptions when saving Ogg files.
* Fixed handling of non-audio blocks, sampling rates, DSD audio in WavPack files.
* TableOfContentsFrame::toString() improved.
* UserTextIdentificationFrame::toString() improved.
* Marked FileRef::create() deprecated.
* Marked MPEG::File::save() with boolean parameters deprecated,
provide overloads with enum parameters.
* Several smaller bug fixes and performance improvements.
TagLib 1.11.1 (Oct 24, 2016)
============================
* Fixed binary incompatible change in TagLib::String.
* Fixed reading ID3v2 CTOC frames with a lot of entries.
* Fixed seeking ByteVectorStream from the end.
TagLib 1.11 (Apr 29, 2016)
==========================
1.11:
* Fixed reading APE items with long keys.
* Fixed reading ID3v2 SYLT frames when description is empty.
1.11 BETA 2:
* Better handling of PCM WAV files with a 'fact' chunk.
* Better handling of corrupted APE tags.
* Efficient decoding of unsynchronized ID3v2 frames.
* Fixed text encoding when saving certain frames in ID3v2.3 tags.
* Fixed updating the size of RIFF files when removing chunks.
* Several smaller bug fixes and performance improvements.
1.11 BETA:
* New API for creating FileRef from IOStream.
* Added support for ID3v2 PCST and WFED frames.
* Added support for pictures in XiphComment.
* Added String::clear().
* Added FLAC::File::strip() for removing non-standard tags.
* Added alternative functions to XiphComment::removeField().
* Added BUILD_BINDINGS build option.
* Added ENABLE_CCACHE build option.
* Replaced ENABLE_STATIC build option with BUILD_SHARED_LIBS.
* Better handling of duplicate ID3v2 tags in all kinds of files.
* Better handling of duplicate tag chunks in WAV files.
* Better handling of duplicate tag chunks in AIFF files.
* Better handling of duplicate Vorbis comment blocks in FLAC files.
* Better handling of broken MPEG audio frames.
* Fixed crash when calling File::properties() after strip().
* Fixed crash when parsing certain MPEG files.
* Fixed crash when saving Ogg files.
* Fixed possible file corruptions when saving ASF files.
* Fixed possible file corruptions when saving FLAC files.
* Fixed possible file corruptions when saving MP4 files.
* Fixed possible file corruptions when saving MPEG files.
* Fixed possible file corruptions when saving APE files.
* Fixed possible file corruptions when saving Musepack files.
* Fixed possible file corruptions when saving WavPack files.
* Fixed updating the comment field of Vorbis comments.
* Fixed reading date and time in ID3v2.3 tags.
* Marked ByteVector::null and ByteVector::isNull() deprecated.
* Marked String::null and String::isNull() deprecated.
* Marked XiphComment::removeField() deprecated.
* Marked Ogg::Page::getCopyWithNewPageSequenceNumber() deprecated. It returns null.
* Marked custom integer types deprecated.
* Many smaller bug fixes and performance improvements.
TagLib 1.10 (Nov 11, 2015)
==========================
1.10:
* Added new options to the tagwriter example.
* Fixed self-assignment operator in some types.
* Fixed extraction of MP4 tag keys with an empty list.
1.10 BETA:
* New API for the audio length in milliseconds.
* Added support for ID3v2 ETCO and SYLT frames.
* Added support for album artist in PropertyMap API of MP4 files.
* Added support for embedded frames in ID3v2 CHAP and CTOC frames.
* Added support for AIFF-C files.
* Better handling of duplicate ID3v2 tags in MPEG files.
* Allowed generating taglib.pc on Windows.
* Added ZLIB_SOURCE build option.
* Fixed backwards-incompatible change in TagLib::String when constructing UTF16 strings.
* Fixed crash when parsing certain FLAC files.
* Fixed crash when encoding empty strings.
* Fixed saving of certain XM files on OS X.
* Changed Xiph and APE generic getters to return space-concatenated values.
* Fixed possible file corruptions when removing tags from WAV files.
* Added support for MP4 files with 64-bit atoms in certain 64-bit environments.
* Prevented ID3v2 padding from being too large.
* Fixed crash when parsing corrupted APE files.
* Fixed crash when parsing corrupted WAV files.
* Fixed crash when parsing corrupted Ogg FLAC files.
* Fixed crash when parsing corrupted MPEG files.
* Fixed saving empty tags in WAV files.
* Fixed crash when parsing corrupted Musepack files.
* Fixed possible memory leaks when parsing AIFF and WAV files.
* Fixed crash when parsing corrupted MP4 files.
* Stopped writing empty ID3v2 frames.
* Fixed possible file corruptions when saving WMA files.
* Added TagLib::MP4::Tag::isEmpty().
* Added accessors to manipulate MP4 tags.
* Fixed crash when parsing corrupted WavPack files.
* Fixed seeking MPEG frames.
* Fixed reading FLAC files with zero-sized padding blocks.
* Added support for reading the encoder information of WMA files.
* Added support for reading the codec of WAV files.
* Added support for multi channel WavPack files.
* Added support for reading the nominal bitrate of Ogg Speex files.
* Added support for VBR headers in MPEG files.
* Marked FLAC::File::streamInfoData() deprecated. It returns an empty ByteVector.
* Marked FLAC::File::streamLength() deprecated. It returns zero.
* Fixed possible file corruptions when adding an ID3v1 tag to FLAC files.
* Many smaller bug fixes and performance improvements.
TagLib 1.9.1 (Oct 8, 2013)
==========================
* Fixed binary incompatible change in TagLib::Map and TagLib::List.
* Fixed constructing String from ByteVector.
* Fixed compilation on MSVC with the /Zc:wchar_t- option.
* Fixed detecting of RIFF files with invalid chunk sizes.
* Added TagLib::MP4::Properties::codec().
TagLib 1.9 (Oct 6, 2013)
========================
* 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.
* New ID3v1::Tag methods for working directly with genre numbers.
* New MPEG::File methods for checking which tags are saved in the file.
* Added support for the PropertyMap API to ASF and MP4 files.
* Added MusicBrainz identifiers to the PropertyMap API.
* Allowed reading of MP4 cover art without an explicitly specified format.
* Better parsing of corrupted FLAC files.
* Fixed saving of PropertyMap comments without description into ID3v2 tags.
* Fixed crash when parsing certain XM files.
* Fixed compilation of unit test with clang.
* Better handling of files that can't be open or have read-only permissions.
* Improved atomic reference counting.
* New hookable API for debug messages.
* More complete Windows install instructions.
* Many smaller bug fixes and performance improvements.
TagLib 1.8 (Sep 6, 2012)
========================
1.8:
* Added support for OWNE ID3 frames.
* Changed key validation in the new PropertyMap API.
* ID3v1::Tag::setStringHandler will no longer delete the previous handler,
the caller is responsible for this.
* File objects will also no longer delete the passed IOStream objects. It
should be done in the caller code after the File object is no longer
used.
* Added ID3v2::Tag::setLatin1StringHandler for custom handling of
latin1-encoded text in ID3v2 frames.
* Fixed validation of ID3v2 frame IDs (IDs with '0' were ignored).
1.8 BETA:
* New API for accessing tags by name.
* New abstract I/O stream layer to allow custom I/O handlers.
* Support for writing ID3v2.3 tags.
* Support for various module file formats (MOD, S3M, IT, XM).
* Support for MP4 and ASF is now enabled by default.
* Started using atomic int operations for reference counting.
* Added methods for checking if WMA and MP4 files are DRM-protected.
* Added taglib_free to the C bindings.
* New method to allow removing pictures from FLAC files.
* Support for reading audio properties from ALAC and Musepack SV8 files.
* Added replay-gain information to Musepack audio properties.
* Support for APEv2 binary tags.
* Many AudioProperties subclasses now provide information about the total number of samples.
* Various small bug fixes.
TagLib 1.7.2 (Apr 20, 2012)
===========================
* Fixed division by zero while parsing corrupted MP4 files (CVE-2012-2396).
* Fixed compilation on Haiku.
TagLib 1.7.1 (Mar 17, 2012)
===========================
* Improved parsing of corrupted WMA, RIFF and OGG files.
* Fixed a memory leak in the WMA parser.
* Fixed a memory leak in the FLAC parser.
* Fixed a possible division by zero in the APE parser.
* Added detection of TTA2 files.
* Fixed saving of multiple identically named tags to Vorbis Comments.
TagLib 1.7 (Mar 11, 2011)
=========================
1.7:
* Fixed memory leaks in the FLAC file format parser.
* Fixed bitrate calculation for WAV files.
1.7 RC1:
* Support for reading/writing tags from Monkey's Audio files. (BUG:210404)
* Support for reading/writing embedded pictures from WMA files.
* Support for reading/writing embedded pictures from FLAC files (BUG:218696).
* Implemented APE::Tag::isEmpty() to check for all APE tags, not just the
basic ones.
* Added reading of WAV audio length. (BUG:116033)
* Exposed FLAC MD5 signature of the uncompressed audio stream via
FLAC::Properties::signature(). (BUG:160172)
* Added function ByteVector::toHex() for hex-encoding of byte vectors.
* WavPack reader now tries to get the audio length by finding the final
block, if the header doesn't have the information. (BUG:258016)
* Fixed a memory leak in the ID3v2.2 PIC frame parser. (BUG:257007)
* Fixed writing of RIFF files with even chunk sizes. (BUG:243954)
* Fixed compilation on MSVC 2010.
* Removed support for building using autoconf/automake.
* API docs can be now built using "make docs".
TagLib 1.6.3 (Apr 17, 2010)
===========================
* Fixed definitions of the TAGLIB_WITH_MP4 and TAGLIB_WITH_ASF macros.
* Fixed upgrading of ID3v2.3 genre frame with ID3v1 code 0 (Blues).
* New method `int String::toInt(bool *ok)` which can return whether the
conversion to a number was successful.
* Fixed parsing of incorrectly written lengths in ID3v2 (affects mainly
compressed frames). (BUG:231075)
TagLib 1.6.2 (Apr 9, 2010)
==========================
* Read Vorbis Comments from the first FLAC metadata block, if there are
multiple ones. (BUG:211089)
* Fixed a memory leak in FileRef's OGA format detection.
* Fixed compilation with the Sun Studio compiler. (BUG:215225)
* Handle WM/TrackNumber attributes with DWORD content in WMA files.
(BUG:218526)
* More strict check if something is a valid MP4 file. (BUG:216819)
* Correctly save MP4 int-pair atoms with flags set to 0.
* Fixed compilation of the test runner on Windows.
* Store ASF attributes larger than 64k in the metadata library object.
* Ignore trailing non-data atoms when parsing MP4 covr atoms.
* Don't upgrade ID3v2.2 frame TDA to TDRC. (BUG:228968)
TagLib 1.6.1 (Oct 31, 2009)
===========================
* Better detection of the audio codec of .oga files in FileRef.
* Fixed saving of Vorbis comments to Ogg FLAC files. TagLib tried to
include the Vorbis framing bit, which is only correct for Ogg Vorbis.
* Public symbols now have explicitly set visibility to "default" on GCC.
* Added missing exports for static ID3v1 functions.
* Fixed a typo in taglib_c.pc
* Fixed a failing test on ppc64.
* Support for binary 'covr' atom in MP4 files. TagLib 1.6 treated them
as text atoms, which corrupted them in some cases.
* Fixed ID3v1-style genre to string conversion in MP4 files.
TagLib 1.6 (Sep 13, 2009)
=========================
1.6:
* New CMake option to build a static version - ENABLE_STATIC.
* Added support for disabling dllimport/dllexport on Windows using the
TAGLIB_STATIC macro.
* Support for parsing the obsolete 'gnre' MP4 atom.
* New cpp macros TAGLIB_WITH_MP4 and TAGLIB_WITH_ASF to determine if
TagLib was built with MP4/ASF support.
1.6 RC1:
* Split Ogg packets larger than 64k into multiple pages. (BUG:171957)
* TagLib can now use FLAC padding block. (BUG:107659)
* ID3v2.2 frames are now not incorrectly saved. (BUG:176373)
* Support for ID3v2.2 PIC frames. (BUG:167786)
* Fixed a bug in ByteVectorList::split().
* XiphComment::year() now falls back to YEAR if DATE doesn't exist
and XiphComment::year() falls back to TRACKNUM if TRACKNUMBER doesn't
exist. (BUG:144396)
* Improved ID3v2.3 genre parsing. (BUG:188578)
* Better checking of corrupted ID3v2 APIC data. (BUG:168382)
* Bitrate calculating using the Xing header now uses floating point
numbers. (BUG:172556)
* New TagLib::String method rfind().
* Added support for MP4 file format with iTunes-style metadata [optional].
* Added support for ASF (WMA) file format [optional].
* Fixed crash when saving a Locator APEv2 tag. (BUG:169810)
* Fixed a possible crash in the non-const version of String::operator[]
and in String::operator+=. (BUG:169389)
* Added support for PRIV ID3v2 frames.
* Empty ID3v2 genres are no longer treated as numeric ID3v1 genres.
* Added support for the POPM (rating/play count) ID3v2 frame.
* Generic RIFF file format support:
* Support for AIFF files with ID3v2 tags.
* Support for WAV files with ID3v2 tags.
* Fixed crash on handling unsupported ID3v2 frames, e.g. on encrypted
frames. (BUG:161721)
* Fixed overflow while calculating bitrate of FLAC files with a very
high bitrate.

View File

@@ -1,19 +1,23 @@
cmake_minimum_required(VERSION 3.10...3.31)
project(taglib)
cmake_minimum_required(VERSION 2.8.0 FATAL_ERROR)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules")
if(NOT ${CMAKE_VERSION} VERSION_LESS 2.8.12)
cmake_policy(SET CMP0022 OLD)
endif()
include(CTest)
include(FeatureSummary)
include(GNUInstallDirs)
include(CMakePackageConfigHelpers)
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules")
if(DEFINED ENABLE_STATIC)
message(FATAL_ERROR "This option is no longer available, use BUILD_SHARED_LIBS instead")
endif()
if(NOT BUILD_SHARED_LIBS)
add_definitions(-DTAGLIB_STATIC)
option(BUILD_SHARED_LIBS "Build shared libraries" OFF)
if(APPLE)
option(BUILD_FRAMEWORK "Build a macOS framework" OFF)
if(BUILD_FRAMEWORK)
set(BUILD_SHARED_LIBS ON)
#set(CMAKE_MACOSX_RPATH 1)
set(FRAMEWORK_INSTALL_DIR "/Library/Frameworks" CACHE STRING "Directory to install frameworks to.")
endif()
endif()
option(ENABLE_STATIC_RUNTIME "Visual Studio, link with runtime statically" OFF)
@@ -27,40 +31,39 @@ if(ENABLE_CCACHE)
endif()
option(VISIBILITY_HIDDEN "Build with -fvisibility=hidden" OFF)
if(VISIBILITY_HIDDEN)
add_definitions(-fvisibility=hidden)
endif()
option(BUILD_TESTS "Build the test suite" OFF)
option(BUILD_EXAMPLES "Build the examples" OFF)
option(BUILD_BINDINGS "Build the bindings" ON)
option(NO_ITUNES_HACKS "Disable workarounds for iTunes bugs" OFF)
add_definitions(-DHAVE_CONFIG_H)
set(TESTS_DIR "${CMAKE_CURRENT_SOURCE_DIR}/tests/")
## the following are directories where stuff will be installed to
set(LIB_SUFFIX "" CACHE STRING "Define suffix of directory name (32/64)")
set(EXEC_INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}" CACHE PATH "Base directory for executables and libraries")
set(BIN_INSTALL_DIR "${EXEC_INSTALL_PREFIX}/bin" CACHE PATH "The subdirectory to the binaries prefix (default prefix/bin)")
set(LIB_INSTALL_DIR "${EXEC_INSTALL_PREFIX}/lib${LIB_SUFFIX}" CACHE PATH "The subdirectory relative to the install prefix where libraries will be installed (default is /lib${LIB_SUFFIX})")
set(INCLUDE_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/include" CACHE PATH "The subdirectory to the header prefix")
if(APPLE)
option(BUILD_FRAMEWORK "Build an OS X framework" OFF)
set(FRAMEWORK_INSTALL_DIR "/Library/Frameworks" CACHE STRING "Directory to install frameworks to.")
option(PLATFORM_WINRT "Enable WinRT support" OFF)
if(PLATFORM_WINRT)
add_definitions(-DPLATFORM_WINRT)
endif()
if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX)
set(TAGLIB_INSTALL_SUFFIX "" CACHE STRING
"Suffix added to installed files (include directory, libraries, .pc)")
add_definitions(-DHAVE_CONFIG_H)
set(TESTS_DIR "${CMAKE_CURRENT_SOURCE_DIR}/tests/" CACHE STRING
"Tests directory, is path to unit test data when 'data' is appended")
set(TESTS_TMPDIR "" CACHE STRING
"Directory for temporary files created during unit tests, system tmpdir is used if undefined")
if(CMAKE_C_COMPILER_ID MATCHES "^(GNU|Clang|AppleClang)$")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall")
endif()
if(CMAKE_CXX_COMPILER_ID MATCHES "^(GNU|Clang|AppleClang)$")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall")
endif()
if(MSVC AND ENABLE_STATIC_RUNTIME)
foreach(flag_var CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE CMAKE_CXX_FLAGS_MINSIZEREL CMAKE_CXX_FLAGS_RELWITHDEBINFO)
string(REGEX REPLACE "/MD" "/MT" ${flag_var} "${${flag_var}}")
endforeach(flag_var)
if(MSVC)
if(ENABLE_STATIC_RUNTIME)
foreach(flag_var CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE CMAKE_CXX_FLAGS_MINSIZEREL CMAKE_CXX_FLAGS_RELWITHDEBINFO)
string(REGEX REPLACE "/MD" "/MT" ${flag_var} "${${flag_var}}")
endforeach(flag_var)
endif()
endif()
# Read version information from file taglib/toolkit/taglib.h into variables
@@ -85,53 +88,136 @@ else()
set(TAGLIB_LIB_VERSION_STRING "${TAGLIB_LIB_MAJOR_VERSION}.${TAGLIB_LIB_MINOR_VERSION}.${TAGLIB_LIB_PATCH_VERSION}")
endif()
# 1. If the library source code has changed at all since the last update, then increment revision.
# 2. If any interfaces have been added, removed, or changed since the last update, increment current, and set revision to 0.
# 3. If any interfaces have been added since the last public release, then increment age.
# 4. If any interfaces have been removed since the last public release, then set age to 0.
set(TAGLIB_SOVERSION_CURRENT 17)
set(TAGLIB_SOVERSION_REVISION 0)
set(TAGLIB_SOVERSION_AGE 16)
math(EXPR TAGLIB_SOVERSION_MAJOR "${TAGLIB_SOVERSION_CURRENT} - ${TAGLIB_SOVERSION_AGE}")
math(EXPR TAGLIB_SOVERSION_MINOR "${TAGLIB_SOVERSION_AGE}")
math(EXPR TAGLIB_SOVERSION_PATCH "${TAGLIB_SOVERSION_REVISION}")
# Major version: increase it if you break ABI compatibility.
# Minor version: increase it if you add ABI compatible features.
# Patch version: increase it for bug fix releases.
set(TAGLIB_SOVERSION_MAJOR 2)
set(TAGLIB_SOVERSION_MINOR 2)
set(TAGLIB_SOVERSION_PATCH 1)
include(ConfigureChecks.cmake)
option(WITH_APE "Build with APE, MPC, WavPack" ON)
option(WITH_ASF "Build with ASF" ON)
option(WITH_DSF "Build with DSF" ON)
option(WITH_MATROSKA "Build with Matroska" ON)
option(WITH_MOD "Build with Tracker modules" ON)
option(WITH_MP4 "Build with MP4" ON)
option(WITH_RIFF "Build with AIFF, RIFF, WAV" ON)
option(WITH_SHORTEN "Build with Shorten" ON)
option(WITH_TRUEAUDIO "Build with TrueAudio" ON)
option(WITH_VORBIS "Build with Vorbis, FLAC, Ogg, Opus" ON)
# Determine whether zlib is installed.
option(WITH_ZLIB "Build with ZLIB" ON)
if(WITH_ZLIB)
find_package("ZLIB")
set(HAVE_ZLIB ${ZLIB_FOUND})
if(ZLIB_FOUND)
set(ZLIB_LIBRARIES_FLAGS -lz)
if(NOT BUILD_SHARED_LIBS)
# When linking TagLib statically, zlib has to be linked explicitly.
set(ZLIB_INTERFACE_LINK_LIBRARIES ZLIB::ZLIB)
endif()
endif()
endif()
if(NOT WIN32)
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/taglib-config.cmake" "${CMAKE_CURRENT_BINARY_DIR}/taglib-config")
install(PROGRAMS "${CMAKE_CURRENT_BINARY_DIR}/taglib-config" DESTINATION "${BIN_INSTALL_DIR}")
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/taglib-config.cmake" "${CMAKE_CURRENT_BINARY_DIR}/taglib-config" @ONLY)
install(PROGRAMS "${CMAKE_CURRENT_BINARY_DIR}/taglib-config" DESTINATION "${CMAKE_INSTALL_BINDIR}"
RENAME "taglib${TAGLIB_INSTALL_SUFFIX}-config")
endif()
if(WIN32)
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/taglib-config.cmd.cmake" "${CMAKE_CURRENT_BINARY_DIR}/taglib-config.cmd")
install(PROGRAMS "${CMAKE_CURRENT_BINARY_DIR}/taglib-config.cmd" DESTINATION "${BIN_INSTALL_DIR}")
install(PROGRAMS "${CMAKE_CURRENT_BINARY_DIR}/taglib-config.cmd" DESTINATION "${CMAKE_INSTALL_BINDIR}"
RENAME "taglib${TAGLIB_INSTALL_SUFFIX}-config.cmd")
endif()
if(NOT BUILD_FRAMEWORK)
if(IS_ABSOLUTE ${CMAKE_INSTALL_INCLUDEDIR})
set(CMAKE_PC_INCLUDEDIR ${CMAKE_INSTALL_INCLUDEDIR})
else()
set(CMAKE_PC_INCLUDEDIR "\${prefix}/${CMAKE_INSTALL_INCLUDEDIR}")
endif()
if(IS_ABSOLUTE ${CMAKE_INSTALL_LIBDIR})
set(CMAKE_PC_LIBDIR ${CMAKE_INSTALL_LIBDIR})
else()
set(CMAKE_PC_LIBDIR "\${prefix}/${CMAKE_INSTALL_LIBDIR}")
endif()
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/taglib.pc.cmake" "${CMAKE_CURRENT_BINARY_DIR}/taglib.pc" @ONLY)
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/taglib.pc" DESTINATION "${LIB_INSTALL_DIR}/pkgconfig")
endif()
if(NOT HAVE_ZLIB AND ZLIB_SOURCE)
set(HAVE_ZLIB 1)
set(HAVE_ZLIB_SOURCE 1)
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/taglib.pc" DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig"
RENAME "taglib${TAGLIB_INSTALL_SUFFIX}.pc")
endif()
include_directories(${CMAKE_CURRENT_BINARY_DIR})
configure_file(config.h.cmake "${CMAKE_CURRENT_BINARY_DIR}/config.h")
option(TRACE_IN_RELEASE "Output debug messages even in release mode" OFF)
if(TRACE_IN_RELEASE)
set(TRACE_IN_RELEASE TRUE)
endif()
find_package(utf8cpp QUIET)
if(utf8cpp_FOUND)
message(STATUS "Using utfcpp ${utf8cpp_VERSION} from ${utf8cpp_CONFIG}")
else()
find_path(utf8cpp_INCLUDE_DIR NAMES utf8.h PATH_SUFFIXES utf8cpp
DOC "utf8cpp include directory")
mark_as_advanced(utf8cpp_INCLUDE_DIR)
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(utf8cpp REQUIRED_VARS utf8cpp_INCLUDE_DIR)
if(utf8cpp_FOUND)
set(utf8cpp_INCLUDE_DIRS "${utf8cpp_INCLUDE_DIR}")
if(NOT TARGET utf8::cpp)
add_library(utf8::cpp INTERFACE IMPORTED)
set_target_properties(utf8::cpp PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES "${utf8cpp_INCLUDE_DIR}")
endif()
message(STATUS "Using utfcpp from ${utf8cpp_INCLUDE_DIR}")
else()
if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/utfcpp/CMakeLists.txt)
add_subdirectory("3rdparty/utfcpp")
message(STATUS "Using utfcpp from ${utf8cpp_SOURCE_DIR}")
else()
message(FATAL_ERROR
"utfcpp not found. Either install package (probably utfcpp, utf8cpp, or libutfcpp-dev) "
"or fetch the git submodule using\n"
"git submodule update --init")
endif()
endif()
endif()
if(WITH_APE)
set(TAGLIB_WITH_APE TRUE)
endif()
if(WITH_ASF)
set(TAGLIB_WITH_ASF TRUE)
endif()
if(WITH_DSF)
set(TAGLIB_WITH_DSF TRUE)
endif()
if(WITH_MATROSKA)
set(TAGLIB_WITH_MATROSKA TRUE)
endif()
if(WITH_MOD)
set(TAGLIB_WITH_MOD TRUE)
endif()
if(WITH_MP4)
set(TAGLIB_WITH_MP4 TRUE)
endif()
option(TRACE_IN_RELEASE "Output debug messages even in release mode" OFF)
if(TRACE_IN_RELEASE)
set(TRACE_IN_RELEASE TRUE)
if(WITH_RIFF)
set(TAGLIB_WITH_RIFF TRUE)
endif()
if(WITH_SHORTEN)
set(TAGLIB_WITH_SHORTEN TRUE)
endif()
if(WITH_TRUEAUDIO)
set(TAGLIB_WITH_TRUEAUDIO TRUE)
endif()
if(WITH_VORBIS)
set(TAGLIB_WITH_VORBIS TRUE)
endif()
configure_file(taglib/taglib_config.h.cmake "${CMAKE_CURRENT_BINARY_DIR}/taglib_config.h")
@@ -142,9 +228,13 @@ if(BUILD_BINDINGS)
add_subdirectory(bindings)
endif()
if(BUILD_TESTS AND NOT BUILD_SHARED_LIBS)
enable_testing()
add_subdirectory(tests)
if(BUILD_TESTING)
find_package(CppUnit)
if(CppUnit_FOUND)
add_subdirectory(tests)
else()
message(WARNING "BUILD_TESTING requested, but CppUnit not found, skipping tests.")
endif()
endif()
if(BUILD_EXAMPLES)
@@ -152,7 +242,6 @@ if(BUILD_EXAMPLES)
endif()
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/Doxyfile.cmake" "${CMAKE_CURRENT_BINARY_DIR}/Doxyfile")
file(COPY doc/taglib.png DESTINATION doc)
add_custom_target(docs doxygen)
# uninstall target
@@ -161,3 +250,5 @@ configure_file("${CMAKE_CURRENT_SOURCE_DIR}/cmake_uninstall.cmake.in" "${CMAKE_C
if(NOT TARGET uninstall)
add_custom_target(uninstall COMMAND "${CMAKE_COMMAND}" -P "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake")
endif()
feature_summary(WHAT ALL FATAL_ON_MISSING_REQUIRED_PACKAGES)

View File

@@ -20,7 +20,7 @@ if(NOT ${SIZEOF_LONGLONG} EQUAL 8)
endif()
check_type_size("wchar_t" SIZEOF_WCHAR_T)
if(${SIZEOF_WCHAR_T} LESS 2)
if(NOT ${SIZEOF_WCHAR_T} GREATER 1)
message(FATAL_ERROR "TagLib requires that wchar_t is sufficient to store a UTF-16 char.")
endif()
@@ -34,178 +34,65 @@ if(NOT ${SIZEOF_DOUBLE} EQUAL 8)
message(FATAL_ERROR "TagLib requires that double is 64-bit wide.")
endif()
# Enable check_cxx_source_compiles() to work with Boost "header-only" libraries.
find_package(Boost)
if(Boost_FOUND)
set(CMAKE_REQUIRED_INCLUDES "${CMAKE_REQUIRED_INCLUDES};${Boost_INCLUDE_DIRS}")
endif()
# Determine which kind of atomic operations your compiler supports.
check_cxx_source_compiles("
#include <atomic>
int main() {
std::atomic<unsigned int> x;
x.fetch_add(1);
x.fetch_sub(1);
return 0;
}
" HAVE_STD_ATOMIC)
if(NOT HAVE_STD_ATOMIC)
find_package(Boost COMPONENTS atomic)
if(Boost_ATOMIC_FOUND)
set(HAVE_BOOST_ATOMIC 1)
else()
set(HAVE_BOOST_ATOMIC 0)
endif()
if(NOT HAVE_BOOST_ATOMIC)
check_cxx_source_compiles("
int main() {
volatile int x;
__sync_add_and_fetch(&x, 1);
int y = __sync_sub_and_fetch(&x, 1);
return 0;
}
" HAVE_GCC_ATOMIC)
if(NOT HAVE_GCC_ATOMIC)
check_cxx_source_compiles("
#include <libkern/OSAtomic.h>
int main() {
volatile int32_t x;
OSAtomicIncrement32Barrier(&x);
int32_t y = OSAtomicDecrement32Barrier(&x);
return 0;
}
" HAVE_MAC_ATOMIC)
if(NOT HAVE_MAC_ATOMIC)
check_cxx_source_compiles("
#include <windows.h>
int main() {
volatile LONG x;
InterlockedIncrement(&x);
LONG y = InterlockedDecrement(&x);
return 0;
}
" HAVE_WIN_ATOMIC)
if(NOT HAVE_WIN_ATOMIC)
check_cxx_source_compiles("
#include <ia64intrin.h>
int main() {
volatile int x;
__sync_add_and_fetch(&x, 1);
int y = __sync_sub_and_fetch(&x, 1);
return 0;
}
" HAVE_IA64_ATOMIC)
endif()
endif()
endif()
endif()
endif()
# Determine which kind of byte swap functions your compiler supports.
check_cxx_source_compiles("
#include <boost/endian/conversion.hpp>
int main() {
boost::endian::endian_reverse(static_cast<uint16_t>(1));
boost::endian::endian_reverse(static_cast<uint32_t>(1));
boost::endian::endian_reverse(static_cast<uint64_t>(1));
__builtin_bswap16(0);
__builtin_bswap32(0);
__builtin_bswap64(0);
return 0;
}
" HAVE_BOOST_BYTESWAP)
" HAVE_GCC_BYTESWAP)
if(NOT HAVE_BOOST_BYTESWAP)
if(NOT HAVE_GCC_BYTESWAP)
check_cxx_source_compiles("
#include <byteswap.h>
int main() {
__builtin_bswap16(0);
__builtin_bswap32(0);
__builtin_bswap64(0);
__bswap_16(0);
__bswap_32(0);
__bswap_64(0);
return 0;
}
" HAVE_GCC_BYTESWAP)
" HAVE_GLIBC_BYTESWAP)
if(NOT HAVE_GCC_BYTESWAP)
if(NOT HAVE_GLIBC_BYTESWAP)
check_cxx_source_compiles("
#include <byteswap.h>
#include <cstdlib>
int main() {
__bswap_16(0);
__bswap_32(0);
__bswap_64(0);
_byteswap_ushort(0);
_byteswap_ulong(0);
_byteswap_uint64(0);
return 0;
}
" HAVE_GLIBC_BYTESWAP)
" HAVE_MSC_BYTESWAP)
if(NOT HAVE_GLIBC_BYTESWAP)
if(NOT HAVE_MSC_BYTESWAP)
check_cxx_source_compiles("
#include <stdlib.h>
#include <libkern/OSByteOrder.h>
int main() {
_byteswap_ushort(0);
_byteswap_ulong(0);
_byteswap_uint64(0);
OSSwapInt16(0);
OSSwapInt32(0);
OSSwapInt64(0);
return 0;
}
" HAVE_MSC_BYTESWAP)
" HAVE_MAC_BYTESWAP)
if(NOT HAVE_MSC_BYTESWAP)
if(NOT HAVE_MAC_BYTESWAP)
check_cxx_source_compiles("
#include <libkern/OSByteOrder.h>
#include <sys/endian.h>
int main() {
OSSwapInt16(0);
OSSwapInt32(0);
OSSwapInt64(0);
swap16(0);
swap32(0);
swap64(0);
return 0;
}
" HAVE_MAC_BYTESWAP)
if(NOT HAVE_MAC_BYTESWAP)
check_cxx_source_compiles("
#include <sys/endian.h>
int main() {
swap16(0);
swap32(0);
swap64(0);
return 0;
}
" HAVE_OPENBSD_BYTESWAP)
endif()
" HAVE_OPENBSD_BYTESWAP)
endif()
endif()
endif()
endif()
# Determine whether your compiler supports some safer version of vsprintf.
check_cxx_source_compiles("
#include <cstdio>
#include <cstdarg>
int main() {
char buf[20];
va_list args;
vsnprintf(buf, 20, \"%d\", args);
return 0;
}
" HAVE_VSNPRINTF)
if(NOT HAVE_VSNPRINTF)
check_cxx_source_compiles("
#include <cstdio>
#include <cstdarg>
int main() {
char buf[20];
va_list args;
vsprintf_s(buf, \"%d\", args);
return 0;
}
" HAVE_VSPRINTF_S)
endif()
# Determine whether your compiler supports ISO _strdup.
check_cxx_source_compiles("
@@ -216,33 +103,7 @@ check_cxx_source_compiles("
}
" HAVE_ISO_STRDUP)
# Determine whether zlib is installed.
if(NOT ZLIB_SOURCE)
find_package(ZLIB)
if(ZLIB_FOUND)
set(HAVE_ZLIB 1)
else()
set(HAVE_ZLIB 0)
endif()
if(NOT HAVE_ZLIB)
find_package(Boost COMPONENTS iostreams zlib)
if(Boost_IOSTREAMS_FOUND AND Boost_ZLIB_FOUND)
set(HAVE_BOOST_ZLIB 1)
else()
set(HAVE_BOOST_ZLIB 0)
endif()
endif()
# Detect WinRT mode
if(CMAKE_SYSTEM_NAME STREQUAL "WindowsStore")
set(PLATFORM_WINRT 1)
endif()
# Determine whether CppUnit is installed.
if(BUILD_TESTS AND NOT BUILD_SHARED_LIBS)
find_package(CppUnit)
if(NOT CppUnit_FOUND)
message(STATUS "CppUnit not found, disabling tests.")
set(BUILD_TESTS OFF)
endif()
endif()

View File

@@ -1,37 +1,78 @@
# Doxyfile 1.3.4
# Doxyfile 1.9.1
#---------------------------------------------------------------------------
# Project related configuration options
#---------------------------------------------------------------------------
DOXYFILE_ENCODING = UTF-8
PROJECT_NAME = TagLib
PROJECT_NUMBER = ${TAGLIB_LIB_VERSION_STRING}
PROJECT_BRIEF =
PROJECT_LOGO = @CMAKE_SOURCE_DIR@/doc/taglib.svg
OUTPUT_DIRECTORY = doc
CREATE_SUBDIRS = NO
ALLOW_UNICODE_NAMES = NO
OUTPUT_LANGUAGE = English
USE_WINDOWS_ENCODING = NO
BRIEF_MEMBER_DESC = YES
REPEAT_BRIEF = YES
ABBREVIATE_BRIEF = "The $name class" \
"The $name widget" \
"The $name file" \
is \
provides \
specifies \
contains \
represents \
a \
an \
the
ALWAYS_DETAILED_SEC = NO
INLINE_INHERITED_MEMB = NO
FULL_PATH_NAMES = NO
STRIP_FROM_PATH =
STRIP_FROM_PATH =
STRIP_FROM_INC_PATH =
SHORT_NAMES = NO
JAVADOC_AUTOBRIEF = NO
JAVADOC_BANNER = NO
QT_AUTOBRIEF = NO
MULTILINE_CPP_IS_BRIEF = NO
DETAILS_AT_TOP = NO
PYTHON_DOCSTRING = YES
INHERIT_DOCS = YES
DISTRIBUTE_GROUP_DOC = NO
SEPARATE_MEMBER_PAGES = NO
TAB_SIZE = 4
ALIASES =
ALIASES =
OPTIMIZE_OUTPUT_FOR_C = NO
OPTIMIZE_OUTPUT_JAVA = NO
OPTIMIZE_FOR_FORTRAN = NO
OPTIMIZE_OUTPUT_VHDL = NO
OPTIMIZE_OUTPUT_SLICE = NO
EXTENSION_MAPPING =
MARKDOWN_SUPPORT = YES
TOC_INCLUDE_HEADINGS = 5
AUTOLINK_SUPPORT = YES
BUILTIN_STL_SUPPORT = NO
CPP_CLI_SUPPORT = NO
SIP_SUPPORT = NO
IDL_PROPERTY_SUPPORT = YES
DISTRIBUTE_GROUP_DOC = NO
GROUP_NESTED_COMPOUNDS = NO
SUBGROUPING = YES
INLINE_GROUPED_CLASSES = NO
INLINE_SIMPLE_STRUCTS = NO
TYPEDEF_HIDES_STRUCT = NO
LOOKUP_CACHE_SIZE = 0
NUM_PROC_THREADS = 1
#---------------------------------------------------------------------------
# Build related configuration options
#---------------------------------------------------------------------------
EXTRACT_ALL = YES
EXTRACT_PRIVATE = NO
EXTRACT_PRIV_VIRTUAL = NO
EXTRACT_PACKAGE = NO
EXTRACT_STATIC = NO
EXTRACT_LOCAL_CLASSES = NO
EXTRACT_LOCAL_METHODS = NO
EXTRACT_ANON_NSPACES = NO
RESOLVE_UNNAMED_PARAMS = YES
HIDE_UNDOC_MEMBERS = NO
HIDE_UNDOC_CLASSES = NO
HIDE_FRIEND_COMPOUNDS = NO
@@ -39,172 +80,264 @@ HIDE_IN_BODY_DOCS = NO
INTERNAL_DOCS = YES
CASE_SENSE_NAMES = YES
HIDE_SCOPE_NAMES = NO
HIDE_COMPOUND_REFERENCE= NO
SHOW_INCLUDE_FILES = YES
SHOW_GROUPED_MEMB_INC = NO
FORCE_LOCAL_INCLUDES = NO
INLINE_INFO = YES
SORT_MEMBER_DOCS = YES
SORT_BRIEF_DOCS = NO
SORT_MEMBERS_CTORS_1ST = NO
SORT_GROUP_NAMES = NO
SORT_BY_SCOPE_NAME = NO
STRICT_PROTO_MATCHING = NO
GENERATE_TODOLIST = NO
GENERATE_TESTLIST = NO
GENERATE_BUGLIST = NO
GENERATE_DEPRECATEDLIST= NO
ENABLED_SECTIONS =
GENERATE_DEPRECATEDLIST= YES
ENABLED_SECTIONS =
MAX_INITIALIZER_LINES = 30
SHOW_USED_FILES = YES
SHOW_FILES = YES
SHOW_NAMESPACES = YES
FILE_VERSION_FILTER =
LAYOUT_FILE =
CITE_BIB_FILES =
#---------------------------------------------------------------------------
# configuration options related to warning and progress messages
# Configuration options related to warning and progress messages
#---------------------------------------------------------------------------
QUIET = NO
WARNINGS = YES
WARN_IF_UNDOCUMENTED = NO
WARN_IF_DOC_ERROR = YES
WARN_NO_PARAMDOC = NO
WARN_AS_ERROR = NO
WARN_FORMAT = "$file:$line: $text"
WARN_LOGFILE =
WARN_LOGFILE =
#---------------------------------------------------------------------------
# configuration options related to the input files
# Configuration options related to the input files
#---------------------------------------------------------------------------
INPUT = @CMAKE_SOURCE_DIR@/taglib
INPUT_ENCODING = UTF-8
FILE_PATTERNS = *.h \
*.hh \
*.H
*.H \
*.dox
RECURSIVE = YES
EXCLUDE =
EXCLUDE =
EXCLUDE_SYMLINKS = NO
EXCLUDE_PATTERNS =
EXAMPLE_PATH =
EXAMPLE_PATTERNS =
EXCLUDE_PATTERNS =
EXCLUDE_SYMBOLS =
EXAMPLE_PATH =
EXAMPLE_PATTERNS = *
EXAMPLE_RECURSIVE = NO
IMAGE_PATH =
INPUT_FILTER =
IMAGE_PATH =
INPUT_FILTER =
FILTER_PATTERNS =
FILTER_SOURCE_FILES = NO
FILTER_SOURCE_PATTERNS =
USE_MDFILE_AS_MAINPAGE =
#---------------------------------------------------------------------------
# configuration options related to source browsing
# Configuration options related to source browsing
#---------------------------------------------------------------------------
SOURCE_BROWSER = NO
INLINE_SOURCES = NO
STRIP_CODE_COMMENTS = YES
REFERENCED_BY_RELATION = YES
REFERENCES_RELATION = YES
REFERENCES_LINK_SOURCE = YES
SOURCE_TOOLTIPS = YES
USE_HTAGS = NO
VERBATIM_HEADERS = YES
CLANG_ASSISTED_PARSING = NO
CLANG_ADD_INC_PATHS = YES
CLANG_OPTIONS =
CLANG_DATABASE_PATH =
#---------------------------------------------------------------------------
# configuration options related to the alphabetical class index
# Configuration options related to the alphabetical class index
#---------------------------------------------------------------------------
ALPHABETICAL_INDEX = YES
COLS_IN_ALPHA_INDEX = 5
IGNORE_PREFIX =
IGNORE_PREFIX =
#---------------------------------------------------------------------------
# configuration options related to the HTML output
# Configuration options related to the HTML output
#---------------------------------------------------------------------------
GENERATE_HTML = YES
HTML_OUTPUT = html
HTML_FILE_EXTENSION = .html
HTML_HEADER = @CMAKE_SOURCE_DIR@/doc/api-header.html
HTML_FOOTER = @CMAKE_SOURCE_DIR@/doc/api-footer.html
HTML_STYLESHEET = @CMAKE_SOURCE_DIR@/doc/taglib-api.css
HTML_ALIGN_MEMBERS = YES
HTML_STYLESHEET =
HTML_EXTRA_STYLESHEET =
HTML_EXTRA_FILES =
HTML_COLORSTYLE_HUE = 220
HTML_COLORSTYLE_SAT = 100
HTML_COLORSTYLE_GAMMA = 80
HTML_TIMESTAMP = NO
HTML_DYNAMIC_MENUS = YES
HTML_DYNAMIC_SECTIONS = NO
HTML_INDEX_NUM_ENTRIES = 100
GENERATE_DOCSET = NO
DOCSET_FEEDNAME = "Doxygen generated docs"
DOCSET_BUNDLE_ID = org.doxygen.Project
DOCSET_PUBLISHER_ID = org.doxygen.Publisher
DOCSET_PUBLISHER_NAME = Publisher
GENERATE_HTMLHELP = NO
CHM_FILE =
HHC_LOCATION =
CHM_FILE =
HHC_LOCATION =
GENERATE_CHI = NO
CHM_INDEX_ENCODING =
BINARY_TOC = NO
TOC_EXPAND = NO
DISABLE_INDEX = YES
ENUM_VALUES_PER_LINE = 4
GENERATE_QHP = NO
QCH_FILE =
QHP_NAMESPACE = org.doxygen.Project
QHP_VIRTUAL_FOLDER = doc
QHP_CUST_FILTER_NAME =
QHP_CUST_FILTER_ATTRS =
QHP_SECT_FILTER_ATTRS =
QHG_LOCATION =
GENERATE_ECLIPSEHELP = NO
ECLIPSE_DOC_ID = org.doxygen.Project
DISABLE_INDEX = NO
GENERATE_TREEVIEW = NO
ENUM_VALUES_PER_LINE = 4
TREEVIEW_WIDTH = 250
EXT_LINKS_IN_WINDOW = NO
HTML_FORMULA_FORMAT = png
FORMULA_FONTSIZE = 10
FORMULA_TRANSPARENT = YES
FORMULA_MACROFILE =
USE_MATHJAX = NO
MATHJAX_FORMAT = HTML-CSS
MATHJAX_RELPATH = https://cdn.jsdelivr.net/npm/mathjax@2
MATHJAX_EXTENSIONS =
MATHJAX_CODEFILE =
SEARCHENGINE = NO
SERVER_BASED_SEARCH = NO
EXTERNAL_SEARCH = NO
SEARCHENGINE_URL =
SEARCHDATA_FILE = searchdata.xml
EXTERNAL_SEARCH_ID =
EXTRA_SEARCH_MAPPINGS =
#---------------------------------------------------------------------------
# configuration options related to the LaTeX output
# Configuration options related to the LaTeX output
#---------------------------------------------------------------------------
GENERATE_LATEX = NO
LATEX_OUTPUT = latex
LATEX_CMD_NAME = latex
MAKEINDEX_CMD_NAME = makeindex
LATEX_MAKEINDEX_CMD = makeindex
COMPACT_LATEX = NO
PAPER_TYPE = letter
EXTRA_PACKAGES =
LATEX_HEADER =
EXTRA_PACKAGES =
LATEX_HEADER =
LATEX_FOOTER =
LATEX_EXTRA_STYLESHEET =
LATEX_EXTRA_FILES =
PDF_HYPERLINKS = YES
USE_PDFLATEX = YES
LATEX_BATCHMODE = NO
LATEX_HIDE_INDICES = NO
LATEX_BIB_STYLE = plain
LATEX_TIMESTAMP = NO
LATEX_EMOJI_DIRECTORY =
#---------------------------------------------------------------------------
# configuration options related to the RTF output
# Configuration options related to the RTF output
#---------------------------------------------------------------------------
GENERATE_RTF = NO
RTF_OUTPUT = rtf
COMPACT_RTF = NO
RTF_HYPERLINKS = NO
RTF_STYLESHEET_FILE =
RTF_EXTENSIONS_FILE =
RTF_STYLESHEET_FILE =
RTF_EXTENSIONS_FILE =
#---------------------------------------------------------------------------
# configuration options related to the man page output
# Configuration options related to the man page output
#---------------------------------------------------------------------------
GENERATE_MAN = NO
MAN_OUTPUT = man
MAN_EXTENSION = .3
MAN_SUBDIR =
MAN_LINKS = NO
#---------------------------------------------------------------------------
# configuration options related to the XML output
# Configuration options related to the XML output
#---------------------------------------------------------------------------
GENERATE_XML = NO
XML_OUTPUT = xml
XML_SCHEMA =
XML_DTD =
XML_PROGRAMLISTING = YES
XML_NS_MEMB_FILE_SCOPE = NO
#---------------------------------------------------------------------------
# configuration options for the AutoGen Definitions output
# Configuration options related to the DOCBOOK output
#---------------------------------------------------------------------------
GENERATE_DOCBOOK = NO
DOCBOOK_OUTPUT = docbook
#---------------------------------------------------------------------------
# Configuration options for the AutoGen Definitions output
#---------------------------------------------------------------------------
GENERATE_AUTOGEN_DEF = NO
#---------------------------------------------------------------------------
# configuration options related to the Perl module output
# Configuration options related to the Perl module output
#---------------------------------------------------------------------------
GENERATE_PERLMOD = NO
PERLMOD_LATEX = NO
PERLMOD_PRETTY = YES
PERLMOD_MAKEVAR_PREFIX =
PERLMOD_MAKEVAR_PREFIX =
#---------------------------------------------------------------------------
# Configuration options related to the preprocessor
# Configuration options related to the preprocessor
#---------------------------------------------------------------------------
ENABLE_PREPROCESSING = YES
MACRO_EXPANSION = NO
EXPAND_ONLY_PREDEF = NO
SEARCH_INCLUDES = YES
INCLUDE_PATH =
INCLUDE_FILE_PATTERNS =
INCLUDE_PATH =
INCLUDE_FILE_PATTERNS =
PREDEFINED = DO_NOT_DOCUMENT \
DOXYGEN \
WITH_MP4 \
WITH_ASF
EXPAND_AS_DEFINED =
DOXYGEN
EXPAND_AS_DEFINED =
SKIP_FUNCTION_MACROS = YES
#---------------------------------------------------------------------------
# Configuration::addtions related to external references
# Configuration options related to external references
#---------------------------------------------------------------------------
TAGFILES =
GENERATE_TAGFILE =
TAGFILES =
GENERATE_TAGFILE =
ALLEXTERNALS = NO
EXTERNAL_GROUPS = YES
PERL_PATH = /usr/bin/perl
EXTERNAL_PAGES = YES
#---------------------------------------------------------------------------
# Configuration options related to the dot tool
# Configuration options related to the dot tool
#---------------------------------------------------------------------------
CLASS_DIAGRAMS = YES
DIA_PATH =
HIDE_UNDOC_RELATIONS = YES
HAVE_DOT = YES
DOT_NUM_THREADS = 0
DOT_FONTNAME = Helvetica
DOT_FONTSIZE = 10
DOT_FONTPATH =
CLASS_GRAPH = YES
COLLABORATION_GRAPH = YES
GROUP_GRAPHS = YES
UML_LOOK = NO
UML_LIMIT_NUM_FIELDS = 10
DOT_UML_DETAILS = NO
DOT_WRAP_THRESHOLD = 17
TEMPLATE_RELATIONS = YES
INCLUDE_GRAPH = YES
INCLUDED_BY_GRAPH = YES
CALL_GRAPH = NO
CALLER_GRAPH = NO
GRAPHICAL_HIERARCHY = YES
DOT_IMAGE_FORMAT = png
DOT_PATH =
DOTFILE_DIRS =
MAX_DOT_GRAPH_WIDTH = 1024
MAX_DOT_GRAPH_HEIGHT = 1024
DIRECTORY_GRAPH = YES
DOT_IMAGE_FORMAT = svg
INTERACTIVE_SVG = NO
DOT_PATH =
DOTFILE_DIRS =
MSCFILE_DIRS =
DIAFILE_DIRS =
PLANTUML_JAR_PATH =
PLANTUML_CFG_FILE =
PLANTUML_INCLUDE_PATH =
DOT_GRAPH_MAX_NODES = 100
MAX_DOT_GRAPH_DEPTH = 0
DOT_TRANSPARENT = NO
DOT_MULTI_TARGETS = NO
GENERATE_LEGEND = YES
DOT_CLEANUP = YES
#---------------------------------------------------------------------------
# Configuration::addtions related to the search engine
#---------------------------------------------------------------------------
SEARCHENGINE = NO

159
INSTALL
View File

@@ -1,159 +0,0 @@
TagLib Installation
===================
TagLib uses the CMake build system. As a user, you will most likely want to
build TagLib in release mode and install it into a system-wide location.
This can be done using the following commands:
cmake -DCMAKE_INSTALL_PREFIX=/usr/local -DCMAKE_BUILD_TYPE=Release .
make
sudo make install
In order to build the included examples, use the BUILD_EXAMPLES option:
cmake -DBUILD_EXAMPLES=ON [...]
See http://www.cmake.org/cmake/help/runningcmake.html for generic help on
running CMake.
Mac OS X
--------
On Mac OS X, you might want to build a framework that can be easily integrated
into your application. If you set the BUILD_FRAMEWORK option on, it will compile
TagLib as a framework. For example, the following command can be used to build
an Universal Binary framework with Mac OS X 10.4 as the deployment target:
cmake -DCMAKE_BUILD_TYPE=Release \
-DBUILD_FRAMEWORK=ON \
-DCMAKE_C_COMPILER=/usr/bin/gcc-4.0 \
-DCMAKE_CXX_COMPILER=/usr/bin/c++-4.0 \
-DCMAKE_OSX_SYSROOT=/Developer/SDKs/MacOSX10.4u.sdk/ \
-DCMAKE_OSX_DEPLOYMENT_TARGET=10.4 \
-DCMAKE_OSX_ARCHITECTURES="ppc;i386;x86_64"
For a 10.6 Snow Leopard static library with both 32-bit and 64-bit code, use:
cmake -DCMAKE_BUILD_TYPE=Release \
-DCMAKE_OSX_DEPLOYMENT_TARGET=10.6 \
-DCMAKE_OSX_ARCHITECTURES="i386;x86_64" \
-DBUILD_SHARED_LIBS=OFF \
-DCMAKE_INSTALL_PREFIX="<folder you want to build to>"
After 'make', and 'make install', add libtag.a to your XCode project, and add
the include folder to the project's User Header Search Paths.
Windows
-------
It's Windows ... Systems vary!
This means you need to adjust things to suit your system, especially paths.
Tested with:
Microsoft Visual Studio 2010
Gcc by mingw-w64.sf.net v4.6.3 (Strawberry Perl 32b)
MinGW32-4.8.0
Requirements:
1. Tool chain, Build Environment, Whatever ya want to call it ...
Installed and working.
2. CMake program. (Available at: www.cmake.org)
Installed and working.
Optional:
1. Zlib library.
Available in some Tool Chains, Not all.
Search the web, Take your choice.
Useful configuration options used with CMake (GUI and/or Command line):
Any of the ZLIB_ variables may be used at the command line, ZLIB_ROOT is only
available on the Command line.
ZLIB_ROOT= Where to find ZLib's root directory.
Assumes parent of: \include and \lib.
ZLIB_INCLUDE_DIR= Where to find ZLib's Include directory.
ZLIB_LIBRARY= Where to find ZLib's Library.
ZLIB_SOURCE= Where to find ZLib's Source Code.
Alternative to ZLIB_INCLUDE_DIR and ZLIB_LIBRARY.
CMAKE_INSTALL_PREFIX= Where to install Taglib.
CMAKE_BUILD_TYPE= Release, Debug, etc ... (Not available in MSVC)
The easiest way is at the Command Prompt.
MSVS Command Prompt for MSVS Users.
(Batch file and/or Shortcuts are your friends)
1. Build the Makefiles:
Replace "GENERATOR" with your needs.
For MSVS : "Visual Studio X" where X is the single or two digit version.
For MinGW: "MinGW Makefiles"
C:\GitRoot\taglib> cmake -G "GENERATOR" -DCMAKE_INSTALL_PREFIX=C:\Libraries\taglib
Or use the CMake GUI:
1. Open CMake GUI.
2. Set Paths.
"Where is the source code" and "Where to build the binaries"
Example, Both would be: C:\GitRoot\taglib
3. Tick: Advanced
4. Select: Configure
5. Select: Generator
6. Tick: Use default native compilers
7. Select: Finish
Wait until done.
5. If using ZLib, Scroll down.
(to the bottom of the list of options ... should go over them all)
1. Edit: ZLIB_INCLUDE_DIR
2. Edit: ZLIB_LIBRARY
6. Select: Generate
2. Build the project:
MSVS:
C:\GitRoot\taglib> msbuild all_build.vcxproj /p:Configuration=Release
OR (Depending on MSVS version or personal choice)
C:\GitRoot\taglib> devenv all_build.vcxproj /build Release
MinGW:
C:\GitRoot\taglib> gmake
OR (Depending on MinGW install)
C:\GitRoot\taglib> mingw32-make
Or in the MSVS GUI:
1. Open MSVS.
2. Open taglib solution.
3. Set build type to: Release (look in the tool bars)
2. Hit F7 to build the solution. (project)
3. Install the project:
(Change 'install' to 'uninstall' to uninstall the project)
MSVS:
C:\GitRoot\taglib> msbuild install.vcxproj
OR (Depending on MSVC version or personal choice)
C:\GitRoot\taglib> devenv install.vcxproj
MinGW:
C:\GitRoot\taglib> gmake install
OR (Depending on MinGW install)
C:\GitRoot\taglib> mingw32-make install
Or in the MSVS GUI:
1. Open project.
2. Open Solution Explorer.
3. Right Click: INSTALL
4. Select: Project Only
5. Select: Build Only INSTALL
To build a static library, set the following two options with CMake.
-DBUILD_SHARED_LIBS=OFF -DENABLE_STATIC_RUNTIME=ON
Including ENABLE_STATIC_RUNTIME=ON indicates you want TagLib built using the
static runtime library, rather than the DLL form of the runtime.
Unit Tests
----------
If you want to run the test suite to make sure TagLib works properly on your
system, you need to have cppunit installed. To build the tests, include
the option -DBUILD_TESTS=on when running cmake.
The test suite has a custom target in the build system, so you can run
the tests using make:
make check

519
INSTALL.md Normal file
View File

@@ -0,0 +1,519 @@
# TagLib Installation
TagLib uses the CMake build system. As a user, you will most likely want to
build TagLib in release mode and install it into a system-wide location.
This can be done using the following commands:
cmake -DCMAKE_INSTALL_PREFIX=/usr/local -DCMAKE_BUILD_TYPE=Release .
make
sudo make install
In order to build the included examples, use the `BUILD_EXAMPLES` option:
cmake -DBUILD_EXAMPLES=ON [...]
If you want to build TagLib without ZLib, you can use
cmake -DCMAKE_INSTALL_PREFIX=/usr/local -DCMAKE_BUILD_TYPE=Release -DWITH_ZLIB=OFF .
make
sudo make install
See [cmake(1)](https://cmake.org/cmake/help/latest/manual/cmake.1.html) for
generic help on running CMake.
## Build Options
These are the most important build options. For details, have a look into the
CMakeLists.txt file.
| Option | Description |
|-------------------------|----------------------------------------------------|
| `BUILD_SHARED_LIBS` | Build shared libraries |
| `CMAKE_BUILD_TYPE` | Debug, Release, RelWithDebInfo, MinSizeRel |
| `BUILD_EXAMPLES` | Build examples |
| `BUILD_BINDINGS` | Build C bindings |
| `BUILD_TESTING` | Build unit tests |
| `TRACE_IN_RELEASE` | Enable debug output in release builds |
| `WITH_ZLIB` | Whether to build with ZLib (default ON) |
| `ZLIB_ROOT` | Where to find ZLib's root directory |
| `ZLIB_INCLUDE_DIR` | Where to find ZLib's include directory |
| `ZLIB_LIBRARY` | Where to find ZLib's library |
| `CMAKE_INSTALL_PREFIX` | Where to install Taglib |
| `TAGLIB_INSTALL_SUFFIX` | Suffix added to installed libraries, includes, ... |
| `ENABLE_STATIC_RUNTIME` | Link with MSVC runtime statically |
| `BUILD_FRAMEWORK` | Build a macOS framework |
| `TESTS_DIR` | Where to find unit test data (with data appended) |
| `TESTS_TMPDIR` | Where to create temporary files in unit tests |
If you want to install TagLib 2 alongside TagLib 1, you can use
`-DTAGLIB_INSTALL_SUFFIX=-2` and make sure that `BUILD_EXAMPLES` is not `ON`
for both versions. The installed files will then include bin/taglib-2-config,
include/taglib-2, cmake/taglib-2, pkgconfig/taglib-2.pc,
pkgconfig/taglib_c-2.pc and the libraries have a suffix "-2".
### Compile Time Configuration of Supported Formats
To reduce the size of the library, it is possible to switch off supported file
formats. By default, all formats are enabled. Support for MPEG files (MP3, AAC)
and ID3 tags cannot be disabled. The following CMake options are available:
| Option | Description |
|-------------------------|----------------------------------------------------|
| `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, 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 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
if the library is built specifically for a certain project. The public header
files still contain the full API, if you use TagLib with a reduced set of
formats, you can include taglib_config.h and use its definitions (prefixed with
`TAGLIB_`, e.g. `TAGLIB_WITH_APE`), as it is done in examples/framelist.cpp.
## Dependencies
A required dependency is [utf8cpp](https://github.com/nemtrif/utfcpp). You can
install the corresponding package (libutfcpp-dev on Ubuntu, utf8cpp in Homebrew,
utfcpp in vcpkg) or fetch the Git submodule with `git submodule update --init`.
Optional dependencies are
- [zlib](https://www.zlib.net/): You can disable it with `-DWITH_ZLIB=OFF`,
build and install it from the sources or use a package (zlib1g-dev on Ubuntu,
zlib in vcpkg). It is needed for compressed ID3v2 frames.
- [CppUnit](https://wiki.documentfoundation.org/Cppunit): Is required for unit
tests, which are disabled by default. If you enable them with
`-DBUILD_TESTING=ON`, you can build and install it from the sources or use a
package (libcppunit-dev on Ubuntu, cppunit in Homebrew, cppunit in vcpkg).
If the unit tests are enabled, you can run them with your build tool
(`make check`, `ninja check`) or via CMake
`cmake --build /path/to/build-dir --target check`.
## UNIX (Including Linux, BSD and macOS)
#### Building TagLib
On Linux, you can install the dependencies using the package manager of your
distribution. On macOS with Homebrew, you can use `brew install cppunit utf8cpp`
to install the dependencies.
```
# Adapt these environment variables to your directories
TAGLIB_SRC_DIR=$HOME/projects/taglib/src/taglib
TAGLIB_DST_DIR=$HOME/projects/taglib/src/build
cd $TAGLIB_SRC_DIR
cmake -B $TAGLIB_DST_DIR -DBUILD_SHARED_LIBS=ON -DVISIBILITY_HIDDEN=ON \
-DBUILD_TESTING=ON -DBUILD_EXAMPLES=ON -DBUILD_BINDINGS=ON \
-DCMAKE_BUILD_TYPE=Release
cmake --build $TAGLIB_DST_DIR --config Release
cmake --build $TAGLIB_DST_DIR --config Release --target check
# Install to ~/pkg folder
cmake --install $TAGLIB_DST_DIR --config Release --prefix $HOME/pkg --strip
# Run example from installed package
LD_LIBRARY_PATH=$HOME/pkg/lib $HOME/pkg/bin/tagreader /path/to/audio-file
```
#### Building a Project Using TagLib
As an example for an external application using TagLib, we create a folder
taglib-client and copy the file tagreader.cpp from the examples folder
of the TagLib sources into it. We then add this simple CMakeLists.txt.
```
cmake_minimum_required(VERSION 3.5.0)
project(taglib-client)
set(CMAKE_CXX_STANDARD 17)
find_package(ZLIB)
find_package(TagLib 2.0.0 REQUIRED)
add_executable(tagreader tagreader.cpp)
target_link_libraries(tagreader TagLib::tag)
```
To build into a folder `build` inside this directory, just run
```
# Set this to the path where TagLib is installed
TAGLIB_PREFIX=$HOME/pkg
cmake -B build -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=$TAGLIB_PREFIX
cmake --build build --config Release
build/tagreader /path/to/audio-file
```
If TagLib is installed in a standard location (e.g. /usr, /usr/local), its CMake
configuration is found without setting CMAKE_INSTALL_PREFIX (or alternatives
like CMAKE_PREFIX_PATH, CMAKE_SYSTEM_PREFIX_PATH).
To use the C-bindings with tagreader_c.c, you can change the last two lines in
CMakeLists.txt to
```
add_executable(tagreader_c tagreader_c.c)
target_link_libraries(tagreader_c TagLib::tag_c)
```
#### Building a Project Using pkg-config
Before version 2.0, TagLib did not export CMake configuration, therefore
`pkg-config` could be used. This is still possible.
Note, however, that `pkg-config` makes it more difficult to use packages which
are not installed in a standard location. You can point `pkg-config` to search
in non-standard locations for .pc-files, but they still contain the install
locations defined when building TagLib. Since we did not give a
`CMAKE_INSTALL_PREFIX` in the example above, the default `/usr/local/` is used.
```
PKG_CONFIG_PATH=$HOME/pkg/lib/pkgconfig pkg-config --libs --cflags taglib
-I/usr/local/include -I/usr/local/include/taglib -L/usr/local/lib -ltag -lz
```
The following examples use the same build example with additional CMake
parameters affecting the installation location.
- Using the default prefix `-DCMAKE_INSTALL_PREFIX=/usr/local`:
```
-I/usr/local/include -I/usr/local/include/taglib -L/usr/local/lib -ltag -lz
```
- Using an absolute prefix `-DCMAKE_INSTALL_PREFIX=/usr`:
```
-I/usr/include/taglib -ltag -lz
```
- Using absolute lib and include directories
`-DCMAKE_INSTALL_LIBDIR=/abs-lib -DCMAKE_INSTALL_INCLUDEDIR=/abs-include -DCMAKE_INSTALL_PREFIX=/usr`:
```
-I/abs-include -I/abs-include/taglib -L/abs-lib -ltag -lz
```
- Using relative lib and include directories
`-DCMAKE_INSTALL_LIBDIR=rel-lib -DCMAKE_INSTALL_INCLUDEDIR=rel-include -DCMAKE_INSTALL_PREFIX=/usr`:
```
-I/usr/rel-include -I/usr/rel-include/taglib -L/usr/rel-lib -ltag -lz
```
This is the output of
```
PKG_CONFIG_PATH=$HOME/pkg/rel-lib/pkgconfig pkg-config --libs --cflags taglib
```
You could add `--define-prefix` to the `pkg-config` arguments to have the
effective location `$HOME/pkg/` instead of `/usr/` in the output.
Therefore, the correct paths for our example package would be given when using
the `--define-prefix` feature.
```
PKG_CONFIG_PATH=$HOME/pkg/lib/pkgconfig pkg-config --define-prefix --libs --cflags taglib
```
You can use pkg-config from CMake, however, relocation with `--define-prefix`
is not supported.
```
cmake_minimum_required(VERSION 3.6.0)
project(taglib-client)
find_package(PkgConfig)
pkg_check_modules(TAGLIB REQUIRED IMPORTED_TARGET taglib)
add_executable(tagreader tagreader.cpp)
target_link_libraries(tagreader PkgConfig::TAGLIB)
```
#### Framework on macOS
On macOS, you might want to build a framework that can be easily integrated
into your application. If you set the BUILD_FRAMEWORK option on, it will compile
TagLib as a framework. For example, the following command can be used to build
a framework with macOS 10.10 as the deployment target:
mkdir build; cd build
cmake .. -DCMAKE_BUILD_TYPE=Release \
-DBUILD_TESTING=OFF \
-DBUILD_FRAMEWORK=ON \
-DCMAKE_OSX_DEPLOYMENT_TARGET=10.10 \
-DCMAKE_OSX_ARCHITECTURES="arm64;x86_64"
make
For a 10.10 static library, use:
mkdir build; cd build
cmake .. -DCMAKE_BUILD_TYPE=Release \
-DBUILD_TESTING=OFF \
-DBUILD_SHARED_LIBS=OFF \
-DCMAKE_OSX_DEPLOYMENT_TARGET=10.10 \
-DCMAKE_OSX_ARCHITECTURES="arm64;x86_64"
make
After `make`, and `make install`, add `libtag.` to your XCode project, and add
the include folder to the project's User Header Search Paths.
## Windows
### Using Visual Studio Build Tools
For this example, we assume that you have the Visual Studio Build Tools 2022
installed. Additionally, you need [cmake](https://cmake.org/), which can be
installed for example using [scoop](https://scoop.sh/) with
`scoop install cmake`.
#### Building TagLib (MSVC)
You can build and install the dependencies
[utf8cpp](https://github.com/nemtrif/utfcpp) and optionally
[zlib](https://www.zlib.net/) and
[CppUnit](https://wiki.documentfoundation.org/Cppunit) yourself, but it is
probably easier using a package manager such as [vcpkg](https://vcpkg.io/),
which can be installed using `scoop install vcpkg` and then install the
dependencies using `vcpkg install utfcpp zlib cppunit`.
Now you can build TagLib from PowerShell.
```
# Adapt these environment variables to your directories
$env:TAGLIB_SRC_DIR = "${env:HOMEDRIVE}${env:HOMEPATH}/projects/taglib/src/taglib"
$env:TAGLIB_DST_DIR = "${env:HOMEDRIVE}${env:HOMEPATH}/projects/taglib/src/msvs_vcpkg_build"
cd $env:TAGLIB_SRC_DIR
cmake -B $env:TAGLIB_DST_DIR -DBUILD_SHARED_LIBS=ON -DVISIBILITY_HIDDEN=ON `
-DBUILD_TESTING=ON -DBUILD_EXAMPLES=ON -DBUILD_BINDINGS=ON `
-DCMAKE_BUILD_TYPE=Release `
-DCMAKE_TOOLCHAIN_FILE="$env:VCPKG_ROOT/scripts/buildsystems/vcpkg.cmake" `
-G "Visual Studio 17 2022"
cmake --build $env:TAGLIB_DST_DIR --config Release
# Add directories containing DLL dependencies to path
$env:Path += -join(";$env:TAGLIB_DST_DIR\taglib\Release;",
"$env:TAGLIB_DST_DIR\bindings\c\Release;",
"$env:VCPKG_ROOT\packages\cppunit_x64-windows\bin;",
"$env:VCPKG_ROOT\packages\utfcpp_x64-windows\bin;",
"$env:VCPKG_ROOT\packages\zlib_x64-windows\bin")
cmake --build $env:TAGLIB_DST_DIR --config Release --target check
# Install to \pkg folder on current drive
cmake --install $env:TAGLIB_DST_DIR --config Release --prefix /pkg --strip
# Static library
$env:TAGLIB_DST_DIR = "C:/Users/fle/projects/taglib/src/msvs_vcpkg_static_build"
cmake -B $env:TAGLIB_DST_DIR -DBUILD_SHARED_LIBS=OFF -DVISIBILITY_HIDDEN=ON `
-DBUILD_TESTING=ON -DBUILD_EXAMPLES=ON -DBUILD_BINDINGS=ON `
-DCMAKE_BUILD_TYPE=Release `
-DCMAKE_TOOLCHAIN_FILE="$env:VCPKG_ROOT/scripts/buildsystems/vcpkg.cmake" `
-G "Visual Studio 17 2022"
cmake --build $env:TAGLIB_DST_DIR --config Release
cmake --build $env:TAGLIB_DST_DIR --config Release --target check
# Install to \pkg_static folder on current drive
cmake --install $env:TAGLIB_DST_DIR --config Release --prefix /pkg_static --strip
```
Including `ENABLE_STATIC_RUNTIME=ON` indicates you want TagLib built using the
static runtime library, rather than the DLL form of the runtime.
#### Building a Project Using TagLib (MSVC)
As an example for an external application using TagLib, we create a folder
taglib-client and copy the file tagreader.cpp from the examples folder
of the TagLib sources into it. We then add this simple CMakeLists.txt.
```
cmake_minimum_required(VERSION 3.5.0)
project(taglib-client)
set(CMAKE_CXX_STANDARD 17)
find_package(ZLIB)
find_package(TagLib 2.0.0 REQUIRED)
add_executable(tagreader tagreader.cpp)
target_link_libraries(tagreader TagLib::tag)
```
To build into a folder build inside this directory, just run
```
# Set this to the path where TagLib is installed
$env:TAGLIB_PREFIX = "/pkg"
cmake -B build -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX="$env:TAGLIB_PREFIX" `
-DCMAKE_TOOLCHAIN_FILE="$env:VCPKG_ROOT/scripts/buildsystems/vcpkg.cmake" `
-G "Visual Studio 17 2022"
cmake --build build --config Release
# Add directories containing DLL dependencies to path
$env:Path += ";$env:TAGLIB_PREFIX\bin;$env:VCPKG_ROOT\packages\zlib_x64-windows\bin"
build\Release\tagreader /path/to/audio-file
```
To use the C-bindings with tagreader_c.c, you can change the last two lines in
CMakeLists.txt to
```
add_executable(tagreader_c tagreader_c.c)
target_link_libraries(tagreader_c TagLib::tag_c)
```
If you link against a static TagLib, you have to adapt the installation path
(e.g. "/pkg_static") and add the following line to CMakeLists.txt
```
target_compile_definitions(tagreader_c PRIVATE TAGLIB_STATIC)
```
### Using MSYS2
#### Building TagLib (MSYS2)
To build TagLib using Clang from MSYS, install [msys2](https://www.msys2.org/),
then start the MSYS CLANG64 shell and install the dependencies as they are
specified in the [MSYS recipe for TagLib](
https://packages.msys2.org/package/mingw-w64-clang-x86_64-taglib?repo=clang64).
```
pacman -Suy
pacman -S mingw-w64-clang-x86_64-gcc-libs mingw-w64-clang-x86_64-zlib \
mingw-w64-clang-x86_64-cc mingw-w64-clang-x86_64-cmake \
mingw-w64-clang-x86_64-cppunit mingw-w64-clang-x86_64-ninja
```
Then you can build TagLib from the MSYS CLANG64 Bash.
```
# Adapt these environment variables to your directories
TAGLIB_SRC_DIR=$HOME/projects/taglib/src/taglib
TAGLIB_DST_DIR=$HOME/projects/taglib/src/msys_build
cd $TAGLIB_SRC_DIR
cmake -B $TAGLIB_DST_DIR -DBUILD_SHARED_LIBS=ON -DVISIBILITY_HIDDEN=ON \
-DBUILD_TESTING=ON -DBUILD_EXAMPLES=ON -DBUILD_BINDINGS=ON \
-DCMAKE_BUILD_TYPE=Release -G Ninja
cmake --build $TAGLIB_DST_DIR --config Release
PATH=$PATH:/clang64/bin:$TAGLIB_DST_DIR/taglib:$TAGLIB_DST_DIR/bindings/c \
cmake --build $TAGLIB_DST_DIR --config Release --target check
# Install to /pkg_msys folder inside MSYS root (e.g. C:/msys64/pkg_msys/)
cmake --install $TAGLIB_DST_DIR --config Release --prefix /pkg_msys --strip
```
#### Building a Project Using TagLib (MSYS2)
As an example for an external application using TagLib, we create a folder
taglib-client and copy the file tagreader.cpp from the examples folder
of the TagLib sources into it. We then add this simple CMakeLists.txt.
```
cmake_minimum_required(VERSION 3.5.0)
project(taglib-client)
set(CMAKE_CXX_STANDARD 17)
find_package(ZLIB)
find_package(TagLib 2.0.0 REQUIRED)
add_executable(tagreader tagreader.cpp)
target_link_libraries(tagreader TagLib::tag)
```
To build into a folder build_msys inside this directory, just run
```
# Set this to the path where TagLib is installed
TAGLIB_PREFIX=/pkg_msys
cmake -B build_msys -DCMAKE_BUILD_TYPE=Release \
-DCMAKE_INSTALL_PREFIX=$TAGLIB_PREFIX -G Ninja
cmake --build build_msys --config Release
PATH=$PATH:$TAGLIB_PREFIX/bin
build_msys/tagreader /path/to/audio-file
```
To use the C-bindings with tagreader_c.c, you can change the last two lines in
CMakeLists.txt to
```
add_executable(tagreader_c tagreader_c.c)
target_link_libraries(tagreader_c TagLib::tag_c)
```
### Using MinGW
The instructions for MSYS2 can also be used to build with MinGW. You could use
Git Bash together with the MinGW provided by Qt.
```
# Adapt these environment variables to your directories
PATH=$PATH:/c/Qt/Tools/mingw1120_64/bin
TAGLIB_SRC_DIR=$HOME/projects/taglib/src/taglib
TAGLIB_DST_DIR=$HOME/projects/taglib/src/mingw_build
cd $TAGLIB_SRC_DIR
cmake -B $TAGLIB_DST_DIR -DBUILD_SHARED_LIBS=ON -DVISIBILITY_HIDDEN=ON \
-DBUILD_EXAMPLES=ON -DBUILD_BINDINGS=ON -DWITH_ZLIB=OFF \
-DCMAKE_BUILD_TYPE=Release -G 'MinGW Makefiles'
cmake --build $TAGLIB_DST_DIR --config Release
PATH=$PATH:$TAGLIB_DST_DIR/taglib \
$TAGLIB_DST_DIR/examples/tagreader /path/to/audio-file
# Install to C:\pkg_mingw
cmake --install $TAGLIB_DST_DIR --config Release --prefix /c/pkg_mingw --strip
```
The installed package can then be used by other projects.
```
TAGLIB_PREFIX=/c/pkg_mingw
cmake -B build_mingw -DCMAKE_BUILD_TYPE=Release \
-DCMAKE_INSTALL_PREFIX=$TAGLIB_PREFIX -G 'MinGW Makefiles'
cmake --build build_mingw --config Release
PATH=$PATH:$TAGLIB_PREFIX/bin
build_mingw/tagreader /path/to/audio-file
```
## Android
### Using vcpkg
The bash script below can be used to build TagLib for Android using vcpkg.
It must be started in the parent folder of the taglib source folder and will
build in a folder _android_build_ and install into _android_pkg_.
The package and the unit tests are then transferred to an Android device
and the unit tests are run on the device.
Note that `TESTS_TMPDIR` is set because there is no system-wide temporary folder
on Android. `TESTS_DIR` is set to run the tests on the target.
```
# You may have to adapt the NDK and vcpkg paths and the ABI/triplet.
export ANDROID_NDK_HOME=$HOME/Development/android-sdk/ndk/23.1.7779620
export VCPKG_ROOT=$HOME/Development/vcpkg
PATH=$PATH:$VCPKG_ROOT
# armeabi-v7a/arm-android or arm64-v8a/arm64-android or x86/x86-android or x86_64/x64-android
android_abi=armeabi-v7a
vcpkg_target_triplet=arm-android
vcpkg_toolchain_file=$VCPKG_ROOT/scripts/buildsystems/vcpkg.cmake
android_toolchain_file=$ANDROID_NDK_HOME/build/cmake/android.toolchain.cmake
vcpkg install --triplet $vcpkg_target_triplet utfcpp zlib cppunit
cmake -B android_build -S taglib \
-DCMAKE_TOOLCHAIN_FILE=$vcpkg_toolchain_file \
-DVCPKG_CHAINLOAD_TOOLCHAIN_FILE=$android_toolchain_file \
-DVCPKG_TARGET_TRIPLET=$vcpkg_target_triplet \
-DANDROID_ABI=$android_abi \
-GNinja -DBUILD_SHARED_LIBS=ON -DCMAKE_BUILD_TYPE=Release \
-DVISIBILITY_HIDDEN=ON -DENABLE_CCACHE=ON -DBUILD_EXAMPLES=ON -DBUILD_TESTING=ON \
-DTESTS_DIR=/data/local/tmp/tests/ -DTESTS_TMPDIR=/data/local/tmp
cmake --build android_build --config Release
cmake --install android_build --config Release --prefix android_pkg --strip
cp -a android_build/tests/test_runner android_pkg/bin/
if hash adb 2>/dev/null; then
adb push android_pkg /data/local/tmp/
adb push android_build/tests/test_runner /data/local/tmp/tests/test_runner
adb push taglib/tests/data /data/local/tmp/tests/
adb shell "env LD_LIBRARY_PATH=/data/local/tmp/android_pkg/lib /data/local/tmp/tests/test_runner"
# You could also try an example binary:
# adb shell "env LD_LIBRARY_PATH=/data/local/tmp/android_pkg/lib /data/local/tmp/android_pkg/bin/tagreader '/sdcard/Music/Some Album/A Track.mp3'"
fi
```

288
NEWS
View File

@@ -1,288 +0,0 @@
TagLib 1.11 (Mar 4, 2016)
=========================
1.11 BETA 2:
* Better handling of PCM WAV files with a 'fact' chunk.
* Better handling of corrupted APE tags.
* Efficient decoding of unsynchronized ID3v2 frames.
* Fixed text encoding when saving certain frames in ID3v2.3 tags.
* Fixed updating the size of RIFF files when removing chunks.
* Several smaller bug fixes and performance improvements.
1.11 BETA:
* New API for creating FileRef from IOStream.
* Added support for ID3v2 PCST and WFED frames.
* Added support for pictures in XiphComment.
* Added String::clear().
* Added FLAC::File::strip() for removing non-standard tags.
* Added alternative functions to XiphComment::removeField().
* Added BUILD_BINDINGS build option.
* Added ENABLE_CCACHE build option.
* Replaced ENABLE_STATIC build option with BUILD_SHARED_LIBS.
* Better handling of duplicate ID3v2 tags in all kinds of files.
* Better handling of duplicate tag chunks in WAV files.
* Better handling of duplicate tag chunks in AIFF files.
* Better handling of duplicate Vorbis comment blocks in FLAC files.
* Better handling of broken MPEG audio frames.
* Fixed crash when calling File::properties() after strip().
* Fixed crash when parsing certain MPEG files.
* Fixed crash when saving Ogg files.
* Fixed possible file corruptions when saving ASF files.
* Fixed possible file corruptions when saving FLAC files.
* Fixed possible file corruptions when saving MP4 files.
* Fixed possible file corruptions when saving MPEG files.
* Fixed possible file corruptions when saving APE files.
* Fixed possible file corruptions when saving Musepack files.
* Fixed possible file corruptions when saving WavPack files.
* Fixed updating the comment field of Vorbis comments.
* Fixed reading date and time in ID3v2.3 tags.
* Marked ByteVector::null and ByteVector::isNull() deprecated.
* Marked String::null and String::isNull() deprecated.
* Marked XiphComment::removeField() deprecated.
* Marked Ogg::Page::getCopyWithNewPageSequenceNumber() deprecated. It returns null.
* Marked custom integer types deprecated.
* Many smaller bug fixes and performance improvements.
TagLib 1.10 (Nov 11, 2015)
==========================
1.10:
* Added new options to the tagwriter example.
* Fixed self-assignment operator in some types.
* Fixed extraction of MP4 tag keys with an empty list.
1.10 BETA:
* New API for the audio length in milliseconds.
* Added support for ID3v2 ETCO and SYLT frames.
* Added support for album artist in PropertyMap API of MP4 files.
* Added support for embedded frames in ID3v2 CHAP and CTOC frames.
* Added support for AIFF-C files.
* Better handling of duplicate ID3v2 tags in MPEG files.
* Allowed generating taglib.pc on Windows.
* Added ZLIB_SOURCE build option.
* Fixed backwards-incompatible change in TagLib::String when constructing UTF16 strings.
* Fixed crash when parsing certain FLAC files.
* Fixed crash when encoding empty strings.
* Fixed saving of certain XM files on OS X.
* Changed Xiph and APE generic getters to return space-concatenated values.
* Fixed possible file corruptions when removing tags from WAV files.
* Added support for MP4 files with 64-bit atoms in certain 64-bit environments.
* Prevented ID3v2 padding from being too large.
* Fixed crash when parsing corrupted APE files.
* Fixed crash when parsing corrupted WAV files.
* Fixed crash when parsing corrupted Ogg FLAC files.
* Fixed crash when parsing corrupted MPEG files.
* Fixed saving empty tags in WAV files.
* Fixed crash when parsing corrupted Musepack files.
* Fixed possible memory leaks when parsing AIFF and WAV files.
* Fixed crash when parsing corrupted MP4 files.
* Stopped writing empty ID3v2 frames.
* Fixed possible file corruptions when saving WMA files.
* Added TagLib::MP4::Tag::isEmpty().
* Added accessors to manipulate MP4 tags.
* Fixed crash when parsing corrupted WavPack files.
* Fixed seeking MPEG frames.
* Fixed reading FLAC files with zero-sized padding blocks.
* Added support for reading the encoder information of WMA files.
* Added support for reading the codec of WAV files.
* Added support for multi channel WavPack files.
* Added support for reading the nominal bitrate of Ogg Speex files.
* Added support for VBR headers in MPEG files.
* Marked FLAC::File::streamInfoData() deprecated. It returns an empty ByteVector.
* Marked FLAC::File::streamLength() deprecated. It returns zero.
* Fixed possible file corruptions when adding an ID3v1 tag to FLAC files.
* Many smaller bug fixes and performance improvements.
TagLib 1.9.1 (Oct 8, 2013)
==========================
* Fixed binary incompatible change in TagLib::Map and TagLib::List.
* Fixed constructing String from ByteVector.
* Fixed compilation on MSVC with the /Zc:wchar_t- option.
* Fixed detecting of RIFF files with invalid chunk sizes.
* Added TagLib::MP4::Properties::codec().
TagLib 1.9 (Oct 6, 2013)
========================
* 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.
* New ID3v1::Tag methods for working directly with genre numbers.
* New MPEG::File methods for checking which tags are saved in the file.
* Added support for the PropertyMap API to ASF and MP4 files.
* Added MusicBrainz identifiers to the PropertyMap API.
* Allowed reading of MP4 cover art without an explicitly specified format.
* Better parsing of corrupted FLAC files.
* Fixed saving of PropertyMap comments without description into ID3v2 tags.
* Fixed crash when parsing certain XM files.
* Fixed compilation of unit test with clang.
* Better handling of files that can't be open or have read-only permissions.
* Improved atomic reference counting.
* New hookable API for debug messages.
* More complete Windows install instructions.
* Many smaller bug fixes and performance improvements.
TagLib 1.8 (Sep 6, 2012)
========================
1.8:
* Added support for OWNE ID3 frames.
* Changed key validation in the new PropertyMap API.
* ID3v1::Tag::setStringHandler will no londer delete the previous handler,
the caller is responsible for this.
* File objects will also no longer delete the passed IOStream objects. It
should be done in the caller code after the File object is no longer
used.
* Added ID3v2::Tag::setLatin1StringHandler for custom handling of
latin1-encoded text in ID3v2 frames.
* Fixed validation of ID3v2 frame IDs (IDs with '0' were ignored).
1.8 BETA:
* New API for accessing tags by name.
* New abstract I/O stream layer to allow custom I/O handlers.
* Support for writing ID3v2.3 tags.
* Support for various module file formats (MOD, S3M, IT, XM).
* Support for MP4 and ASF is now enabled by default.
* Started using atomic int operations for reference counting.
* Added methods for checking if WMA and MP4 files are DRM-protected.
* Added taglib_free to the C bindings.
* New method to allow removing pictures from FLAC files.
* Support for reading audio properties from ALAC and Musepack SV8 files.
* Added replay-gain information to Musepack audio properties.
* Support for APEv2 binary tags.
* Many AudioProperties subclasses now provide information about the total number of samples.
* Various small bug fixes.
TagLib 1.7.2 (Apr 20, 2012)
===========================
* Fixed division by zero while parsing corrupted MP4 files (CVE-2012-2396).
* Fixed compilation on Haiku.
TagLib 1.7.1 (Mar 17, 2012)
===========================
* Improved parsing of corrupted WMA, RIFF and OGG files.
* Fixed a memory leak in the WMA parser.
* Fixed a memory leak in the FLAC parser.
* Fixed a possible division by zero in the APE parser.
* Added detection of TTA2 files.
* Fixed saving of multiple identically named tags to Vorbis Comments.
TagLib 1.7 (Mar 11, 2011)
=========================
1.7:
* Fixed memory leaks in the FLAC file format parser.
* Fixed bitrate calculation for WAV files.
1.7 RC1:
* Support for reading/writing tags from Monkey's Audio files. (BUG:210404)
* Support for reading/writing embedded pictures from WMA files.
* Support for reading/writing embedded pictures from FLAC files (BUG:218696).
* Implemented APE::Tag::isEmpty() to check for all APE tags, not just the
basic ones.
* Added reading of WAV audio length. (BUG:116033)
* Exposed FLAC MD5 signature of the uncompressed audio stream via
FLAC::Properties::signature(). (BUG:160172)
* Added function ByteVector::toHex() for hex-encoding of byte vectors.
* WavPack reader now tries to get the audio length by finding the final
block, if the header doesn't have the information. (BUG:258016)
* Fixed a memory leak in the ID3v2.2 PIC frame parser. (BUG:257007)
* Fixed writing of RIFF files with even chunk sizes. (BUG:243954)
* Fixed compilation on MSVC 2010.
* Removed support for building using autoconf/automake.
* API docs can be now built using "make docs".
TagLib 1.6.3 (Apr 17, 2010)
===========================
* Fixed definitions of the TAGLIB_WITH_MP4 and TAGLIB_WITH_ASF macros.
* Fixed upgrading of ID3v2.3 genre frame with ID3v1 code 0 (Blues).
* New method `int String::toInt(bool *ok)` which can return whether the
conversion to a number was successfull.
* Fixed parsing of incorrectly written lengths in ID3v2 (affects mainly
compressed frames). (BUG:231075)
TagLib 1.6.2 (Apr 9, 2010)
==========================
* Read Vorbis Comments from the first FLAC metadata block, if there are
multipe ones. (BUG:211089)
* Fixed a memory leak in FileRef's OGA format detection.
* Fixed compilation with the Sun Studio compiler. (BUG:215225)
* Handle WM/TrackNumber attributes with DWORD content in WMA files.
(BUG:218526)
* More strict check if something is a valid MP4 file. (BUG:216819)
* Correctly save MP4 int-pair atoms with flags set to 0.
* Fixed compilation of the test runner on Windows.
* Store ASF attributes larger than 64k in the metadata library object.
* Ignore trailing non-data atoms when parsing MP4 covr atoms.
* Don't upgrade ID3v2.2 frame TDA to TDRC. (BUG:228968)
TagLib 1.6.1 (Oct 31, 2009)
===========================
* Better detection of the audio codec of .oga files in FileRef.
* Fixed saving of Vorbis comments to Ogg FLAC files. TagLib tried to
include the Vorbis framing bit, which is only correct for Ogg Vorbis.
* Public symbols now have explicitly set visibility to "default" on GCC.
* Added missing exports for static ID3v1 functions.
* Fixed a typo in taglib_c.pc
* Fixed a failing test on ppc64.
* Support for binary 'covr' atom in MP4 files. TagLib 1.6 treated them
as text atoms, which corrupted them in some cases.
* Fixed ID3v1-style genre to string conversion in MP4 files.
TagLib 1.6 (Sep 13, 2009)
=========================
1.6:
* New CMake option to build a static version - ENABLE_STATIC.
* Added support for disabling dllimport/dllexport on Windows using the
TAGLIB_STATIC macro.
* Support for parsing the obsolete 'gnre' MP4 atom.
* New cpp macros TAGLIB_WITH_MP4 and TAGLIB_WITH_ASF to determin if
TagLib was built with MP4/ASF support.
1.6 RC1:
* Split Ogg packets larger than 64k into multiple pages. (BUG:171957)
* TagLib can now use FLAC padding block. (BUG:107659)
* ID3v2.2 frames are now not incorrectly saved. (BUG:176373)
* Support for ID3v2.2 PIC frames. (BUG:167786)
* Fixed a bug in ByteVectorList::split().
* XiphComment::year() now falls back to YEAR if DATE doesn't exist
and XiphComment::year() falls back to TRACKNUM if TRACKNUMBER doesn't
exist. (BUG:144396)
* Improved ID3v2.3 genre parsing. (BUG:188578)
* Better checking of corrupted ID3v2 APIC data. (BUG:168382)
* Bitrate calculating using the Xing header now uses floating point
numbers. (BUG:172556)
* New TagLib::String method rfind().
* Added support for MP4 file format with iTunes-style metadata [optional].
* Added support for ASF (WMA) file format [optional].
* Fixed crash when saving a Locator APEv2 tag. (BUG:169810)
* Fixed a possible crash in the non-const version of String::operator[]
and in String::operator+=. (BUG:169389)
* Added support for PRIV ID3v2 frames.
* Empty ID3v2 genres are no longer treated as numeric ID3v1 genres.
* Added support for the POPM (rating/playcount) ID3v2 frame.
* Generic RIFF file format support:
* Support for AIFF files with ID3v2 tags.
* Support for WAV files with ID3v2 tags.
* Fixed crash on handling unsupported ID3v2 frames, e.g. on encrypted
frames. (BUG:161721)
* Fixed overflow while calculating bitrate of FLAC files with a very
high bitrate.

38
README.md Normal file
View File

@@ -0,0 +1,38 @@
# TagLib
[![Build Status](../../actions/workflows/build.yml/badge.svg)](../../actions)
### TagLib Audio Metadata Library
https://taglib.org/
TagLib is a library for reading and editing the metadata of several
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
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.
[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

@@ -1 +1 @@
add_subdirectory(c)
add_subdirectory(c)

View File

@@ -2,5 +2,4 @@ There are a few other people that have done bindings externally that I have
been made aware of. I have not personally reviewed these bindings, but I'm
listing them here so that those who find them useful are able to find them:
http://developer.kde.org/~wheeler/taglib.html#bindings
https://taglib.org/#language-bindings

View File

@@ -1,27 +1,92 @@
include_directories(
set(tag_c_HDR_DIRS
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/toolkit
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/asf
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/mpeg
${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/flac
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/mpc
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/mp4
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/mpeg/id3v2
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/mpeg/id3v2/frames
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/wavpack
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/ogg/speex
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/trueaudio
)
if(WITH_ASF)
set(tag_c_HDR_DIRS ${tag_c_HDR_DIRS}
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/asf
)
endif()
if(WITH_VORBIS)
set(tag_c_HDR_DIRS ${tag_c_HDR_DIRS}
${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/flac
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/ogg/speex
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/ogg/opus
)
endif()
if(WITH_APE)
set(tag_c_HDR_DIRS ${tag_c_HDR_DIRS}
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/mpc
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/wavpack
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/ape
)
endif()
if(WITH_MP4)
set(tag_c_HDR_DIRS ${tag_c_HDR_DIRS}
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/mp4
)
endif()
if(WITH_TRUEAUDIO)
set(tag_c_HDR_DIRS ${tag_c_HDR_DIRS}
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/trueaudio
)
endif()
if(WITH_RIFF)
set(tag_c_HDR_DIRS ${tag_c_HDR_DIRS}
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/riff
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/riff/aiff
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/riff/wav
)
endif()
if(WITH_MOD)
set(tag_c_HDR_DIRS ${tag_c_HDR_DIRS}
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/it
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/mod
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/s3m
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/xm
)
endif()
if(WITH_DSF)
set(tag_c_HDR_DIRS ${tag_c_HDR_DIRS}
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/dsf
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/dsdiff
)
endif()
if(WITH_SHORTEN)
set(tag_c_HDR_DIRS ${tag_c_HDR_DIRS}
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/shorten
)
endif()
if(WITH_MATROSKA)
set(tag_c_HDR_DIRS ${tag_c_HDR_DIRS}
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/matroska
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/matroska/ebml
)
endif()
include_directories(${tag_c_HDR_DIRS})
set(tag_c_HDRS tag_c.h)
add_library(tag_c tag_c.cpp ${tag_c_HDRS})
target_link_libraries(tag_c tag)
set_target_properties(tag_c PROPERTIES PUBLIC_HEADER "${tag_c_HDRS}")
target_include_directories(tag_c INTERFACE
$<INSTALL_INTERFACE:include/taglib${TAGLIB_INSTALL_SUFFIX}>
)
target_link_libraries(tag_c PRIVATE tag)
set_target_properties(tag_c PROPERTIES
PUBLIC_HEADER "${tag_c_HDRS}"
DEFINE_SYMBOL MAKE_TAGLIB_LIB
)
if(VISIBILITY_HIDDEN)
set_target_properties(tag_c PROPERTIES C_VISIBILITY_PRESET hidden)
endif()
if(BUILD_FRAMEWORK)
set_target_properties(tag_c PROPERTIES FRAMEWORK TRUE)
endif()
@@ -38,7 +103,7 @@ if(HAVE_CRUN_LIB)
# the only game in town, the three available STLs -- Cstd,
# stlport4 and stdcxx -- make this a mess. The KDE-Solaris
# team supports stdcxx (Apache RogueWave stdcxx 4.1.3).
# According to http://bugs.kde.org/show_bug.cgi?id=215225 the library can have the following two names:
find_library(ROGUEWAVE_STDCXX_LIBRARY NAMES stdcxx4 stdcxx)
if(NOT ROGUEWAVE_STDCXX_LIBRARY)
@@ -48,21 +113,45 @@ if(HAVE_CRUN_LIB)
endif()
set_target_properties(tag_c PROPERTIES
VERSION 0.0.0
SOVERSION 0
VERSION ${TAGLIB_SOVERSION_MAJOR}.${TAGLIB_SOVERSION_MINOR}.${TAGLIB_SOVERSION_PATCH}
SOVERSION ${TAGLIB_SOVERSION_MAJOR}
DEFINE_SYMBOL MAKE_TAGLIB_C_LIB
INSTALL_NAME_DIR ${LIB_INSTALL_DIR}
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)
set(TAGLIB_LIBRARY_SUFFIX "${TAGLIB_INSTALL_SUFFIX}${CMAKE_SHARED_LIBRARY_SUFFIX}")
else()
set(TAGLIB_LIBRARY_SUFFIX "${TAGLIB_INSTALL_SUFFIX}${CMAKE_STATIC_LIBRARY_SUFFIX}")
endif()
set_target_properties(tag_c PROPERTIES SUFFIX ${TAGLIB_LIBRARY_SUFFIX})
endif()
install(TARGETS tag_c
FRAMEWORK DESTINATION ${FRAMEWORK_INSTALL_DIR}
LIBRARY DESTINATION ${LIB_INSTALL_DIR}
RUNTIME DESTINATION ${BIN_INSTALL_DIR}
ARCHIVE DESTINATION ${LIB_INSTALL_DIR}
PUBLIC_HEADER DESTINATION ${INCLUDE_INSTALL_DIR}/taglib
EXPORT taglibTargets
FRAMEWORK DESTINATION ${FRAMEWORK_INSTALL_DIR}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/taglib${TAGLIB_INSTALL_SUFFIX}
)
if(NOT BUILD_FRAMEWORK)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/taglib_c.pc.cmake ${CMAKE_CURRENT_BINARY_DIR}/taglib_c.pc)
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/taglib_c.pc DESTINATION ${LIB_INSTALL_DIR}/pkgconfig)
if(IS_ABSOLUTE ${CMAKE_INSTALL_INCLUDEDIR})
set(CMAKE_PC_INCLUDEDIR ${CMAKE_INSTALL_INCLUDEDIR})
else()
set(CMAKE_PC_INCLUDEDIR "\${prefix}/${CMAKE_INSTALL_INCLUDEDIR}")
endif()
if(IS_ABSOLUTE ${CMAKE_INSTALL_LIBDIR})
set(CMAKE_PC_LIBDIR ${CMAKE_INSTALL_LIBDIR})
else()
set(CMAKE_PC_LIBDIR "\${prefix}/${CMAKE_INSTALL_LIBDIR}")
endif()
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/taglib_c.pc.cmake ${CMAKE_CURRENT_BINARY_DIR}/taglib_c.pc @ONLY)
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/taglib_c.pc DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig
RENAME taglib${TAGLIB_INSTALL_SUFFIX}_c.pc)
endif()

View File

@@ -19,28 +19,70 @@
* USA *
***************************************************************************/
#include "tag_c.h"
#include <cstdlib>
#include <cstring>
#include <sstream>
#include <utility>
#ifdef HAVE_CONFIG_H
# include <config.h>
# include "config.h"
#endif
#include <stdlib.h>
#include <fileref.h>
#include <tfile.h>
#include <asffile.h>
#include <vorbisfile.h>
#include <mpegfile.h>
#include <flacfile.h>
#include <oggflacfile.h>
#include <mpcfile.h>
#include <wavpackfile.h>
#include <speexfile.h>
#include <trueaudiofile.h>
#include <mp4file.h>
#include <tag.h>
#include <string.h>
#include <id3v2framefactory.h>
#include "tag_c.h"
#include "taglib_config.h"
#include "tstringlist.h"
#include "tbytevectorstream.h"
#include "tiostream.h"
#include "tfile.h"
#include "tpropertymap.h"
#include "fileref.h"
#include "mpegfile.h"
#include "tag.h"
#include "id3v2framefactory.h"
#ifdef TAGLIB_WITH_ASF
#include "asffile.h"
#endif
#ifdef TAGLIB_WITH_VORBIS
#include "vorbisfile.h"
#include "flacfile.h"
#include "oggflacfile.h"
#include "speexfile.h"
#include "opusfile.h"
#endif
#ifdef TAGLIB_WITH_APE
#include "mpcfile.h"
#include "wavpackfile.h"
#endif
#ifdef TAGLIB_WITH_TRUEAUDIO
#include "trueaudiofile.h"
#endif
#ifdef TAGLIB_WITH_MP4
#include "mp4file.h"
#endif
#ifdef TAGLIB_WITH_RIFF
#include "aifffile.h"
#include "wavfile.h"
#endif
#ifdef TAGLIB_WITH_APE
#include "apefile.h"
#endif
#ifdef TAGLIB_WITH_MOD
#include "itfile.h"
#include "modfile.h"
#include "s3mfile.h"
#include "xmfile.h"
#endif
#ifdef TAGLIB_WITH_DSF
#include "dsffile.h"
#include "dsdifffile.h"
#endif
#ifdef TAGLIB_WITH_SHORTEN
#include "shortenfile.h"
#endif
#ifdef TAGLIB_WITH_MATROSKA
#include "matroskafile.h"
#endif
using namespace TagLib;
@@ -69,7 +111,7 @@ namespace
{
return String(s, unicodeStrings ? String::UTF8 : String::Latin1);
}
}
} // namespace
void taglib_set_strings_unicode(BOOL unicode)
{
@@ -87,67 +129,176 @@ void taglib_free(void* pointer)
}
////////////////////////////////////////////////////////////////////////////////
// TagLib::File wrapper
// TagLib::IOStream wrapper
////////////////////////////////////////////////////////////////////////////////
TagLib_IOStream *taglib_memory_iostream_new(const char *data, unsigned int size)
{
return reinterpret_cast<TagLib_IOStream *>(
new ByteVectorStream(ByteVector(data, size)));
}
void taglib_iostream_free(TagLib_IOStream *stream)
{
delete reinterpret_cast<IOStream *>(stream);
}
////////////////////////////////////////////////////////////////////////////////
// TagLib::FileRef wrapper
////////////////////////////////////////////////////////////////////////////////
TagLib_File *taglib_file_new(const char *filename)
{
return reinterpret_cast<TagLib_File *>(FileRef::create(filename));
return reinterpret_cast<TagLib_File *>(new FileRef(filename));
}
#ifdef _WIN32
TagLib_File *taglib_file_new_wchar(const wchar_t *filename)
{
return reinterpret_cast<TagLib_File *>(new FileRef(filename));
}
#endif
template<typename T>
TagLib_File *taglib_file_new_type_any_char(const T *filename, TagLib_File_Type type)
{
File *file = NULL;
switch(type) {
case TagLib_File_MPEG:
file = new MPEG::File(filename);
break;
#ifdef TAGLIB_WITH_VORBIS
case TagLib_File_OggVorbis:
file = new Ogg::Vorbis::File(filename);
break;
case TagLib_File_FLAC:
file = new FLAC::File(filename);
break;
case TagLib_File_OggFlac:
file = new Ogg::FLAC::File(filename);
break;
case TagLib_File_Speex:
file = new Ogg::Speex::File(filename);
break;
case TagLib_File_Opus:
file = new Ogg::Opus::File(filename);
break;
#endif
#ifdef TAGLIB_WITH_APE
case TagLib_File_MPC:
file = new MPC::File(filename);
break;
case TagLib_File_WavPack:
file = new WavPack::File(filename);
break;
case TagLib_File_APE:
file = new APE::File(filename);
break;
#endif
#ifdef TAGLIB_WITH_TRUEAUDIO
case TagLib_File_TrueAudio:
file = new TrueAudio::File(filename);
break;
#endif
#ifdef TAGLIB_WITH_MP4
case TagLib_File_MP4:
file = new MP4::File(filename);
break;
#endif
#ifdef TAGLIB_WITH_ASF
case TagLib_File_ASF:
file = new ASF::File(filename);
break;
#endif
#ifdef TAGLIB_WITH_RIFF
case TagLib_File_AIFF:
file = new RIFF::AIFF::File(filename);
break;
case TagLib_File_WAV:
file = new RIFF::WAV::File(filename);
break;
#endif
#ifdef TAGLIB_WITH_MOD
case TagLib_File_IT:
file = new IT::File(filename);
break;
case TagLib_File_Mod:
file = new Mod::File(filename);
break;
case TagLib_File_S3M:
file = new S3M::File(filename);
break;
case TagLib_File_XM:
file = new XM::File(filename);
break;
#endif
#ifdef TAGLIB_WITH_DSF
case TagLib_File_DSF:
file = new DSF::File(filename);
break;
case TagLib_File_DSDIFF:
file = new DSDIFF::File(filename);
break;
#endif
#ifdef TAGLIB_WITH_SHORTEN
case TagLib_File_SHORTEN:
file = new Shorten::File(filename);
break;
#endif
#ifdef TAGLIB_WITH_MATROSKA
case TagLib_File_MATROSKA:
file = new Matroska::File(filename);
break;
#endif
default:
break;
}
return file ? reinterpret_cast<TagLib_File *>(new FileRef(file)) : NULL;
}
TagLib_File *taglib_file_new_type(const char *filename, TagLib_File_Type type)
{
switch(type) {
case TagLib_File_MPEG:
return reinterpret_cast<TagLib_File *>(new MPEG::File(filename));
case TagLib_File_OggVorbis:
return reinterpret_cast<TagLib_File *>(new Ogg::Vorbis::File(filename));
case TagLib_File_FLAC:
return reinterpret_cast<TagLib_File *>(new FLAC::File(filename));
case TagLib_File_MPC:
return reinterpret_cast<TagLib_File *>(new MPC::File(filename));
case TagLib_File_OggFlac:
return reinterpret_cast<TagLib_File *>(new Ogg::FLAC::File(filename));
case TagLib_File_WavPack:
return reinterpret_cast<TagLib_File *>(new WavPack::File(filename));
case TagLib_File_Speex:
return reinterpret_cast<TagLib_File *>(new Ogg::Speex::File(filename));
case TagLib_File_TrueAudio:
return reinterpret_cast<TagLib_File *>(new TrueAudio::File(filename));
case TagLib_File_MP4:
return reinterpret_cast<TagLib_File *>(new MP4::File(filename));
case TagLib_File_ASF:
return reinterpret_cast<TagLib_File *>(new ASF::File(filename));
default:
return 0;
}
return taglib_file_new_type_any_char(filename, type);
}
#ifdef _WIN32
TagLib_File *taglib_file_new_type_wchar(const wchar_t *filename, TagLib_File_Type type)
{
return taglib_file_new_type_any_char(filename, type);
}
#endif
TagLib_File *taglib_file_new_iostream(TagLib_IOStream *stream)
{
return reinterpret_cast<TagLib_File *>(
new FileRef(reinterpret_cast<IOStream *>(stream)));
}
void taglib_file_free(TagLib_File *file)
{
delete reinterpret_cast<File *>(file);
delete reinterpret_cast<FileRef *>(file);
}
BOOL taglib_file_is_valid(const TagLib_File *file)
{
return reinterpret_cast<const File *>(file)->isValid();
return !reinterpret_cast<const FileRef *>(file)->isNull();
}
TagLib_Tag *taglib_file_tag(const TagLib_File *file)
{
const File *f = reinterpret_cast<const File *>(file);
auto f = reinterpret_cast<const FileRef *>(file);
return reinterpret_cast<TagLib_Tag *>(f->tag());
}
const TagLib_AudioProperties *taglib_file_audioproperties(const TagLib_File *file)
{
const File *f = reinterpret_cast<const File *>(file);
auto f = reinterpret_cast<const FileRef *>(file);
return reinterpret_cast<const TagLib_AudioProperties *>(f->audioProperties());
}
BOOL taglib_file_save(TagLib_File *file)
{
return reinterpret_cast<File *>(file)->save();
return reinterpret_cast<FileRef *>(file)->save();
}
////////////////////////////////////////////////////////////////////////////////
@@ -156,7 +307,7 @@ BOOL taglib_file_save(TagLib_File *file)
char *taglib_tag_title(const TagLib_Tag *tag)
{
const Tag *t = reinterpret_cast<const Tag *>(tag);
auto t = reinterpret_cast<const Tag *>(tag);
char *s = stringToCharArray(t->title());
if(stringManagementEnabled)
strings.append(s);
@@ -165,7 +316,7 @@ char *taglib_tag_title(const TagLib_Tag *tag)
char *taglib_tag_artist(const TagLib_Tag *tag)
{
const Tag *t = reinterpret_cast<const Tag *>(tag);
auto t = reinterpret_cast<const Tag *>(tag);
char *s = stringToCharArray(t->artist());
if(stringManagementEnabled)
strings.append(s);
@@ -174,7 +325,7 @@ char *taglib_tag_artist(const TagLib_Tag *tag)
char *taglib_tag_album(const TagLib_Tag *tag)
{
const Tag *t = reinterpret_cast<const Tag *>(tag);
auto t = reinterpret_cast<const Tag *>(tag);
char *s = stringToCharArray(t->album());
if(stringManagementEnabled)
strings.append(s);
@@ -183,7 +334,7 @@ char *taglib_tag_album(const TagLib_Tag *tag)
char *taglib_tag_comment(const TagLib_Tag *tag)
{
const Tag *t = reinterpret_cast<const Tag *>(tag);
auto t = reinterpret_cast<const Tag *>(tag);
char *s = stringToCharArray(t->comment());
if(stringManagementEnabled)
strings.append(s);
@@ -192,7 +343,7 @@ char *taglib_tag_comment(const TagLib_Tag *tag)
char *taglib_tag_genre(const TagLib_Tag *tag)
{
const Tag *t = reinterpret_cast<const Tag *>(tag);
auto t = reinterpret_cast<const Tag *>(tag);
char *s = stringToCharArray(t->genre());
if(stringManagementEnabled)
strings.append(s);
@@ -201,55 +352,55 @@ char *taglib_tag_genre(const TagLib_Tag *tag)
unsigned int taglib_tag_year(const TagLib_Tag *tag)
{
const Tag *t = reinterpret_cast<const Tag *>(tag);
auto t = reinterpret_cast<const Tag *>(tag);
return t->year();
}
unsigned int taglib_tag_track(const TagLib_Tag *tag)
{
const Tag *t = reinterpret_cast<const Tag *>(tag);
auto t = reinterpret_cast<const Tag *>(tag);
return t->track();
}
void taglib_tag_set_title(TagLib_Tag *tag, const char *title)
{
Tag *t = reinterpret_cast<Tag *>(tag);
auto t = reinterpret_cast<Tag *>(tag);
t->setTitle(charArrayToString(title));
}
void taglib_tag_set_artist(TagLib_Tag *tag, const char *artist)
{
Tag *t = reinterpret_cast<Tag *>(tag);
auto t = reinterpret_cast<Tag *>(tag);
t->setArtist(charArrayToString(artist));
}
void taglib_tag_set_album(TagLib_Tag *tag, const char *album)
{
Tag *t = reinterpret_cast<Tag *>(tag);
auto t = reinterpret_cast<Tag *>(tag);
t->setAlbum(charArrayToString(album));
}
void taglib_tag_set_comment(TagLib_Tag *tag, const char *comment)
{
Tag *t = reinterpret_cast<Tag *>(tag);
auto t = reinterpret_cast<Tag *>(tag);
t->setComment(charArrayToString(comment));
}
void taglib_tag_set_genre(TagLib_Tag *tag, const char *genre)
{
Tag *t = reinterpret_cast<Tag *>(tag);
auto t = reinterpret_cast<Tag *>(tag);
t->setGenre(charArrayToString(genre));
}
void taglib_tag_set_year(TagLib_Tag *tag, unsigned int year)
{
Tag *t = reinterpret_cast<Tag *>(tag);
auto t = reinterpret_cast<Tag *>(tag);
t->setYear(year);
}
void taglib_tag_set_track(TagLib_Tag *tag, unsigned int track)
{
Tag *t = reinterpret_cast<Tag *>(tag);
auto t = reinterpret_cast<Tag *>(tag);
t->setTrack(track);
}
@@ -258,8 +409,8 @@ void taglib_tag_free_strings()
if(!stringManagementEnabled)
return;
for(List<char *>::ConstIterator it = strings.begin(); it != strings.end(); ++it)
free(*it);
for(auto &string : std::as_const(strings))
free(string);
strings.clear();
}
@@ -269,25 +420,25 @@ void taglib_tag_free_strings()
int taglib_audioproperties_length(const TagLib_AudioProperties *audioProperties)
{
const AudioProperties *p = reinterpret_cast<const AudioProperties *>(audioProperties);
return p->length();
auto p = reinterpret_cast<const AudioProperties *>(audioProperties);
return p->lengthInSeconds();
}
int taglib_audioproperties_bitrate(const TagLib_AudioProperties *audioProperties)
{
const AudioProperties *p = reinterpret_cast<const AudioProperties *>(audioProperties);
auto p = reinterpret_cast<const AudioProperties *>(audioProperties);
return p->bitrate();
}
int taglib_audioproperties_samplerate(const TagLib_AudioProperties *audioProperties)
{
const AudioProperties *p = reinterpret_cast<const AudioProperties *>(audioProperties);
auto p = reinterpret_cast<const AudioProperties *>(audioProperties);
return p->sampleRate();
}
int taglib_audioproperties_channels(const TagLib_AudioProperties *audioProperties)
{
const AudioProperties *p = reinterpret_cast<const AudioProperties *>(audioProperties);
auto p = reinterpret_cast<const AudioProperties *>(audioProperties);
return p->channels();
}
@@ -313,3 +464,422 @@ void taglib_id3v2_set_default_text_encoding(TagLib_ID3v2_Encoding encoding)
ID3v2::FrameFactory::instance()->setDefaultTextEncoding(type);
}
/******************************************************************************
* Properties API
******************************************************************************/
namespace {
void _taglib_property_set(TagLib_File *file, const char* prop, const char* value, bool append)
{
if(file == NULL || prop == NULL)
return;
auto tfile = reinterpret_cast<FileRef *>(file);
auto propStr = charArrayToString(prop);
PropertyMap map = tfile->tag()->properties();
if(value) {
auto property = map.find(propStr);
if(property == map.end()) {
map.insert(propStr, StringList(charArrayToString(value)));
}
else {
if(append) {
property->second.append(charArrayToString(value));
}
else {
property->second = StringList(charArrayToString(value));
}
}
}
else {
map.erase(propStr);
}
tfile->setProperties(map);
}
} // namespace
void taglib_property_set(TagLib_File *file, const char *prop, const char *value)
{
_taglib_property_set(file, prop, value, false);
}
void taglib_property_set_append(TagLib_File *file, const char *prop, const char *value)
{
_taglib_property_set(file, prop, value, true);
}
char** taglib_property_keys(const TagLib_File *file)
{
if(file == NULL)
return NULL;
const PropertyMap map = reinterpret_cast<const FileRef *>(file)->properties();
if(map.isEmpty())
return NULL;
auto props = static_cast<char **>(malloc(sizeof(char *) * (map.size() + 1)));
char **pp = props;
for(const auto &i : map) {
*pp++ = stringToCharArray(i.first);
}
*pp = NULL;
return props;
}
char **taglib_property_get(const TagLib_File *file, const char *prop)
{
if(file == NULL || prop == NULL)
return NULL;
const PropertyMap map = reinterpret_cast<const FileRef *>(file)->properties();
auto property = map.find(charArrayToString(prop));
if(property == map.end())
return NULL;
auto props = static_cast<char **>(malloc(sizeof(char *) * (property->second.size() + 1)));
char **pp = props;
for(const auto &i : property->second) {
*pp++ = stringToCharArray(i);
}
*pp = NULL;
return props;
}
void taglib_property_free(char **props)
{
if(props == NULL)
return;
char **p = props;
while(*p) {
free(*p++);
}
free(props);
}
/******************************************************************************
* Complex Properties API
******************************************************************************/
namespace {
bool _taglib_complex_property_set(
TagLib_File *file, const char *key,
const TagLib_Complex_Property_Attribute **value, bool append)
{
if(file == NULL || key == NULL)
return false;
auto tfile = reinterpret_cast<FileRef *>(file);
auto keyStr = charArrayToString(key);
if(value == NULL) {
return tfile->setComplexProperties(keyStr, {});
}
VariantMap map;
const TagLib_Complex_Property_Attribute** attrPtr = value;
while(*attrPtr) {
const TagLib_Complex_Property_Attribute *attr = *attrPtr;
auto attrKey = charArrayToString(attr->key);
switch(attr->value.type) {
case TagLib_Variant_Void:
map.insert(attrKey, Variant());
break;
case TagLib_Variant_Bool:
map.insert(attrKey, attr->value.value.boolValue != 0);
break;
case TagLib_Variant_Int:
map.insert(attrKey, attr->value.value.intValue);
break;
case TagLib_Variant_UInt:
map.insert(attrKey, attr->value.value.uIntValue);
break;
case TagLib_Variant_LongLong:
map.insert(attrKey, attr->value.value.longLongValue);
break;
case TagLib_Variant_ULongLong:
map.insert(attrKey, attr->value.value.uLongLongValue);
break;
case TagLib_Variant_Double:
map.insert(attrKey, attr->value.value.doubleValue);
break;
case TagLib_Variant_String:
map.insert(attrKey, charArrayToString(attr->value.value.stringValue));
break;
case TagLib_Variant_StringList: {
StringList strs;
if(attr->value.value.stringListValue) {
char **s = attr->value.value.stringListValue;;
while(*s) {
strs.append(charArrayToString(*s++));
}
}
map.insert(attrKey, strs);
break;
}
case TagLib_Variant_ByteVector:
map.insert(attrKey, ByteVector(attr->value.value.byteVectorValue,
attr->value.size));
break;
}
++attrPtr;
}
return append ? tfile->setComplexProperties(keyStr, tfile->complexProperties(keyStr).append(map))
: tfile->setComplexProperties(keyStr, {map});
}
} // namespace
BOOL taglib_complex_property_set(
TagLib_File *file, const char *key,
const TagLib_Complex_Property_Attribute **value)
{
return _taglib_complex_property_set(file, key, value, false);
}
BOOL taglib_complex_property_set_append(
TagLib_File *file, const char *key,
const TagLib_Complex_Property_Attribute **value)
{
return _taglib_complex_property_set(file, key, value, true);
}
char** taglib_complex_property_keys(const TagLib_File *file)
{
if(file == NULL) {
return NULL;
}
const StringList strs = reinterpret_cast<const FileRef *>(file)->complexPropertyKeys();
if(strs.isEmpty()) {
return NULL;
}
auto keys = static_cast<char **>(malloc(sizeof(char *) * (strs.size() + 1)));
char **keyPtr = keys;
for(const auto &str : strs) {
*keyPtr++ = stringToCharArray(str);
}
*keyPtr = NULL;
return keys;
}
TagLib_Complex_Property_Attribute*** taglib_complex_property_get(
const TagLib_File *file, const char *key)
{
if(file == NULL || key == NULL) {
return NULL;
}
const auto variantMaps = reinterpret_cast<const FileRef *>(file)->complexProperties(charArrayToString(key));
if(variantMaps.isEmpty()) {
return NULL;
}
auto props = static_cast<TagLib_Complex_Property_Attribute ***>(
malloc(sizeof(TagLib_Complex_Property_Attribute **) * (variantMaps.size() + 1)));
TagLib_Complex_Property_Attribute ***propPtr = props;
for(const auto &variantMap : variantMaps) {
if(!variantMap.isEmpty()) {
auto attrs = static_cast<TagLib_Complex_Property_Attribute **>(
malloc(sizeof(TagLib_Complex_Property_Attribute *) * (variantMap.size() + 1)));
auto attr = static_cast<TagLib_Complex_Property_Attribute *>(
malloc(sizeof(TagLib_Complex_Property_Attribute) * variantMap.size()));
TagLib_Complex_Property_Attribute **attrPtr = attrs;
// The next assignment is redundant to silence the clang analyzer,
// it is done at the end of the loop, which must be entered because
// variantMap is not empty.
*attrPtr = attr;
for(const auto &[k, v] : variantMap) {
attr->key = stringToCharArray(k);
attr->value.size = 0;
switch(v.type()) {
case Variant::Void:
attr->value.type = TagLib_Variant_Void;
attr->value.value.stringValue = NULL;
break;
case Variant::Bool:
attr->value.type = TagLib_Variant_Bool;
attr->value.value.boolValue = v.value<bool>();
break;
case Variant::Int:
attr->value.type = TagLib_Variant_Int;
attr->value.value.intValue = v.value<int>();
break;
case Variant::UInt:
attr->value.type = TagLib_Variant_UInt;
attr->value.value.uIntValue = v.value<unsigned int>();
break;
case Variant::LongLong:
attr->value.type = TagLib_Variant_LongLong;
attr->value.value.longLongValue = v.value<long long>();
break;
case Variant::ULongLong:
attr->value.type = TagLib_Variant_ULongLong;
attr->value.value.uLongLongValue = v.value<unsigned long long>();
break;
case Variant::Double:
attr->value.type = TagLib_Variant_Double;
attr->value.value.doubleValue = v.value<double>();
break;
case Variant::String: {
attr->value.type = TagLib_Variant_String;
auto str = v.value<String>();
attr->value.value.stringValue = stringToCharArray(str);
attr->value.size = str.size();
break;
}
case Variant::StringList: {
attr->value.type = TagLib_Variant_StringList;
auto strs = v.value<StringList>();
auto strPtr = static_cast<char **>(malloc(sizeof(char *) * (strs.size() + 1)));
attr->value.value.stringListValue = strPtr;
attr->value.size = strs.size();
for(const auto &str : strs) {
*strPtr++ = stringToCharArray(str);
}
*strPtr = NULL;
break;
}
case Variant::ByteVector: {
attr->value.type = TagLib_Variant_ByteVector;
const ByteVector data = v.value<ByteVector>();
auto bytePtr = static_cast<char *>(malloc(data.size()));
attr->value.value.byteVectorValue = bytePtr;
attr->value.size = data.size();
::memcpy(bytePtr, data.data(), data.size());
break;
}
case Variant::ByteVectorList:
case Variant::VariantList:
case Variant::VariantMap: {
attr->value.type = TagLib_Variant_String;
std::stringstream ss;
ss << v;
attr->value.value.stringValue = stringToCharArray(ss.str());
break;
}
}
*attrPtr++ = attr++;
}
*attrPtr = NULL;
*propPtr++ = attrs;
}
}
*propPtr = NULL;
return props;
}
void taglib_picture_from_complex_property(
TagLib_Complex_Property_Attribute*** properties,
TagLib_Complex_Property_Picture_Data *picture)
{
if(!properties || !picture) {
return;
}
std::memset(picture, 0, sizeof(*picture));
TagLib_Complex_Property_Attribute*** propPtr = properties;
while(!picture->data && *propPtr) {
TagLib_Complex_Property_Attribute** attrPtr = *propPtr;
while(*attrPtr) {
TagLib_Complex_Property_Attribute *attr = *attrPtr;
switch(attr->value.type) {
case TagLib_Variant_String:
if(strcmp("mimeType", attr->key) == 0) {
picture->mimeType = attr->value.value.stringValue;
}
else if(strcmp("description", attr->key) == 0) {
picture->description = attr->value.value.stringValue;
}
else if(strcmp("pictureType", attr->key) == 0) {
picture->pictureType = attr->value.value.stringValue;
}
break;
case TagLib_Variant_ByteVector:
if(strcmp("data", attr->key) == 0) {
picture->data = attr->value.value.byteVectorValue;
picture->size = attr->value.size;
}
break;
default:
break;
}
++attrPtr;
}
++propPtr;
}
}
void taglib_complex_property_free_keys(char **keys)
{
if(keys == NULL) {
return;
}
char **k = keys;
while(*k) {
free(*k++);
}
free(keys);
}
void taglib_complex_property_free(
TagLib_Complex_Property_Attribute ***props)
{
if(props == NULL) {
return;
}
TagLib_Complex_Property_Attribute*** propPtr = props;
while(*propPtr) {
TagLib_Complex_Property_Attribute** attrPtr = *propPtr;
while(*attrPtr) {
TagLib_Complex_Property_Attribute *attr = *attrPtr;
switch(attr->value.type) {
case TagLib_Variant_String:
free(attr->value.value.stringValue);
break;
case TagLib_Variant_StringList:
if(attr->value.value.stringListValue) {
char **s = attr->value.value.stringListValue;
while(*s) {
free(*s++);
}
free(attr->value.value.stringListValue);
}
break;
case TagLib_Variant_ByteVector:
free(attr->value.value.byteVectorValue);
break;
case TagLib_Variant_Void:
case TagLib_Variant_Bool:
case TagLib_Variant_Int:
case TagLib_Variant_UInt:
case TagLib_Variant_LongLong:
case TagLib_Variant_ULongLong:
case TagLib_Variant_Double:
break;
}
free(attr->key);
++attrPtr;
}
free(**propPtr);
free(*propPtr++);
}
free(props);
}

View File

@@ -43,7 +43,11 @@ extern "C" {
#define TAGLIB_C_EXPORT
#endif
#ifndef BOOL
#include <wchar.h>
#ifdef _MSC_VER
/* minwindef.h contains typedef int BOOL */
#include <windows.h>
#elif !defined BOOL
#define BOOL int
#endif
@@ -57,14 +61,15 @@ extern "C" {
*******************************************************************************/
/*
* These are used for type provide some type safety to the C API (as opposed to
* using void *, but pointers to them are simply cast to the corresponding C++
* These are used to give the C API some type safety (as opposed to
* using void * ), but pointers to them are simply cast to the corresponding C++
* types in the implementation.
*/
typedef struct { int dummy; } TagLib_File;
typedef struct { int dummy; } TagLib_Tag;
typedef struct { int dummy; } TagLib_AudioProperties;
typedef struct { int dummy; } TagLib_IOStream;
/*!
* By default all strings coming into or out of TagLib's C API are in UTF8.
@@ -86,6 +91,22 @@ TAGLIB_C_EXPORT void taglib_set_string_management_enabled(BOOL management);
*/
TAGLIB_C_EXPORT void taglib_free(void* pointer);
/*******************************************************************************
* Stream API
******************************************************************************/
/*!
* Creates a byte vector stream from \a size bytes of \a data.
* Such a stream can be used with taglib_file_new_iostream() to create a file
* from memory.
*/
TAGLIB_C_EXPORT TagLib_IOStream *taglib_memory_iostream_new(const char *data, unsigned int size);
/*!
* Frees and closes the stream.
*/
TAGLIB_C_EXPORT void taglib_iostream_free(TagLib_IOStream *stream);
/*******************************************************************************
* File API
******************************************************************************/
@@ -100,7 +121,19 @@ typedef enum {
TagLib_File_Speex,
TagLib_File_TrueAudio,
TagLib_File_MP4,
TagLib_File_ASF
TagLib_File_ASF,
TagLib_File_AIFF,
TagLib_File_WAV,
TagLib_File_APE,
TagLib_File_IT,
TagLib_File_Mod,
TagLib_File_S3M,
TagLib_File_XM,
TagLib_File_Opus,
TagLib_File_DSF,
TagLib_File_DSDIFF,
TagLib_File_SHORTEN,
TagLib_File_MATROSKA
} TagLib_File_Type;
/*!
@@ -111,12 +144,28 @@ typedef enum {
* be opened.
*/
TAGLIB_C_EXPORT TagLib_File *taglib_file_new(const char *filename);
#ifdef _WIN32
TAGLIB_C_EXPORT TagLib_File *taglib_file_new_wchar(const wchar_t *filename);
#endif
/*!
* Creates a TagLib file based on \a filename. Rather than attempting to guess
* the type, it will use the one specified by \a type.
*/
TAGLIB_C_EXPORT TagLib_File *taglib_file_new_type(const char *filename, TagLib_File_Type type);
#ifdef _WIN32
TAGLIB_C_EXPORT TagLib_File *taglib_file_new_type_wchar(const wchar_t *filename, TagLib_File_Type type);
#endif
/*!
* Creates a TagLib file from a \a stream.
* A byte vector stream can be used to read a file from memory and write to
* memory, e.g. when fetching network data.
* The stream has to be created using taglib_memory_iostream_new() and shall be
* freed using taglib_iostream_free() \e after this file is freed with
* taglib_file_free().
*/
TAGLIB_C_EXPORT TagLib_File *taglib_file_new_iostream(TagLib_IOStream *stream);
/*!
* Frees and closes the file.
@@ -124,7 +173,7 @@ TAGLIB_C_EXPORT TagLib_File *taglib_file_new_type(const char *filename, TagLib_F
TAGLIB_C_EXPORT void taglib_file_free(TagLib_File *file);
/*!
* Returns true if the file is open and readble and valid information for
* Returns \c true if the file is open and readable and valid information for
* the Tag and / or AudioProperties was found.
*/
@@ -137,7 +186,7 @@ TAGLIB_C_EXPORT BOOL taglib_file_is_valid(const TagLib_File *file);
TAGLIB_C_EXPORT TagLib_Tag *taglib_file_tag(const TagLib_File *file);
/*!
* Returns a pointer to the the audio properties associated with this file. This
* Returns a pointer to the audio properties associated with this file. This
* will be freed automatically when the file is freed.
*/
TAGLIB_C_EXPORT const TagLib_AudioProperties *taglib_file_audioproperties(const TagLib_File *file);
@@ -192,12 +241,12 @@ TAGLIB_C_EXPORT char *taglib_tag_comment(const TagLib_Tag *tag);
TAGLIB_C_EXPORT char *taglib_tag_genre(const TagLib_Tag *tag);
/*!
* Returns the tag's year or 0 if year is not set.
* Returns the tag's year or 0 if the year is not set.
*/
TAGLIB_C_EXPORT unsigned int taglib_tag_year(const TagLib_Tag *tag);
/*!
* Returns the tag's track number or 0 if track number is not set.
* Returns the tag's track number or 0 if the track number is not set.
*/
TAGLIB_C_EXPORT unsigned int taglib_tag_track(const TagLib_Tag *tag);
@@ -292,6 +341,275 @@ typedef enum {
TAGLIB_C_EXPORT void taglib_id3v2_set_default_text_encoding(TagLib_ID3v2_Encoding encoding);
/******************************************************************************
* Properties API
******************************************************************************/
/*!
* Sets the property \a prop with \a value. Use \a value = NULL to remove
* the property, otherwise it will be replaced.
*/
TAGLIB_C_EXPORT void taglib_property_set(TagLib_File *file, const char *prop, const char *value);
/*!
* Appends \a value to the property \a prop (sets it if non-existing).
* Use \a value = NULL to remove all values associated with the property.
*/
TAGLIB_C_EXPORT void taglib_property_set_append(TagLib_File *file, const char *prop, const char *value);
/*!
* Get the keys of the property map.
*
* \return NULL terminated array of C-strings (char *), only NULL if empty.
* It must be freed by the client using taglib_property_free().
*/
TAGLIB_C_EXPORT char** taglib_property_keys(const TagLib_File *file);
/*!
* Get value(s) of property \a prop.
*
* \return NULL terminated array of C-strings (char *), only NULL if empty.
* It must be freed by the client using taglib_property_free().
*/
TAGLIB_C_EXPORT char** taglib_property_get(const TagLib_File *file, const char *prop);
/*!
* Frees the NULL terminated array \a props and the C-strings it contains.
*/
TAGLIB_C_EXPORT void taglib_property_free(char **props);
/******************************************************************************
* Complex Properties API
******************************************************************************/
/*!
* Types which can be stored in a TagLib_Variant.
*
* \related TagLib::Variant::Type
* These correspond to TagLib::Variant::Type, but ByteVectorList, VariantList,
* VariantMap are not supported and will be returned as their string
* representation.
*/
typedef enum {
TagLib_Variant_Void,
TagLib_Variant_Bool,
TagLib_Variant_Int,
TagLib_Variant_UInt,
TagLib_Variant_LongLong,
TagLib_Variant_ULongLong,
TagLib_Variant_Double,
TagLib_Variant_String,
TagLib_Variant_StringList,
TagLib_Variant_ByteVector
} TagLib_Variant_Type;
/*!
* Discriminated union used in complex property attributes.
*
* \e type must be set according to the \e value union used.
* \e size is only required for TagLib_Variant_ByteVector and must contain
* the number of bytes.
*
* \related TagLib::Variant.
*/
typedef struct {
TagLib_Variant_Type type;
unsigned int size;
union {
char *stringValue;
char *byteVectorValue;
char **stringListValue;
BOOL boolValue;
int intValue;
unsigned int uIntValue;
long long longLongValue;
unsigned long long uLongLongValue;
double doubleValue;
} value;
} TagLib_Variant;
/*!
* Attribute of a complex property.
* Complex properties consist of a NULL-terminated array of pointers to
* this structure with \e key and \e value.
*/
typedef struct {
char *key;
TagLib_Variant value;
} TagLib_Complex_Property_Attribute;
/*!
* Picture data extracted from a complex property by the convenience function
* taglib_picture_from_complex_property().
*/
typedef struct {
char *mimeType;
char *description;
char *pictureType;
char *data;
unsigned int size;
} TagLib_Complex_Property_Picture_Data;
/*!
* Declare complex property attributes to set a picture.
* Can be used to define a variable \a var which can be used with
* taglib_complex_property_set() and a "PICTURE" key to set an
* embedded picture with the picture data \a dat of size \a siz
* and description \a desc, mime type \a mime and picture type
* \a typ (size is unsigned int, the other input parameters char *).
*/
#define TAGLIB_COMPLEX_PROPERTY_PICTURE(var, dat, siz, desc, mime, typ) \
const TagLib_Complex_Property_Attribute \
var##Attrs[] = { \
{ \
(char *)"data", \
{ \
TagLib_Variant_ByteVector, \
(siz), \
{ \
(char *)(dat) \
} \
} \
}, \
{ \
(char *)"mimeType", \
{ \
TagLib_Variant_String, \
0U, \
{ \
(char *)(mime) \
} \
} \
}, \
{ \
(char *)"description", \
{ \
TagLib_Variant_String, \
0U, \
{ \
(char *)(desc) \
} \
} \
}, \
{ \
(char *)"pictureType", \
{ \
TagLib_Variant_String, \
0U, \
{ \
(char *)(typ) \
} \
} \
} \
}; \
const TagLib_Complex_Property_Attribute *var[] = { \
&var##Attrs[0], &var##Attrs[1], &var##Attrs[2], \
&var##Attrs[3], NULL \
}
/*!
* Sets the complex property \a key with \a value. Use \a value = NULL to
* remove the property, otherwise it will be replaced with the NULL
* terminated array of attributes in \a value.
*
* A picture can be set with the TAGLIB_COMPLEX_PROPERTY_PICTURE macro:
*
* \code {.c}
* TagLib_File *file = taglib_file_new("myfile.mp3");
* FILE *fh = fopen("mypicture.jpg", "rb");
* if(fh) {
* fseek(fh, 0L, SEEK_END);
* long size = ftell(fh);
* fseek(fh, 0L, SEEK_SET);
* char *data = (char *)malloc(size);
* fread(data, size, 1, fh);
* TAGLIB_COMPLEX_PROPERTY_PICTURE(props, data, size, "Written by TagLib",
* "image/jpeg", "Front Cover");
* taglib_complex_property_set(file, "PICTURE", props);
* taglib_file_save(file);
* free(data);
* fclose(fh);
* }
* \endcode
*/
TAGLIB_C_EXPORT BOOL taglib_complex_property_set(
TagLib_File *file, const char *key,
const TagLib_Complex_Property_Attribute **value);
/*!
* Appends \a value to the complex property \a key (sets it if non-existing).
* Use \a value = NULL to remove all values associated with the \a key.
*/
TAGLIB_C_EXPORT BOOL taglib_complex_property_set_append(
TagLib_File *file, const char *key,
const TagLib_Complex_Property_Attribute **value);
/*!
* Get the keys of the complex properties.
*
* \return NULL terminated array of C-strings (char *), only NULL if empty.
* It must be freed by the client using taglib_complex_property_free_keys().
*/
TAGLIB_C_EXPORT char** taglib_complex_property_keys(const TagLib_File *file);
/*!
* Get value(s) of complex property \a key.
*
* \return NULL terminated array of property values, which are themselves an
* array of property attributes, only NULL if empty.
* It must be freed by the client using taglib_complex_property_free().
*/
TAGLIB_C_EXPORT TagLib_Complex_Property_Attribute*** taglib_complex_property_get(
const TagLib_File *file, const char *key);
/*!
* Extract the complex property values of a picture.
*
* This function can be used to get the data from a "PICTURE" complex property
* without having to traverse the whole variant map. A picture can be
* retrieved like this:
*
* \code {.c}
* TagLib_File *file = taglib_file_new("myfile.mp3");
* TagLib_Complex_Property_Attribute*** properties =
* taglib_complex_property_get(file, "PICTURE");
* TagLib_Complex_Property_Picture_Data picture;
* taglib_picture_from_complex_property(properties, &picture);
* // Do something with picture.mimeType, picture.description,
* // picture.pictureType, picture.data, picture.size, e.g. extract it.
* FILE *fh = fopen("mypicture.jpg", "wb");
* if(fh) {
* fwrite(picture.data, picture.size, 1, fh);
* fclose(fh);
* }
* taglib_complex_property_free(properties);
* \endcode
*
* Note that the data in \a picture contains pointers to data in \a properties,
* i.e. it only lives as long as the properties, until they are freed with
* taglib_complex_property_free().
* If you want to access multiple pictures or additional properties of FLAC
* pictures ("width", "height", "numColors", "colorDepth" int values), you
* have to traverse the \a properties yourself.
*/
TAGLIB_C_EXPORT void taglib_picture_from_complex_property(
TagLib_Complex_Property_Attribute*** properties,
TagLib_Complex_Property_Picture_Data *picture);
/*!
* Frees the NULL terminated array \a keys (as returned by
* taglib_complex_property_keys()) and the C-strings it contains.
*/
TAGLIB_C_EXPORT void taglib_complex_property_free_keys(char **keys);
/*!
* Frees the NULL terminated array \a props of property attribute arrays
* (as returned by taglib_complex_property_get()) and the data such as
* C-strings and byte vectors contained in these attributes.
*/
TAGLIB_C_EXPORT void taglib_complex_property_free(
TagLib_Complex_Property_Attribute ***props);
#ifdef __cplusplus
}
#endif

View File

@@ -1,12 +1,11 @@
prefix=${CMAKE_INSTALL_PREFIX}
exec_prefix=${CMAKE_INSTALL_PREFIX}
libdir=${LIB_INSTALL_DIR}
includedir=${INCLUDE_INSTALL_DIR}
prefix=@CMAKE_INSTALL_PREFIX@
exec_prefix=${prefix}
libdir=@CMAKE_PC_LIBDIR@
includedir=@CMAKE_PC_INCLUDEDIR@
Name: TagLib C Bindings
Description: Audio meta-data library (C bindings)
Requires: taglib
Version: ${TAGLIB_LIB_VERSION_STRING}
Libs: -L${LIB_INSTALL_DIR} -ltag_c
Cflags: -I${INCLUDE_INSTALL_DIR}/taglib
Version: @TAGLIB_LIB_VERSION_STRING@
Libs: -L${libdir} -ltag_c@TAGLIB_INSTALL_SUFFIX@
Cflags: -I${includedir}/taglib@TAGLIB_INSTALL_SUFFIX@

View File

@@ -8,7 +8,7 @@
include (MacroEnsureVersion)
if(NOT CPPUNIT_MIN_VERSION)
SET(CPPUNIT_MIN_VERSION 1.12.0)
SET(CPPUNIT_MIN_VERSION 1.14.0)
endif(NOT CPPUNIT_MIN_VERSION)
FIND_PROGRAM(CPPUNIT_CONFIG_EXECUTABLE cppunit-config )
@@ -33,7 +33,7 @@ ELSE(CPPUNIT_INCLUDE_DIR AND CPPUNIT_LIBRARIES)
FIND_PATH(CPPUNIT_CFLAGS cppunit/TestRunner.h PATHS /usr/include /usr/local/include )
FIND_LIBRARY(CPPUNIT_LIBRARIES NAMES cppunit PATHS /usr/lib /usr/local/lib )
# how can we find cppunit version?
MESSAGE (STATUS "Ensure you cppunit installed version is at least ${CPPUNIT_MIN_VERSION}")
MESSAGE (STATUS "Ensure your cppunit installed version is at least ${CPPUNIT_MIN_VERSION}")
SET (CPPUNIT_INSTALLED_VERSION ${CPPUNIT_MIN_VERSION})
ENDIF(CPPUNIT_CONFIG_EXECUTABLE)
@@ -56,7 +56,7 @@ IF(CPPUNIT_INCLUDE_DIR AND CPPUNIT_LIBRARIES)
macro_ensure_version( ${CPPUNIT_MIN_VERSION} ${CPPUNIT_INSTALLED_VERSION} CPPUNIT_INSTALLED_VERSION_OK )
IF(NOT CPPUNIT_INSTALLED_VERSION_OK)
MESSAGE ("** CppUnit version is too old: found ${CPPUNIT_INSTALLED_VERSION} installed, ${CPPUNIT_MIN_VERSION} or major is required")
MESSAGE ("** CppUnit version is too old: found ${CPPUNIT_INSTALLED_VERSION} installed, ${CPPUNIT_MIN_VERSION} or newer is required")
SET(CppUnit_FOUND FALSE)
ENDIF(NOT CPPUNIT_INSTALLED_VERSION_OK)

View File

@@ -1,33 +1,25 @@
/* config.h. Generated by cmake from config.h.cmake */
#ifndef TAGLIB_CONFIG_H
#define TAGLIB_CONFIG_H
/* Defined if your compiler supports some byte swap functions */
#cmakedefine HAVE_BOOST_BYTESWAP 1
#cmakedefine HAVE_GCC_BYTESWAP 1
#cmakedefine HAVE_GLIBC_BYTESWAP 1
#cmakedefine HAVE_MSC_BYTESWAP 1
#cmakedefine HAVE_MAC_BYTESWAP 1
#cmakedefine HAVE_OPENBSD_BYTESWAP 1
/* Defined if your compiler supports some atomic operations */
#cmakedefine HAVE_STD_ATOMIC 1
#cmakedefine HAVE_BOOST_ATOMIC 1
#cmakedefine HAVE_GCC_ATOMIC 1
#cmakedefine HAVE_MAC_ATOMIC 1
#cmakedefine HAVE_WIN_ATOMIC 1
#cmakedefine HAVE_IA64_ATOMIC 1
/* Defined if your compiler supports some safer version of vsprintf */
#cmakedefine HAVE_VSNPRINTF 1
#cmakedefine HAVE_VSPRINTF_S 1
/* Defined if your compiler supports ISO _strdup */
#cmakedefine HAVE_ISO_STRDUP 1
/* Defined if zlib is installed */
#cmakedefine HAVE_ZLIB 1
#cmakedefine HAVE_BOOST_ZLIB 1
/* Indicates whether debug messages are shown even in release mode */
#cmakedefine TRACE_IN_RELEASE 1
#cmakedefine TESTS_DIR "@TESTS_DIR@"
#cmakedefine TESTS_TMPDIR "@TESTS_TMPDIR@"
#endif

View File

@@ -1,4 +1,2 @@
</div>
</div>
</body>
</html>

View File

@@ -1,41 +1,38 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=9"/>
<meta name="generator" content="Doxygen $doxygenversion"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<title>$title ($projectname)</title>
<link href="taglib-api.css" rel="stylesheet" type="text/css">
<link href="$relpath^tabs.css" rel="stylesheet" type="text/css"/>
<script type="text/javascript" src="$relpath^jquery.js"></script>
<script type="text/javascript" src="$relpath^dynsections.js"></script>
$treeview
$search
$mathjax
<link href="$relpath^$stylesheet" rel="stylesheet" type="text/css" />
$extrastylesheet
</head>
<body>
<div id="container">
<table border="0" width="100%">
<tr>
<td width="1">
<img src="../taglib.png">
</td>
<td>
<div id="intro">
<table border="0" height="119" cellpadding="0" cellspacing="0" width="100%">
<tr><td valign="top"><h1>TagLib $projectnumber ($title)</h1></td></tr>
<tr>
<td valign="bottom">
<div id="links">
<a href="index.html">Home</a>
<a href="inherits.html">Class&nbsp;Hierarchy</a>
<a href="namespaces.html">Namespaces</a>
<a href="annotated.html">Classes</a>
<a href="files.html">Headers</a>
<a href="namespacemembers.html">Namespace&nbsp;Members</a>
<a href="functions.html">Class&nbsp;Members</a>
<a href="globals.html">File&nbsp;Members</a>
</div>
</td>
</tr>
</table>
</div>
</td>
</tr>
</table>
<div id="text">
<div id="top">
<div id="titlearea">
<table cellspacing="0" cellpadding="0">
<tbody>
<tr style="height: 56px;">
<td id="projectlogo"><img alt="Logo" src="$relpath^$projectlogo" width="200px"/></td>
<td id="projectalign" style="padding-left: 0.5em;">
<div id="projectname">$projectname
&#160;<span id="projectnumber">$projectnumber</span>
</div>
<div id="projectbrief">$projectbrief</div>
</td>
<td style="padding-left: 0.5em;">
<div id="projectbrief">$projectbrief</div>
</td>
<td>$searchbox</td>
</tr>
</tbody>
</table>
</div>

View File

@@ -1,395 +0,0 @@
body {
font-family: sans-serif;
background: white;
color: black;
margin: 0px;
padding: 15px;
}
a:link {
font-weight: bold;
text-decoration: none;
color: gray;
}
a:visited {
font-weight: bold;
text-decoration: none;
color: gray;
}
a:hover {
color: #cccccc;
text-decoration: underline;
}
a:active {
color: #cccccc;
text-decoration: underline;
}
img {
border-style: none;
}
h1 {
font-family: sans-serif;
}
h2 {
font-family: sans-serif;
}
h3 {
font-family: sans-serif;
}
/* container */
#container {
position: absolute;
border-width: thin;
border-style: solid;
width: 95%;
}
/* intro */
#intro {
padding: 5px;
margin: 0px;
background: #cccccc;
border-width: medium;
border-style: solid;
}
#intro h1 {
margin: 5px;
padding: 5px;
}
/* links */
#links {
font-size: x-small;
vertical-align: bottom;
}
#links a {
border-width: thin;
border-style: dotted;
border-color: white;
/* margin: 0px 10px 0px 0px; */
margin: 1px;
padding: 3px;
line-height: 230%
}
#links a:hover {
color: black;
text-decoration: underline;
}
#links h3 {
outline-width: thin;
border-style: solid;
padding: 2px;
margin: 3px 0px 3px 0px;
}
/* menu */
#menu h3 {
text-align: center;
}
/* text */
#text {
margin: 0px;
padding: 5px 5px 0px 5px;
float: left;
}
#text h3 {
border-width: thin;
border-style: solid;
padding: 2px;
margin: 3px 0px 3px 0px;
}
#text li {
margin: 0px 0px 10px 0px;
}
#text ul {
margin: 5px;
padding: 0px 0px 0px 20px;
}
#leftcolumn {
float: left;
width: 300px;
margin: 0px 10px 0px 0px;
padding: 0px;
}
#rightcolumn {
float: right;
width: 210px;
margin: 0px;
padding: 0px;
}
/* vspacer */
.vspacer {
height: 10px;
}
.silver {
border-width: thin;
border-color: black;
border-style: solid;
background: #cccccc;
}
a.code {
text-decoration: none;
font-weight: normal;
color: #4444ee
}
a.codeRef {
font-weight: normal;
color: #4444ee
}
div.fragment {
width: 98%;
border: 1px solid #CCCCCC;
background-color: #f5f5f5;
padding-left: 4px;
margin: 4px;
}
div.ah {
background-color: black;
font-weight: bold; color: #ffffff;
margin-bottom: 3px;
margin-top: 3px
}
#text td {
width: auto;
}
div.memdoc {
margin-top: 0px;
margin-bottom: 20px;
padding: 10px 10px 10px 40px;
}
div.memproto {
border: thin solid black;
background-color: #f2f2ff;
width: 100%;
margin-top: 20px;
padding-top: 10px;
padding-bottom: 10px;
}
td.paramtype {
color: #602020;
}
table.memname {
font-weight: bold;
}
div.groupHeader {
margin-left: 16px;
margin-top: 12px;
margin-bottom: 6px;
font-weight: bold
}
div.groupText {
margin-left: 16px;
font-style: italic;
font-size: smaller
}
body {
background: white;
color: black;
margin-right: 20px;
margin-left: 20px;
}
td.indexkey {
background-color: #eeeeff;
font-weight: bold;
padding-right : 10px;
padding-top : 2px;
padding-left : 10px;
padding-bottom : 2px;
margin-left : 0px;
margin-right : 0px;
margin-top : 2px;
margin-bottom : 2px
}
td.indexvalue {
background-color: #eeeeff;
font-style: italic;
padding-right : 10px;
padding-top : 2px;
padding-left : 10px;
padding-bottom : 2px;
margin-left : 0px;
margin-right : 0px;
margin-top : 2px;
margin-bottom : 2px
}
tr.memlist {
background-color: #f0f0f0;
}
p.formulaDsp {
text-align: center;
}
img.formulaDsp {
}
img.formulaInl {
vertical-align: middle;
}
span.keyword {
color: #008000
}
span.keywordtype {
color: #604020
}
span.keywordflow {
color: #e08000
}
span.comment {
color: #800000
}
span.preprocessor {
color: #806020
}
span.stringliteral {
color: #002080
}
span.charliteral {
color: #008080
}
.mdTable {
border: 1px solid #868686;
background-color: #f2f2ff;
}
.mdRow {
padding: 8px 20px;
}
.mdescLeft {
font-size: smaller;
font-family: Arial, Helvetica, sans-serif;
background-color: #FAFAFA;
padding-left: 8px;
border-top: 1px none #E0E0E0;
border-right: 1px none #E0E0E0;
border-bottom: 1px none #E0E0E0;
border-left: 1px none #E0E0E0;
margin: 0px;
}
.mdescRight {
font-size: smaller;
font-family: Arial, Helvetica, sans-serif;
font-style: italic;
background-color: #FAFAFA;
padding-left: 4px;
border-top: 1px none #E0E0E0;
border-right: 1px none #E0E0E0;
border-bottom: 1px none #E0E0E0;
border-left: 1px none #E0E0E0;
margin: 0px;
padding-bottom: 0px;
padding-right: 8px;
}
.memItemLeft {
padding: 1px 0px 0px 8px;
margin: 4px;
border-top-width: 1px;
border-right-width: 1px;
border-bottom-width: 1px;
border-left-width: 1px;
border-top-style: solid;
border-top-color: #E0E0E0;
border-right-color: #E0E0E0;
border-bottom-color: #E0E0E0;
border-left-color: #E0E0E0;
border-right-style: none;
border-bottom-style: none;
border-left-style: none;
background-color: #FAFAFA;
font-family: Geneva, Arial, Helvetica, sans-serif;
font-size: 12px;
}
.memItemRight {
padding: 1px 0px 0px 8px;
margin: 4px;
border-top-width: 1px;
border-right-width: 1px;
border-bottom-width: 1px;
border-left-width: 1px;
border-top-style: solid;
border-top-color: #E0E0E0;
border-right-color: #E0E0E0;
border-bottom-color: #E0E0E0;
border-left-color: #E0E0E0;
border-right-style: none;
border-bottom-style: none;
border-left-style: none;
background-color: #FAFAFA;
font-family: Geneva, Arial, Helvetica, sans-serif;
font-size: 13px;
}
.search {
color: #0000ee;
font-weight: bold;
}
form.search {
margin-bottom: 0px;
margin-top: 0px;
}
input.search {
font-size: 75%;
color: #000080;
font-weight: normal;
background-color: #eeeeff;
}
td.tiny {
font-size: 75%;
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

58
doc/taglib.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 11 KiB

View File

@@ -2,9 +2,11 @@ include_directories(
${CMAKE_CURRENT_SOURCE_DIR}/../taglib
${CMAKE_CURRENT_SOURCE_DIR}/../taglib/toolkit
${CMAKE_CURRENT_SOURCE_DIR}/../taglib/ape
${CMAKE_CURRENT_SOURCE_DIR}/../taglib/matroska
${CMAKE_CURRENT_SOURCE_DIR}/../taglib/mpeg
${CMAKE_CURRENT_SOURCE_DIR}/../taglib/mpeg/id3v1
${CMAKE_CURRENT_SOURCE_DIR}/../taglib/mpeg/id3v2
${CMAKE_CURRENT_SOURCE_DIR}/../taglib/mpeg/id3v2/frames
${CMAKE_CURRENT_SOURCE_DIR}/../bindings/c/
)
@@ -37,3 +39,20 @@ target_link_libraries(framelist tag)
add_executable(strip-id3v1 strip-id3v1.cpp)
target_link_libraries(strip-id3v1 tag)
if(WITH_MATROSKA)
########### next target ###############
add_executable(matroskareader matroskareader.cpp)
target_link_libraries(matroskareader tag)
########### next target ###############
add_executable(matroskawriter matroskawriter.cpp)
target_link_libraries(matroskawriter tag)
endif()
install(TARGETS tagreader tagreader_c tagwriter framelist strip-id3v1
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
)

View File

@@ -23,21 +23,20 @@
*/
#include <iostream>
#include <stdlib.h>
#include <cstdlib>
#include <tbytevector.h>
#include "taglib_config.h"
#include "tbytevector.h"
#include "mpegfile.h"
#include "id3v2tag.h"
#include "id3v2frame.h"
#include "id3v2header.h"
#include "commentsframe.h"
#include "id3v1tag.h"
#ifdef TAGLIB_WITH_APE
#include "apetag.h"
#endif
#include <mpegfile.h>
#include <id3v2tag.h>
#include <id3v2frame.h>
#include <id3v2header.h>
#include <id3v1tag.h>
#include <apetag.h>
using namespace std;
using namespace TagLib;
int main(int argc, char *argv[])
@@ -47,7 +46,7 @@ int main(int argc, char *argv[])
for(int i = 1; i < argc; i++) {
cout << "******************** \"" << argv[i] << "\"********************" << endl;
std::cout << "******************** \"" << argv[i] << "\"********************" << std::endl;
MPEG::File f(argv[i]);
@@ -55,55 +54,64 @@ int main(int argc, char *argv[])
if(id3v2tag) {
cout << "ID3v2."
std::cout << "ID3v2."
<< id3v2tag->header()->majorVersion()
<< "."
<< id3v2tag->header()->revisionNumber()
<< ", "
<< id3v2tag->header()->tagSize()
<< " bytes in tag"
<< endl;
<< std::endl;
ID3v2::FrameList::ConstIterator it = id3v2tag->frameList().begin();
for(; it != id3v2tag->frameList().end(); it++)
cout << (*it)->frameID() << " - \"" << (*it)->toString() << "\"" << endl;
const auto &frames = id3v2tag->frameList();
for(auto it = frames.begin(); it != frames.end(); it++) {
std::cout << (*it)->frameID();
if(auto comment = dynamic_cast<ID3v2::CommentsFrame *>(*it))
if(!comment->description().isEmpty())
std::cout << " [" << comment->description() << "]";
std::cout << " - \"" << (*it)->toString() << "\"" << std::endl;
}
}
else
cout << "file does not have a valid id3v2 tag" << endl;
std::cout << "file does not have a valid id3v2 tag" << std::endl;
cout << endl << "ID3v1" << endl;
std::cout << std::endl << "ID3v1" << std::endl;
ID3v1::Tag *id3v1tag = f.ID3v1Tag();
if(id3v1tag) {
cout << "title - \"" << id3v1tag->title() << "\"" << endl;
cout << "artist - \"" << id3v1tag->artist() << "\"" << endl;
cout << "album - \"" << id3v1tag->album() << "\"" << endl;
cout << "year - \"" << id3v1tag->year() << "\"" << endl;
cout << "comment - \"" << id3v1tag->comment() << "\"" << endl;
cout << "track - \"" << id3v1tag->track() << "\"" << endl;
cout << "genre - \"" << id3v1tag->genre() << "\"" << endl;
std::cout << "title - \"" << id3v1tag->title() << "\"" << std::endl;
std::cout << "artist - \"" << id3v1tag->artist() << "\"" << std::endl;
std::cout << "album - \"" << id3v1tag->album() << "\"" << std::endl;
std::cout << "year - \"" << id3v1tag->year() << "\"" << std::endl;
std::cout << "comment - \"" << id3v1tag->comment() << "\"" << std::endl;
std::cout << "track - \"" << id3v1tag->track() << "\"" << std::endl;
std::cout << "genre - \"" << id3v1tag->genre() << "\"" << std::endl;
}
else
cout << "file does not have a valid id3v1 tag" << endl;
std::cout << "file does not have a valid id3v1 tag" << std::endl;
#ifdef TAGLIB_WITH_APE
APE::Tag *ape = f.APETag();
cout << endl << "APE" << endl;
std::cout << std::endl << "APE" << std::endl;
if(ape) {
for(APE::ItemListMap::ConstIterator it = ape->itemListMap().begin();
it != ape->itemListMap().end(); ++it)
const auto &items = ape->itemListMap();
for(auto it = items.begin(); it != items.end(); ++it)
{
if((*it).second.type() != APE::Item::Binary)
cout << (*it).first << " - \"" << (*it).second.toString() << "\"" << endl;
std::cout << (*it).first << " - \"" << (*it).second.toString() << "\"" << std::endl;
else
cout << (*it).first << " - Binary data (" << (*it).second.binaryData().size() << " bytes)" << endl;
std::cout << (*it).first << " - Binary data (" << (*it).second.binaryData().size() << " bytes)" << std::endl;
}
}
else
cout << "file does not have a valid APE tag" << endl;
std::cout << "file does not have a valid APE tag" << std::endl;
#endif
cout << endl;
std::cout << std::endl;
}
}

139
examples/matroskareader.cpp Normal file
View File

@@ -0,0 +1,139 @@
#include <cstdio>
#include "matroskafile.h"
#include "matroskatag.h"
#include "matroskasimpletag.h"
#include "matroskaattachments.h"
#include "matroskaattachedfile.h"
#include "matroskachapters.h"
#include "matroskachapteredition.h"
#include "tstring.h"
#include "tutils.h"
#include "tbytevector.h"
#define GREEN_TEXT(s) "" s ""
#define PRINT_PRETTY(label, value) printf(" " GREEN_TEXT(label) ": %s\n", value)
int main(int argc, char *argv[])
{
if(argc != 2) {
printf("Usage: matroskareader FILE\n");
return 1;
}
TagLib::Matroska::File file(argv[1]);
if(!file.isValid()) {
printf("File is not valid\n");
return 1;
}
auto tag = dynamic_cast<TagLib::Matroska::Tag*>(file.tag());
if(!tag) {
printf("File has no tag\n");
return 0;
}
const TagLib::Matroska::SimpleTagsList &list = tag->simpleTagsList();
printf("Found %u tag(s):\n", list.size());
for(const TagLib::Matroska::SimpleTag &t : list) {
PRINT_PRETTY("Tag Name", t.name().toCString(true));
if(t.type() == TagLib::Matroska::SimpleTag::StringType)
PRINT_PRETTY("Tag Value", t.toString().toCString(true));
else if(t.type() == TagLib::Matroska::SimpleTag::BinaryType)
PRINT_PRETTY("Tag Value",
TagLib::Utils::formatString("Binary with size %i", t.toByteVector().size()).toCString(false)
);
auto targetTypeValue = static_cast<unsigned int>(t.targetTypeValue());
PRINT_PRETTY("Target Type Value",
targetTypeValue == 0 ? "None" : TagLib::Utils::formatString("%i", targetTypeValue).toCString(false)
);
if(auto trackUid = t.trackUid()) {
PRINT_PRETTY("Track UID",
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");
printf("\n");
}
TagLib::Matroska::Attachments *attachments = file.attachments();
if(attachments) {
const TagLib::Matroska::Attachments::AttachedFileList &list = attachments->attachedFileList();
printf("Found %u attachment(s)\n", list.size());
for(const auto &attachedFile : list) {
PRINT_PRETTY("Filename", attachedFile.fileName().toCString(true));
const TagLib::String &description = attachedFile.description();
PRINT_PRETTY("Description", !description.isEmpty() ? description.toCString(true) : "None");
const TagLib::String &mediaType = attachedFile.mediaType();
PRINT_PRETTY("Media Type", !mediaType.isEmpty() ? mediaType.toCString(false) : "None");
PRINT_PRETTY("Data Size",
TagLib::Utils::formatString("%u byte(s)", attachedFile.data().size()).toCString(false)
);
PRINT_PRETTY("UID",
TagLib::Utils::formatString("%llu", attachedFile.uid()).toCString(false)
);
}
}
else
printf("File has no attachments\n");
TagLib::Matroska::Chapters *chapters = file.chapters();
if(chapters) {
printf("Chapters:\n");
const TagLib::Matroska::Chapters::ChapterEditionList &editions = chapters->chapterEditionList();
for(const auto &edition : editions) {
if(edition.uid()) {
PRINT_PRETTY("Edition UID", TagLib::Utils::formatString("%llu", edition.uid())
.toCString(false));
}
PRINT_PRETTY("Edition Flags", TagLib::Utils::formatString("default=%d, ordered=%d",
edition.isDefault(), edition.isOrdered())
.toCString(false));
printf("\n");
for(const auto &chapter : edition.chapterList()) {
PRINT_PRETTY("Chapter UID", TagLib::Utils::formatString("%llu", chapter.uid())
.toCString(false));
PRINT_PRETTY("Chapter flags", TagLib::Utils::formatString("hidden=%d", chapter.isHidden())
.toCString(false));
PRINT_PRETTY("Start-End", TagLib::Utils::formatString("%llu-%llu",
chapter.timeStart(), chapter.timeEnd()).toCString(false));
for(const auto &display : chapter.displayList()) {
PRINT_PRETTY("Display", display.string().toCString(false));
PRINT_PRETTY("Language", !display.language().isEmpty()
? display.language().toCString(false) : "Not set");
}
printf("\n");
}
}
}
else
printf("File has no chapters\n");
if(auto properties = dynamic_cast<const TagLib::Matroska::Properties *>(file.audioProperties())) {
printf("Properties:\n");
PRINT_PRETTY("Doc Type", properties->docType().toCString(false));
PRINT_PRETTY("Doc Type Version", TagLib::String::number(properties->docTypeVersion()).toCString(false));
PRINT_PRETTY("Codec Name", properties->codecName().toCString(true));
PRINT_PRETTY("Bitrate", TagLib::String::number(properties->bitrate()).toCString(false));
PRINT_PRETTY("Sample Rate", TagLib::String::number(properties->sampleRate()).toCString(false));
PRINT_PRETTY("Channels", TagLib::String::number(properties->channels()).toCString(false));
PRINT_PRETTY("Length [ms]", TagLib::String::number(properties->lengthInMilliseconds()).toCString(false));
}
return 0;
}

View File

@@ -0,0 +1,45 @@
#include <cstdio>
#include "matroskafile.h"
#include "matroskatag.h"
#include "matroskasimpletag.h"
#include "matroskaattachments.h"
#include "matroskaattachedfile.h"
#include "tfilestream.h"
#include "tstring.h"
#include "tutils.h"
int main(int argc, char *argv[])
{
if(argc != 3) {
printf("Usage: matroskawriter FILE ARTWORK\n");
return 1;
}
TagLib::Matroska::File file(argv[1]);
if(!file.isValid()) {
printf("File is not valid\n");
return 1;
}
auto tag = file.tag(true);
tag->clearSimpleTags();
tag->addSimpleTag(TagLib::Matroska::SimpleTag(
"Test Name 1", TagLib::String("Test Value 1"),
TagLib::Matroska::SimpleTag::TargetTypeValue::Track, "en"));
tag->addSimpleTag(TagLib::Matroska::SimpleTag(
"Test Name 2", TagLib::String("Test Value 2"),
TagLib::Matroska::SimpleTag::TargetTypeValue::Album));
tag->setTitle("Test title");
tag->setArtist("Test artist");
tag->setYear(1969);
TagLib::FileStream image(argv[2]);
auto data = image.readBlock(image.length());
auto attachments = file.attachments(true);
attachments->addAttachedFile(TagLib::Matroska::AttachedFile(
data, "cover.jpg", "image/jpeg"));
file.save();
return 0;
}

View File

@@ -23,8 +23,9 @@
*/
#include <iostream>
#include <mpegfile.h>
#include <tstring.h>
#include "tstring.h"
#include "mpegfile.h"
using namespace TagLib;

View File

@@ -24,19 +24,19 @@
#include <iostream>
#include <iomanip>
#include <stdio.h>
#include <cstdio>
#include <fileref.h>
#include <tag.h>
#include <tpropertymap.h>
using namespace std;
#include "tpropertymap.h"
#include "tstringlist.h"
#include "tvariant.h"
#include "fileref.h"
#include "tag.h"
int main(int argc, char *argv[])
{
for(int i = 1; i < argc; i++) {
cout << "******************** \"" << argv[i] << "\" ********************" << endl;
std::cout << "******************** \"" << argv[i] << "\" ********************" << std::endl;
TagLib::FileRef f(argv[i]);
@@ -44,46 +44,77 @@ int main(int argc, char *argv[])
TagLib::Tag *tag = f.tag();
cout << "-- TAG (basic) --" << endl;
cout << "title - \"" << tag->title() << "\"" << endl;
cout << "artist - \"" << tag->artist() << "\"" << endl;
cout << "album - \"" << tag->album() << "\"" << endl;
cout << "year - \"" << tag->year() << "\"" << endl;
cout << "comment - \"" << tag->comment() << "\"" << endl;
cout << "track - \"" << tag->track() << "\"" << endl;
cout << "genre - \"" << tag->genre() << "\"" << endl;
std::cout << "-- TAG (basic) --" << std::endl;
std::cout << "title - \"" << tag->title() << "\"" << std::endl;
std::cout << "artist - \"" << tag->artist() << "\"" << std::endl;
std::cout << "album - \"" << tag->album() << "\"" << std::endl;
std::cout << "year - \"" << tag->year() << "\"" << std::endl;
std::cout << "comment - \"" << tag->comment() << "\"" << std::endl;
std::cout << "track - \"" << tag->track() << "\"" << std::endl;
std::cout << "genre - \"" << tag->genre() << "\"" << std::endl;
TagLib::PropertyMap tags = f.file()->properties();
TagLib::PropertyMap tags = f.properties();
if(!tags.isEmpty()) {
unsigned int longest = 0;
for(auto j = tags.cbegin(); j != tags.cend(); ++j) {
if (j->first.size() > longest) {
longest = j->first.size();
}
}
unsigned int longest = 0;
for(TagLib::PropertyMap::ConstIterator i = tags.begin(); i != tags.end(); ++i) {
if (i->first.size() > longest) {
longest = i->first.size();
std::cout << "-- TAG (properties) --" << std::endl;
for(auto j = tags.cbegin(); j != tags.cend(); ++j) {
for(auto k = j->second.begin(); k != j->second.end(); ++k) {
std::cout << std::left << std::setfill(' ') << std::setw(longest) << j->first << " - " << '"' << *k << '"' << std::endl;
}
}
}
cout << "-- TAG (properties) --" << endl;
for(TagLib::PropertyMap::ConstIterator i = tags.begin(); i != tags.end(); ++i) {
for(TagLib::StringList::ConstIterator j = i->second.begin(); j != i->second.end(); ++j) {
cout << left << std::setw(longest) << i->first << " - " << '"' << *j << '"' << endl;
TagLib::StringList names = f.complexPropertyKeys();
for(const auto &name : names) {
const auto& properties = f.complexProperties(name);
for(const auto &property : properties) {
std::cout << name << ":" << std::endl;
for(const auto &[key, value] : property) {
std::cout << " " << std::left << std::setfill(' ') << std::setw(11) << key << " - ";
if(value.type() == TagLib::Variant::ByteVector) {
std::cout << "(" << value.value<TagLib::ByteVector>().size() << " bytes)" << std::endl;
/* The picture could be extracted using:
std::ofstream picture;
TagLib::String fn(argv[i]);
int slashPos = fn.rfind('/');
int dotPos = fn.rfind('.');
if(slashPos >= 0 && dotPos > slashPos) {
fn = fn.substr(slashPos + 1, dotPos - slashPos - 1);
}
fn += ".jpg";
picture.open(fn.toCString(), std::ios_base::out | std::ios_base::binary);
picture << value.value<TagLib::ByteVector>();
picture.close();
*/
}
else {
std::cout << value << std::endl;
}
}
}
}
}
if(!f.isNull() && f.audioProperties()) {
TagLib::AudioProperties *properties = f.audioProperties();
int seconds = properties->length() % 60;
int minutes = (properties->length() - seconds) / 60;
int seconds = properties->lengthInSeconds() % 60;
int minutes = (properties->lengthInSeconds() - seconds) / 60;
cout << "-- AUDIO --" << endl;
cout << "bitrate - " << properties->bitrate() << endl;
cout << "sample rate - " << properties->sampleRate() << endl;
cout << "channels - " << properties->channels() << endl;
cout << "length - " << minutes << ":" << setfill('0') << setw(2) << seconds << endl;
std::cout << "-- AUDIO --" << std::endl;
std::cout << "bitrate - " << properties->bitrate() << std::endl;
std::cout << "sample rate - " << properties->sampleRate() << std::endl;
std::cout << "channels - " << properties->channels() << std::endl;
std::cout << "length - " << minutes << ":" << std::setfill('0') << std::setw(2) << std::right << seconds << std::endl;
}
}
return 0;
}

View File

@@ -22,8 +22,10 @@
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "tag_c.h"
#include <stdio.h>
#include <tag_c.h>
#include <string.h>
#ifndef FALSE
#define FALSE 0
@@ -32,15 +34,16 @@
int main(int argc, char *argv[])
{
int i;
int seconds;
int minutes;
TagLib_File *file;
TagLib_Tag *tag;
const TagLib_AudioProperties *properties;
taglib_set_strings_unicode(FALSE);
taglib_set_strings_unicode(1);
for(i = 1; i < argc; i++) {
TagLib_File *file;
TagLib_Tag *tag;
const TagLib_AudioProperties *properties;
char **propertiesMap;
char **complexKeys;
printf("******************** \"%s\" ********************\n", argv[i]);
file = taglib_file_new(argv[i]);
@@ -50,21 +53,115 @@ int main(int argc, char *argv[])
tag = taglib_file_tag(file);
properties = taglib_file_audioproperties(file);
propertiesMap = taglib_property_keys(file);
complexKeys = taglib_complex_property_keys(file);
if(tag != NULL) {
printf("-- TAG --\n");
printf("-- TAG (basic) --\n");
printf("title - \"%s\"\n", taglib_tag_title(tag));
printf("artist - \"%s\"\n", taglib_tag_artist(tag));
printf("album - \"%s\"\n", taglib_tag_album(tag));
printf("year - \"%i\"\n", taglib_tag_year(tag));
printf("year - \"%u\"\n", taglib_tag_year(tag));
printf("comment - \"%s\"\n", taglib_tag_comment(tag));
printf("track - \"%i\"\n", taglib_tag_track(tag));
printf("track - \"%u\"\n", taglib_tag_track(tag));
printf("genre - \"%s\"\n", taglib_tag_genre(tag));
}
if(propertiesMap != NULL) {
char **keyPtr = propertiesMap;
int longest = 0;
while(*keyPtr) {
int len = (int)strlen(*keyPtr++);
if(len > longest) {
longest = len;
}
}
keyPtr = propertiesMap;
printf("-- TAG (properties) --\n");
while(*keyPtr) {
char **valPtr;
char **propertyValues = valPtr = taglib_property_get(file, *keyPtr);
while(valPtr && *valPtr)
{
printf("%-*s - \"%s\"\n", longest, *keyPtr, *valPtr++);
}
taglib_property_free(propertyValues);
++keyPtr;
}
}
if(complexKeys != NULL) {
char **keyPtr = complexKeys;
while(*keyPtr) {
TagLib_Complex_Property_Attribute*** props =
taglib_complex_property_get(file, *keyPtr);
if(props != NULL) {
TagLib_Complex_Property_Attribute*** propPtr = props;
while(*propPtr) {
TagLib_Complex_Property_Attribute** attrPtr = *propPtr;
printf("%s:\n", *keyPtr);
while(*attrPtr) {
TagLib_Complex_Property_Attribute *attr = *attrPtr;
TagLib_Variant_Type type = attr->value.type;
printf(" %-11s - ", attr->key);
switch(type) {
case TagLib_Variant_Void:
printf("null\n");
break;
case TagLib_Variant_Bool:
printf("%s\n", attr->value.value.boolValue ? "true" : "false");
break;
case TagLib_Variant_Int:
printf("%d\n", attr->value.value.intValue);
break;
case TagLib_Variant_UInt:
printf("%u\n", attr->value.value.uIntValue);
break;
case TagLib_Variant_LongLong:
printf("%lld\n", attr->value.value.longLongValue);
break;
case TagLib_Variant_ULongLong:
printf("%llu\n", attr->value.value.uLongLongValue);
break;
case TagLib_Variant_Double:
printf("%f\n", attr->value.value.doubleValue);
break;
case TagLib_Variant_String:
printf("\"%s\"\n", attr->value.value.stringValue);
break;
case TagLib_Variant_StringList:
if(attr->value.value.stringListValue) {
char **strs = attr->value.value.stringListValue;
char **s = strs;
while(*s) {
if(s != strs) {
printf(" ");
}
printf("%s", *s++);
}
}
printf("\n");
break;
case TagLib_Variant_ByteVector:
printf("(%u bytes)\n", attr->value.size);
break;
}
++attrPtr;
}
++propPtr;
}
taglib_complex_property_free(props);
}
++keyPtr;
}
taglib_complex_property_free_keys(complexKeys);
}
if(properties != NULL) {
seconds = taglib_audioproperties_length(properties) % 60;
minutes = (taglib_audioproperties_length(properties) - seconds) / 60;
int seconds = taglib_audioproperties_length(properties) % 60;
int minutes = (taglib_audioproperties_length(properties) - seconds) / 60;
printf("-- AUDIO --\n");
printf("bitrate - %i\n", taglib_audioproperties_bitrate(properties));
@@ -73,6 +170,7 @@ int main(int argc, char *argv[])
printf("length - %i:%02i\n", minutes, seconds);
}
taglib_property_free(propertiesMap);
taglib_tag_free_strings();
taglib_file_free(file);
}

View File

@@ -24,20 +24,20 @@
#include <iostream>
#include <iomanip>
#include <string.h>
#include <stdio.h>
#include <fstream>
#include <sstream>
#include <cstring>
#include <cstdio>
#include <cstdlib>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <tlist.h>
#include <fileref.h>
#include <tfile.h>
#include <tag.h>
#include <tpropertymap.h>
using namespace std;
#include "tlist.h"
#include "tfile.h"
#include "tpropertymap.h"
#include "tvariant.h"
#include "fileref.h"
#include "tag.h"
bool isArgument(const char *s)
{
@@ -46,31 +46,34 @@ bool isArgument(const char *s)
bool isFile(const char *s)
{
struct stat st;
#ifdef _WIN32
return ::stat(s, &st) == 0 && (st.st_mode & (S_IFREG));
struct _stat64 st;
return ::_stat64(s, &st) == 0 && (st.st_mode & S_IFREG);
#else
struct stat st;
return ::stat(s, &st) == 0 && (st.st_mode & (S_IFREG | S_IFLNK));
#endif
}
void usage()
{
cout << endl;
cout << "Usage: tagwriter <fields> <files>" << endl;
cout << endl;
cout << "Where the valid fields are:" << endl;
cout << " -t <title>" << endl;
cout << " -a <artist>" << endl;
cout << " -A <album>" << endl;
cout << " -c <comment>" << endl;
cout << " -g <genre>" << endl;
cout << " -y <year>" << endl;
cout << " -T <track>" << endl;
cout << " -R <tagname> <tagvalue>" << endl;
cout << " -I <tagname> <tagvalue>" << endl;
cout << " -D <tagname>" << endl;
cout << endl;
std::cout << std::endl;
std::cout << "Usage: tagwriter <fields> <files>" << std::endl;
std::cout << std::endl;
std::cout << "Where the valid fields are:" << std::endl;
std::cout << " -t <title>" << std::endl;
std::cout << " -a <artist>" << std::endl;
std::cout << " -A <album>" << std::endl;
std::cout << " -c <comment>" << std::endl;
std::cout << " -g <genre>" << std::endl;
std::cout << " -y <year>" << std::endl;
std::cout << " -T <track>" << std::endl;
std::cout << " -R <tagname> <tagvalue>" << std::endl;
std::cout << " -I <tagname> <tagvalue>" << std::endl;
std::cout << " -D <tagname>" << std::endl;
std::cout << " -C <complex-property-key> <key1=val1,key2=val2,...>" << std::endl;
std::cout << " -p <picturefile> <description> (\"\" \"\" to remove)" << std::endl;
std::cout << std::endl;
exit(1);
}
@@ -79,20 +82,116 @@ void checkForRejectedProperties(const TagLib::PropertyMap &tags)
{ // stolen from tagreader.cpp
if(tags.size() > 0) {
unsigned int longest = 0;
for(TagLib::PropertyMap::ConstIterator i = tags.begin(); i != tags.end(); ++i) {
for(auto i = tags.begin(); i != tags.end(); ++i) {
if(i->first.size() > longest) {
longest = i->first.size();
}
}
cout << "-- rejected TAGs (properties) --" << endl;
for(TagLib::PropertyMap::ConstIterator i = tags.begin(); i != tags.end(); ++i) {
for(TagLib::StringList::ConstIterator j = i->second.begin(); j != i->second.end(); ++j) {
cout << left << std::setw(longest) << i->first << " - " << '"' << *j << '"' << endl;
std::cout << "-- rejected TAGs (properties) --" << std::endl;
for(auto i = tags.begin(); i != tags.end(); ++i) {
for(auto j = i->second.begin(); j != i->second.end(); ++j) {
std::cout << std::left << std::setw(longest) << i->first << " - " << '"' << *j << '"' << std::endl;
}
}
}
}
/*!
* Create a list of variant maps from a string.
* The shorthand syntax in the string is kept simple, but should be sufficient
* for testing. Multiple maps are separated by ';', values within a map are
* assigned with key=value and separated by a ','. Types are detected, use
* double quotes to force a string. A ByteVector can be constructed from the
* contents of a file, the path is given after "file://". There is no escape
* character, use hex codes for ',' (\x2C) or ';' (\x3B).
*/
TagLib::List<TagLib::VariantMap> parseComplexPropertyValues(const TagLib::String &str)
{
if(str.isEmpty() || str == "\"\"" || str == "''") {
return {};
}
TagLib::List<TagLib::VariantMap> values;
const auto valueStrs = str.split(";");
for(const auto &valueStr : valueStrs) {
TagLib::VariantMap value;
const auto keyValStrs = valueStr.split(",");
for(const auto &keyValStr : keyValStrs) {
if(int equalPos = keyValStr.find('='); equalPos != -1) {
TagLib::String key = keyValStr.substr(0, equalPos);
TagLib::String valStr = keyValStr.substr(equalPos + 1);
bool hasDot = false;
bool hasNonNumeric = false;
bool hasSign = false;
for(auto it = valStr.cbegin(); it != valStr.cend(); ++it) {
if(it == valStr.cbegin() && (*it == '-' || *it == '+')) {
hasSign = true;
}
else if(*it == '.') {
hasDot = true;
}
else if(*it < '0' || *it > '9') {
hasNonNumeric = true;
}
}
TagLib::Variant val;
if(valStr == "null") {
// keep empty variant
}
else if(valStr == "true" || valStr == "false") {
val = TagLib::Variant(valStr == "true");
}
else if(!hasNonNumeric && hasDot) {
val = TagLib::Variant(std::stod(valStr.to8Bit()));
}
else if(!hasNonNumeric && hasSign) {
val = valStr.toLongLong(nullptr);
}
else if(!hasNonNumeric) {
val = valStr.toULongLong(nullptr);
}
else if(valStr.startsWith("file://")) {
auto filePath = valStr.substr(7 );
if(isFile(filePath.toCString())) {
std::ifstream fs;
fs.open(filePath.toCString(), std::ios::in | std::ios::binary);
std::stringstream buffer;
buffer << fs.rdbuf();
fs.close();
TagLib::String buf(buffer.str());
val = TagLib::Variant(buf.data(TagLib::String::Latin1));
}
else {
std::cout << filePath.toCString() << " not found." << std::endl;
val = TagLib::Variant(TagLib::ByteVector());
}
}
else {
int len = valStr.size();
if(len >= 2 && valStr[0] == '"' && valStr[len - 1] == '"') {
valStr = valStr.substr(1, len - 2);
}
int hexPos = 0;
while((hexPos = valStr.find("\\x", hexPos)) != -1) {
char ch;
bool ok;
if(static_cast<int>(valStr.length()) < hexPos + 4 ||
(ch = static_cast<char>(
valStr.substr(hexPos + 2, 2).toLongLong(&ok, 16)), !ok)) {
break;
}
valStr = valStr.substr(0, hexPos) + ch + valStr.substr(hexPos + 4);
++hexPos;
}
val = TagLib::Variant(valStr);
}
value.insert(key, val);
}
}
values.append(value);
}
return values;
}
int main(int argc, char *argv[])
{
TagLib::List<TagLib::FileRef> fileList;
@@ -110,17 +209,18 @@ int main(int argc, char *argv[])
if(fileList.isEmpty())
usage();
for(int i = 1; i < argc - 1; i += 2) {
int i = 1;
while(i < argc - 1) {
if(isArgument(argv[i]) && i + 1 < argc && !isArgument(argv[i + 1])) {
char field = argv[i][1];
TagLib::String value = argv[i + 1];
int numArgsConsumed = 2;
TagLib::List<TagLib::FileRef>::ConstIterator it;
for(it = fileList.begin(); it != fileList.end(); ++it) {
for(auto &f : fileList) {
TagLib::Tag *t = (*it).tag();
TagLib::Tag *t = f.tag();
switch (field) {
case 't':
@@ -147,24 +247,74 @@ int main(int argc, char *argv[])
case 'R':
case 'I':
if(i + 2 < argc) {
TagLib::PropertyMap map = (*it).file()->properties ();
TagLib::PropertyMap map = f.properties();
if(field == 'R') {
map.replace(value, TagLib::String(argv[i + 2]));
}
else {
map.insert(value, TagLib::String(argv[i + 2]));
}
++i;
checkForRejectedProperties((*it).file()->setProperties(map));
numArgsConsumed = 3;
checkForRejectedProperties(f.setProperties(map));
}
else {
usage();
}
break;
case 'D': {
TagLib::PropertyMap map = (*it).file()->properties();
TagLib::PropertyMap map = f.properties();
map.erase(value);
checkForRejectedProperties((*it).file()->setProperties(map));
checkForRejectedProperties(f.setProperties(map));
break;
}
case 'C': {
if(i + 2 < argc) {
numArgsConsumed = 3;
if(!value.isEmpty()) {
TagLib::List<TagLib::VariantMap> values = parseComplexPropertyValues(argv[i + 2]);
f.setComplexProperties(value, values);
}
}
else {
usage();
}
break;
}
case 'p': {
if(i + 2 < argc) {
numArgsConsumed = 3;
if(!value.isEmpty()) {
if(!isFile(value.toCString())) {
std::cout << value.toCString() << " not found." << std::endl;
return 1;
}
std::ifstream picture;
picture.open(value.toCString(), std::ios::in | std::ios::binary);
std::stringstream buffer;
buffer << picture.rdbuf();
picture.close();
TagLib::String buf(buffer.str());
TagLib::ByteVector data(buf.data(TagLib::String::Latin1));
TagLib::String mimeType = data.startsWith("\x89PNG\x0d\x0a\x1a\x0a")
? "image/png" : "image/jpeg";
TagLib::String description(argv[i + 2]);
f.setComplexProperties("PICTURE", {
{
{"data", data},
{"pictureType", "Front Cover"},
{"mimeType", mimeType},
{"description", description}
}
});
}
else {
// empty value, remove pictures
f.setComplexProperties("PICTURE", {});
}
}
else {
usage();
}
break;
}
default:
@@ -172,14 +322,14 @@ int main(int argc, char *argv[])
break;
}
}
i += numArgsConsumed;
}
else
usage();
}
TagLib::List<TagLib::FileRef>::ConstIterator it;
for(it = fileList.begin(); it != fileList.end(); ++it)
(*it).file()->save();
for(auto &f : fileList)
f.save();
return 0;
}

View File

@@ -2,22 +2,32 @@
usage()
{
echo "usage: $0 [OPTIONS]"
echo "usage: $0 [OPTIONS]"
cat << EOH
options:
[--libs]
[--cflags]
[--version]
[--prefix]
[--libs]
[--cflags]
[--version]
[--prefix]
EOH
exit 1;
exit 1
}
prefix=${CMAKE_INSTALL_PREFIX}
exec_prefix=${CMAKE_INSTALL_PREFIX}
libdir=${LIB_INSTALL_DIR}
includedir=${INCLUDE_INSTALL_DIR}
# Looks useless as it is, but could be replaced with a "pcfiledir" by Buildroot.
prefix=
exec_prefix=
if test -z "$prefix"; then
includedir=@CMAKE_INSTALL_FULL_INCLUDEDIR@
else
includedir=${prefix}/@CMAKE_INSTALL_INCLUDEDIR@
fi
if test -z "$exec_prefix"; then
libdir=@CMAKE_INSTALL_FULL_LIBDIR@
else
libdir=${exec_prefix}/@CMAKE_INSTALL_LIBDIR@
fi
flags=""
@@ -29,22 +39,22 @@ while test $# -gt 0
do
case $1 in
--libs)
flags="$flags -L$libdir -ltag"
;;
flags="$flags -L$libdir -ltag@TAGLIB_INSTALL_SUFFIX@ @ZLIB_LIBRARIES_FLAGS@"
;;
--cflags)
flags="$flags -I$includedir/taglib"
;;
flags="$flags -I$includedir -I$includedir/taglib@TAGLIB_INSTALL_SUFFIX@"
;;
--version)
echo ${TAGLIB_LIB_VERSION_STRING}
;;
echo @TAGLIB_LIB_VERSION_STRING@
;;
--prefix)
echo $prefix
;;
*)
echo "$0: unknown option $1"
echo
usage
;;
echo ${prefix:-@CMAKE_INSTALL_PREFIX@}
;;
*)
echo "$0: unknown option $1"
echo
usage
;;
esac
shift
done

12
taglib-config.cmake.in Normal file
View File

@@ -0,0 +1,12 @@
@PACKAGE_INIT@
include("${CMAKE_CURRENT_LIST_DIR}/taglib-targets.cmake")
set(TAGLIB_FOUND ${TagLib_FOUND})
set(TAGLIB_VERSION ${TagLib_VERSION})
check_required_components("TagLib")
if(NOT TARGET TagLib::TagLib)
add_library(TagLib::TagLib ALIAS TagLib::tag)
endif()

View File

@@ -14,11 +14,11 @@ if /i "%1#" == "--cflags#" goto doit
if /i "%1#" == "--version#" goto doit
if /i "%1#" == "--prefix#" goto doit
echo "usage: %0 [OPTIONS]"
echo [--libs]
echo [--cflags]
echo [--version]
echo [--prefix]
echo usage: %0 [OPTIONS]
echo [--libs]
echo [--cflags]
echo [--version]
echo [--prefix]
goto theend
*
@@ -27,10 +27,9 @@ goto theend
* to allow for static, shared or debug builds.
* It would be preferable if the top level CMakeLists.txt provided the library name during config. ??
:doit
if /i "%1#" == "--libs#" echo -L${LIB_INSTALL_DIR} -llibtag
if /i "%1#" == "--cflags#" echo -I${INCLUDE_INSTALL_DIR}/taglib
if /i "%1#" == "--libs#" echo -L${CMAKE_INSTALL_FULL_LIBDIR} -llibtag${TAGLIB_INSTALL_SUFFIX}
if /i "%1#" == "--cflags#" echo -I${CMAKE_INSTALL_FULL_INCLUDEDIR} -I${CMAKE_INSTALL_FULL_INCLUDEDIR}/taglib${TAGLIB_INSTALL_SUFFIX}
if /i "%1#" == "--version#" echo ${TAGLIB_LIB_VERSION_STRING}
if /i "%1#" == "--prefix#" echo ${CMAKE_INSTALL_PREFIX}
:theend

View File

@@ -1,11 +1,11 @@
prefix=@CMAKE_INSTALL_PREFIX@
exec_prefix=@CMAKE_INSTALL_PREFIX@
libdir=@LIB_INSTALL_DIR@
includedir=@INCLUDE_INSTALL_DIR@
exec_prefix=${prefix}
libdir=@CMAKE_PC_LIBDIR@
includedir=@CMAKE_PC_INCLUDEDIR@
Name: TagLib
Description: Audio meta-data library
Requires:
Requires:
Version: @TAGLIB_LIB_VERSION_STRING@
Libs: -L${libdir} -ltag
Cflags: -I${includedir}/taglib
Libs: -L${libdir} -ltag@TAGLIB_INSTALL_SUFFIX@ @ZLIB_LIBRARIES_FLAGS@
Cflags: -I${includedir} -I${includedir}/taglib@TAGLIB_INSTALL_SUFFIX@

View File

@@ -1,41 +1,78 @@
set(CMAKE_INCLUDE_CURRENT_DIR ON)
include_directories(
set(tag_HDR_DIRS
${CMAKE_CURRENT_SOURCE_DIR}/toolkit
${CMAKE_CURRENT_SOURCE_DIR}/asf
${CMAKE_CURRENT_SOURCE_DIR}/mpeg
${CMAKE_CURRENT_SOURCE_DIR}/ogg
${CMAKE_CURRENT_SOURCE_DIR}/ogg/flac
${CMAKE_CURRENT_SOURCE_DIR}/flac
${CMAKE_CURRENT_SOURCE_DIR}/mpc
${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
${CMAKE_CURRENT_SOURCE_DIR}/ape
${CMAKE_CURRENT_SOURCE_DIR}/wavpack
${CMAKE_CURRENT_SOURCE_DIR}/trueaudio
${CMAKE_CURRENT_SOURCE_DIR}/riff
${CMAKE_CURRENT_SOURCE_DIR}/riff/aiff
${CMAKE_CURRENT_SOURCE_DIR}/riff/wav
${CMAKE_CURRENT_SOURCE_DIR}/mod
${CMAKE_CURRENT_SOURCE_DIR}/s3m
${CMAKE_CURRENT_SOURCE_DIR}/it
${CMAKE_CURRENT_SOURCE_DIR}/xm
)
if(ZLIB_FOUND)
include_directories(${ZLIB_INCLUDE_DIR})
elseif(HAVE_ZLIB_SOURCE)
include_directories(${ZLIB_SOURCE})
if(WITH_ASF)
set(tag_HDR_DIRS ${tag_HDR_DIRS}
${CMAKE_CURRENT_SOURCE_DIR}/asf
)
endif()
if(HAVE_BOOST_BYTESWAP OR HAVE_BOOST_ATOMIC OR HAVE_BOOST_ZLIB)
include_directories(${Boost_INCLUDE_DIR})
if(WITH_VORBIS)
set(tag_HDR_DIRS ${tag_HDR_DIRS}
${CMAKE_CURRENT_SOURCE_DIR}/ogg
${CMAKE_CURRENT_SOURCE_DIR}/ogg/flac
${CMAKE_CURRENT_SOURCE_DIR}/flac
${CMAKE_CURRENT_SOURCE_DIR}/ogg/vorbis
${CMAKE_CURRENT_SOURCE_DIR}/ogg/speex
${CMAKE_CURRENT_SOURCE_DIR}/ogg/opus
)
endif()
if(WITH_APE)
set(tag_HDR_DIRS ${tag_HDR_DIRS}
${CMAKE_CURRENT_SOURCE_DIR}/mpc
${CMAKE_CURRENT_SOURCE_DIR}/ape
${CMAKE_CURRENT_SOURCE_DIR}/wavpack
)
endif()
if(WITH_MP4)
set(tag_HDR_DIRS ${tag_HDR_DIRS}
${CMAKE_CURRENT_SOURCE_DIR}/mp4
)
endif()
if(WITH_TRUEAUDIO)
set(tag_HDR_DIRS ${tag_HDR_DIRS}
${CMAKE_CURRENT_SOURCE_DIR}/trueaudio
)
endif()
if(WITH_RIFF)
set(tag_HDR_DIRS ${tag_HDR_DIRS}
${CMAKE_CURRENT_SOURCE_DIR}/riff
${CMAKE_CURRENT_SOURCE_DIR}/riff/aiff
${CMAKE_CURRENT_SOURCE_DIR}/riff/wav
)
endif()
if(WITH_MOD)
set(tag_HDR_DIRS ${tag_HDR_DIRS}
${CMAKE_CURRENT_SOURCE_DIR}/mod
${CMAKE_CURRENT_SOURCE_DIR}/s3m
${CMAKE_CURRENT_SOURCE_DIR}/it
${CMAKE_CURRENT_SOURCE_DIR}/xm
)
endif()
if(WITH_DSF)
set(tag_HDR_DIRS ${tag_HDR_DIRS}
${CMAKE_CURRENT_SOURCE_DIR}/dsf
${CMAKE_CURRENT_SOURCE_DIR}/dsdiff
)
endif()
if(WITH_SHORTEN)
set(tag_HDR_DIRS ${tag_HDR_DIRS}
${CMAKE_CURRENT_SOURCE_DIR}/shorten
)
endif()
if(WITH_MATROSKA)
set(tag_HDR_DIRS ${tag_HDR_DIRS}
${CMAKE_CURRENT_SOURCE_DIR}/matroska
${CMAKE_CURRENT_SOURCE_DIR}/matroska/ebml
)
endif()
include_directories(${tag_HDR_DIRS})
set(tag_PRIVATE_HDRS)
set(tag_HDRS
tag.h
fileref.h
@@ -49,21 +86,24 @@ set(tag_HDRS
toolkit/tstringlist.h
toolkit/tbytevector.h
toolkit/tbytevectorlist.h
toolkit/tvariant.h
toolkit/tbytevectorstream.h
toolkit/tiostream.h
toolkit/tfile.h
toolkit/tfilestream.h
toolkit/tmap.h
toolkit/tmap.tcc
toolkit/tpicturetype.h
toolkit/tpropertymap.h
toolkit/trefcounter.h
toolkit/tdebuglistener.h
toolkit/tversionnumber.h
mpeg/mpegfile.h
mpeg/mpegproperties.h
mpeg/mpegheader.h
mpeg/xingheader.h
mpeg/id3v1/id3v1tag.h
mpeg/id3v1/id3v1genres.h
mpeg/id3v2/id3v2.h
mpeg/id3v2/id3v2extendedheader.h
mpeg/id3v2/id3v2frame.h
mpeg/id3v2/id3v2header.h
@@ -88,60 +128,141 @@ set(tag_HDRS
mpeg/id3v2/frames/chapterframe.h
mpeg/id3v2/frames/tableofcontentsframe.h
mpeg/id3v2/frames/podcastframe.h
ogg/oggfile.h
ogg/oggpage.h
ogg/oggpageheader.h
ogg/xiphcomment.h
ogg/vorbis/vorbisfile.h
ogg/vorbis/vorbisproperties.h
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
flac/flacmetadatablock.h
ape/apefile.h
ape/apeproperties.h
ape/apetag.h
ape/apefooter.h
ape/apeitem.h
mpc/mpcfile.h
mpc/mpcproperties.h
wavpack/wavpackfile.h
wavpack/wavpackproperties.h
trueaudio/trueaudiofile.h
trueaudio/trueaudioproperties.h
riff/rifffile.h
riff/aiff/aifffile.h
riff/aiff/aiffproperties.h
riff/wav/wavfile.h
riff/wav/wavproperties.h
riff/wav/infotag.h
asf/asffile.h
asf/asfproperties.h
asf/asftag.h
asf/asfattribute.h
asf/asfpicture.h
mp4/mp4file.h
mp4/mp4atom.h
mp4/mp4tag.h
mp4/mp4item.h
mp4/mp4properties.h
mp4/mp4coverart.h
mod/modfilebase.h
mod/modfile.h
mod/modtag.h
mod/modproperties.h
it/itfile.h
it/itproperties.h
s3m/s3mfile.h
s3m/s3mproperties.h
xm/xmfile.h
xm/xmproperties.h
)
if(WITH_VORBIS)
set(tag_HDRS ${tag_HDRS}
ogg/oggfile.h
ogg/oggpage.h
ogg/oggpageheader.h
ogg/xiphcomment.h
ogg/vorbis/vorbisfile.h
ogg/vorbis/vorbisproperties.h
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
flac/flacmetadatablock.h
)
endif()
if(WITH_APE)
set(tag_HDRS ${tag_HDRS}
ape/apefile.h
ape/apeproperties.h
ape/apetag.h
ape/apefooter.h
ape/apeitem.h
mpc/mpcfile.h
mpc/mpcproperties.h
wavpack/wavpackfile.h
wavpack/wavpackproperties.h
)
endif()
if(WITH_TRUEAUDIO)
set(tag_HDRS ${tag_HDRS}
trueaudio/trueaudiofile.h
trueaudio/trueaudioproperties.h
)
endif()
if(WITH_RIFF)
set(tag_HDRS ${tag_HDRS}
riff/rifffile.h
riff/aiff/aifffile.h
riff/aiff/aiffproperties.h
riff/wav/wavfile.h
riff/wav/wavproperties.h
riff/wav/infotag.h
)
endif()
if(WITH_ASF)
set(tag_HDRS ${tag_HDRS}
asf/asffile.h
asf/asfproperties.h
asf/asftag.h
asf/asfattribute.h
asf/asfpicture.h
)
endif()
if(WITH_MP4)
set(tag_HDRS ${tag_HDRS}
mp4/mp4file.h
mp4/mp4atom.h
mp4/mp4tag.h
mp4/mp4item.h
mp4/mp4properties.h
mp4/mp4coverart.h
mp4/mp4stem.h
mp4/mp4itemfactory.h
)
endif()
if(WITH_MOD)
set(tag_HDRS ${tag_HDRS}
mod/modfilebase.h
mod/modfile.h
mod/modtag.h
mod/modproperties.h
it/itfile.h
it/itproperties.h
s3m/s3mfile.h
s3m/s3mproperties.h
xm/xmfile.h
xm/xmproperties.h
)
endif()
if(WITH_DSF)
set(tag_HDRS ${tag_HDRS}
dsf/dsffile.h
dsf/dsfproperties.h
dsdiff/dsdifffile.h
dsdiff/dsdiffproperties.h
dsdiff/dsdiffdiintag.h
)
endif()
if(WITH_SHORTEN)
set(tag_HDRS ${tag_HDRS}
shorten/shortenfile.h
shorten/shortenproperties.h
shorten/shortentag.h
)
endif()
if(WITH_MATROSKA)
set(tag_HDRS ${tag_HDRS}
matroska/matroskaattachedfile.h
matroska/matroskaattachments.h
matroska/matroskachapter.h
matroska/matroskachapteredition.h
matroska/matroskachapters.h
matroska/matroskaelement.h
matroska/matroskafile.h
matroska/matroskaproperties.h
matroska/matroskasimpletag.h
matroska/matroskatag.h
)
set(tag_PRIVATE_HDRS ${tag_PRIVATE_HDRS}
matroska/ebml/ebmlbinaryelement.h
matroska/ebml/ebmlelement.h
matroska/ebml/ebmlmasterelement.h
matroska/ebml/ebmlmkattachments.h
matroska/ebml/ebmlmkchapters.h
matroska/ebml/ebmlmkcues.h
matroska/ebml/ebmlmkseekhead.h
matroska/ebml/ebmlmksegment.h
matroska/ebml/ebmlmkinfo.h
matroska/ebml/ebmlmktracks.h
matroska/ebml/ebmlmktags.h
matroska/ebml/ebmlstringelement.h
matroska/ebml/ebmluintelement.h
matroska/ebml/ebmlfloatelement.h
matroska/ebml/ebmlutils.h
matroska/ebml/ebmlvoidelement.h
matroska/matroskacues.h
matroska/matroskaseekhead.h
matroska/matroskasegment.h
)
endif()
set(mpeg_SRCS
mpeg/mpegfile.cpp
@@ -185,156 +306,215 @@ set(frames_SRCS
mpeg/id3v2/frames/podcastframe.cpp
)
set(ogg_SRCS
ogg/oggfile.cpp
ogg/oggpage.cpp
ogg/oggpageheader.cpp
ogg/xiphcomment.cpp
)
if(WITH_VORBIS)
set(ogg_SRCS
ogg/oggfile.cpp
ogg/oggpage.cpp
ogg/oggpageheader.cpp
ogg/xiphcomment.cpp
)
set(vorbis_SRCS
ogg/vorbis/vorbisfile.cpp
ogg/vorbis/vorbisproperties.cpp
)
set(vorbis_SRCS
ogg/vorbis/vorbisfile.cpp
ogg/vorbis/vorbisproperties.cpp
)
set(flacs_SRCS
flac/flacfile.cpp
flac/flacpicture.cpp
flac/flacproperties.cpp
flac/flacmetadatablock.cpp
flac/flacunknownmetadatablock.cpp
)
set(flacs_SRCS
flac/flacfile.cpp
flac/flacpicture.cpp
flac/flacproperties.cpp
flac/flacmetadatablock.cpp
flac/flacunknownmetadatablock.cpp
)
set(oggflacs_SRCS
ogg/flac/oggflacfile.cpp
)
set(oggflacs_SRCS
ogg/flac/oggflacfile.cpp
)
set(mpc_SRCS
mpc/mpcfile.cpp
mpc/mpcproperties.cpp
)
set(speex_SRCS
ogg/speex/speexfile.cpp
ogg/speex/speexproperties.cpp
)
set(mp4_SRCS
mp4/mp4file.cpp
mp4/mp4atom.cpp
mp4/mp4tag.cpp
mp4/mp4item.cpp
mp4/mp4properties.cpp
mp4/mp4coverart.cpp
)
set(opus_SRCS
ogg/opus/opusfile.cpp
ogg/opus/opusproperties.cpp
)
endif()
set(ape_SRCS
ape/apetag.cpp
ape/apefooter.cpp
ape/apeitem.cpp
ape/apefile.cpp
ape/apeproperties.cpp
)
if(WITH_APE)
set(mpc_SRCS
mpc/mpcfile.cpp
mpc/mpcproperties.cpp
)
set(wavpack_SRCS
wavpack/wavpackfile.cpp
wavpack/wavpackproperties.cpp
)
set(ape_SRCS
ape/apetag.cpp
ape/apefooter.cpp
ape/apeitem.cpp
ape/apefile.cpp
ape/apeproperties.cpp
)
set(speex_SRCS
ogg/speex/speexfile.cpp
ogg/speex/speexproperties.cpp
)
set(wavpack_SRCS
wavpack/wavpackfile.cpp
wavpack/wavpackproperties.cpp
)
endif()
set(opus_SRCS
ogg/opus/opusfile.cpp
ogg/opus/opusproperties.cpp
)
if(WITH_MP4)
set(mp4_SRCS
mp4/mp4file.cpp
mp4/mp4atom.cpp
mp4/mp4tag.cpp
mp4/mp4item.cpp
mp4/mp4properties.cpp
mp4/mp4coverart.cpp
mp4/mp4stem.cpp
mp4/mp4itemfactory.cpp
)
endif()
set(trueaudio_SRCS
trueaudio/trueaudiofile.cpp
trueaudio/trueaudioproperties.cpp
)
if(WITH_TRUEAUDIO)
set(trueaudio_SRCS
trueaudio/trueaudiofile.cpp
trueaudio/trueaudioproperties.cpp
)
endif()
set(asf_SRCS
asf/asftag.cpp
asf/asffile.cpp
asf/asfproperties.cpp
asf/asfattribute.cpp
asf/asfpicture.cpp
)
if(WITH_ASF)
set(asf_SRCS
asf/asftag.cpp
asf/asffile.cpp
asf/asfproperties.cpp
asf/asfattribute.cpp
asf/asfpicture.cpp
)
endif()
set(riff_SRCS
riff/rifffile.cpp
)
if(WITH_RIFF)
set(riff_SRCS
riff/rifffile.cpp
)
set(aiff_SRCS
riff/aiff/aifffile.cpp
riff/aiff/aiffproperties.cpp
)
set(aiff_SRCS
riff/aiff/aifffile.cpp
riff/aiff/aiffproperties.cpp
)
set(wav_SRCS
riff/wav/wavfile.cpp
riff/wav/wavproperties.cpp
riff/wav/infotag.cpp
)
set(wav_SRCS
riff/wav/wavfile.cpp
riff/wav/wavproperties.cpp
riff/wav/infotag.cpp
)
endif()
set(mod_SRCS
mod/modfilebase.cpp
mod/modfile.cpp
mod/modtag.cpp
mod/modproperties.cpp
)
if(WITH_MOD)
set(mod_SRCS
mod/modfilebase.cpp
mod/modfile.cpp
mod/modtag.cpp
mod/modproperties.cpp
)
set(s3m_SRCS
s3m/s3mfile.cpp
s3m/s3mproperties.cpp
)
set(s3m_SRCS
s3m/s3mfile.cpp
s3m/s3mproperties.cpp
)
set(it_SRCS
it/itfile.cpp
it/itproperties.cpp
)
set(it_SRCS
it/itfile.cpp
it/itproperties.cpp
)
set(xm_SRCS
xm/xmfile.cpp
xm/xmproperties.cpp
)
set(xm_SRCS
xm/xmfile.cpp
xm/xmproperties.cpp
)
endif()
if(WITH_DSF)
set(dsf_SRCS
dsf/dsffile.cpp
dsf/dsfproperties.cpp
)
set(dsdiff_SRCS
dsdiff/dsdifffile.cpp
dsdiff/dsdiffproperties.cpp
dsdiff/dsdiffdiintag.cpp
)
endif()
if(WITH_SHORTEN)
set(shorten_SRCS
shorten/shortenfile.cpp
shorten/shortenproperties.cpp
shorten/shortentag.cpp
)
endif()
if(WITH_MATROSKA)
set(matroska_SRCS
matroska/matroskaattachedfile.cpp
matroska/matroskaattachments.cpp
matroska/matroskachapter.cpp
matroska/matroskachapteredition.cpp
matroska/matroskachapters.cpp
matroska/matroskacues.cpp
matroska/matroskaelement.cpp
matroska/matroskafile.cpp
matroska/matroskaproperties.cpp
matroska/matroskaseekhead.cpp
matroska/matroskasegment.cpp
matroska/matroskasimpletag.cpp
matroska/matroskatag.cpp
)
set(ebml_SRCS
matroska/ebml/ebmlbinaryelement.cpp
matroska/ebml/ebmlelement.cpp
matroska/ebml/ebmlmasterelement.cpp
matroska/ebml/ebmlmkattachments.cpp
matroska/ebml/ebmlmkchapters.cpp
matroska/ebml/ebmlmkcues.cpp
matroska/ebml/ebmlmkseekhead.cpp
matroska/ebml/ebmlmksegment.cpp
matroska/ebml/ebmlmkinfo.cpp
matroska/ebml/ebmlmktracks.cpp
matroska/ebml/ebmlmktags.cpp
matroska/ebml/ebmlstringelement.cpp
matroska/ebml/ebmluintelement.cpp
matroska/ebml/ebmlfloatelement.cpp
matroska/ebml/ebmlutils.cpp
matroska/ebml/ebmlvoidelement.cpp
)
endif()
set(toolkit_SRCS
toolkit/tstring.cpp
toolkit/tstringlist.cpp
toolkit/tbytevector.cpp
toolkit/tbytevectorlist.cpp
toolkit/tvariant.cpp
toolkit/tbytevectorstream.cpp
toolkit/tiostream.cpp
toolkit/tfile.cpp
toolkit/tfilestream.cpp
toolkit/tdebug.cpp
toolkit/tpicturetype.cpp
toolkit/tpropertymap.cpp
toolkit/trefcounter.cpp
toolkit/tdebuglistener.cpp
toolkit/tzlib.cpp
toolkit/tversionnumber.cpp
)
if(NOT WIN32)
set(unicode_SRCS
toolkit/unicode.cpp
)
endif()
if(HAVE_ZLIB_SOURCE)
set(zlib_SRCS
${ZLIB_SOURCE}/adler32.c
${ZLIB_SOURCE}/crc32.c
${ZLIB_SOURCE}/inffast.c
${ZLIB_SOURCE}/inflate.c
${ZLIB_SOURCE}/inftrees.c
${ZLIB_SOURCE}/zutil.c
)
endif()
set(tag_LIB_SRCS
${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} ${opus_SRCS}
${unicode_SRCS} ${zlib_SRCS}
${dsf_SRCS} ${dsdiff_SRCS} ${shorten_SRCS} ${matroska_SRCS} ${ebml_SRCS}
tag.cpp
tagunion.cpp
fileref.cpp
@@ -342,36 +522,81 @@ set(tag_LIB_SRCS
tagutils.cpp
)
add_library(tag ${tag_LIB_SRCS} ${tag_HDRS})
add_library(tag ${tag_LIB_SRCS} ${tag_HDRS} ${tag_PRIVATE_HDRS})
if(ZLIB_FOUND)
target_link_libraries(tag ${ZLIB_LIBRARIES})
endif()
target_include_directories(tag INTERFACE
$<INSTALL_INTERFACE:include>
$<INSTALL_INTERFACE:include/taglib${TAGLIB_INSTALL_SUFFIX}>
)
if(HAVE_BOOST_ATOMIC)
target_link_libraries(tag ${Boost_ATOMIC_LIBRARY})
endif()
if(HAVE_BOOST_ZLIB)
target_link_libraries(tag ${Boost_IOSTREAMS_LIBRARY} ${Boost_ZLIB_LIBRARY})
endif()
target_link_libraries(tag
PRIVATE $<IF:$<TARGET_EXISTS:utf8::cpp>,utf8::cpp,$<$<TARGET_EXISTS:utf8cpp>:utf8cpp>>
$<$<TARGET_EXISTS:ZLIB::ZLIB>:ZLIB::ZLIB>
)
set_target_properties(tag PROPERTIES
VERSION ${TAGLIB_SOVERSION_MAJOR}.${TAGLIB_SOVERSION_MINOR}.${TAGLIB_SOVERSION_PATCH}
SOVERSION ${TAGLIB_SOVERSION_MAJOR}
INSTALL_NAME_DIR ${LIB_INSTALL_DIR}
INSTALL_NAME_DIR ${CMAKE_INSTALL_FULL_LIBDIR}
DEFINE_SYMBOL MAKE_TAGLIB_LIB
LINK_INTERFACE_LIBRARIES ""
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)
endif()
if(BUILD_FRAMEWORK)
set_target_properties(tag PROPERTIES FRAMEWORK TRUE)
unset(INSTALL_NAME_DIR)
set_target_properties(tag PROPERTIES
FRAMEWORK TRUE
MACOSX_RPATH 1
VERSION "A"
SOVERSION "A"
)
endif()
if(TAGLIB_INSTALL_SUFFIX)
if(BUILD_SHARED_LIBS)
set(TAGLIB_LIBRARY_SUFFIX "${TAGLIB_INSTALL_SUFFIX}${CMAKE_SHARED_LIBRARY_SUFFIX}")
else()
set(TAGLIB_LIBRARY_SUFFIX "${TAGLIB_INSTALL_SUFFIX}${CMAKE_STATIC_LIBRARY_SUFFIX}")
endif()
set_target_properties(tag PROPERTIES SUFFIX ${TAGLIB_LIBRARY_SUFFIX})
endif()
install(TARGETS tag
EXPORT taglibTargets
FRAMEWORK DESTINATION ${FRAMEWORK_INSTALL_DIR}
LIBRARY DESTINATION ${LIB_INSTALL_DIR}
RUNTIME DESTINATION ${BIN_INSTALL_DIR}
ARCHIVE DESTINATION ${LIB_INSTALL_DIR}
PUBLIC_HEADER DESTINATION ${INCLUDE_INSTALL_DIR}/taglib
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/taglib${TAGLIB_INSTALL_SUFFIX}
)
configure_package_config_file(
"${PROJECT_SOURCE_DIR}/taglib-config.cmake.in"
"${PROJECT_BINARY_DIR}/taglib-config.cmake"
INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/taglib${TAGLIB_INSTALL_SUFFIX}
)
write_basic_package_version_file(
"${PROJECT_BINARY_DIR}/taglib-config-version.cmake"
VERSION "${TAGLIB_LIB_VERSION_STRING}"
COMPATIBILITY AnyNewerVersion
)
install(EXPORT taglibTargets
FILE taglib-targets.cmake
NAMESPACE TagLib::
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/taglib${TAGLIB_INSTALL_SUFFIX}
)
install(FILES "${PROJECT_BINARY_DIR}/taglib-config.cmake"
"${PROJECT_BINARY_DIR}/taglib-config-version.cmake"
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/taglib${TAGLIB_INSTALL_SUFFIX}
)

View File

@@ -49,8 +49,8 @@ APE Tag Version 2.000 (with header, recommended):
APE tag items should be sorted ascending by size. When streaming, parts of the
APE tag may be dropped to reduce the danger of drop outs between tracks. This
is not required, but is strongly recommended. It would be desirable for the i
tems to be sorted by importance / size, but this is not feasible. This
is not required, but is strongly recommended. It would be desirable for the
items to be sorted by importance / size, but this is not feasible. This
convention should only be broken when adding less important small items and it
is not desirable to rewrite the entire tag. An APE tag at the end of a file
(the recommended location) must have at least a footer; an APE tag at the

View File

@@ -31,16 +31,14 @@
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#include <tbytevector.h>
#include <tstring.h>
#include <tdebug.h>
#include <tagunion.h>
#include <id3v1tag.h>
#include <id3v2header.h>
#include <tpropertymap.h>
#include <tagutils.h>
#include "apefile.h"
#include "tdebug.h"
#include "tpropertymap.h"
#include "id3v1tag.h"
#include "id3v2header.h"
#include "tagunion.h"
#include "tagutils.h"
#include "apetag.h"
#include "apefooter.h"
@@ -49,47 +47,44 @@ using namespace TagLib;
namespace
{
enum { ApeAPEIndex = 0, ApeID3v1Index = 1 };
}
} // namespace
class APE::File::FilePrivate
{
public:
FilePrivate() :
APELocation(-1),
APESize(0),
ID3v1Location(-1),
ID3v2Header(0),
ID3v2Location(-1),
ID3v2Size(0),
properties(0) {}
offset_t APELocation { -1 };
long APESize { 0 };
~FilePrivate()
{
delete ID3v2Header;
delete properties;
}
offset_t ID3v1Location { -1 };
long APELocation;
long APESize;
long ID3v1Location;
ID3v2::Header *ID3v2Header;
long ID3v2Location;
long ID3v2Size;
std::unique_ptr<ID3v2::Header> ID3v2Header;
offset_t ID3v2Location { -1 };
long ID3v2Size { 0 };
TagUnion tag;
Properties *properties;
std::unique_ptr<Properties> properties;
};
////////////////////////////////////////////////////////////////////////////////
// static members
////////////////////////////////////////////////////////////////////////////////
bool APE::File::isSupported(IOStream *stream)
{
// An APE file has an ID "MAC " somewhere. An ID3v2 tag may precede.
const ByteVector buffer = Utils::readHeader(stream, bufferSize(), true);
return buffer.find("MAC ") >= 0;
}
////////////////////////////////////////////////////////////////////////////////
// public members
////////////////////////////////////////////////////////////////////////////////
APE::File::File(FileName file, bool readProperties, Properties::ReadStyle) :
TagLib::File(file),
d(new FilePrivate())
d(std::make_unique<FilePrivate>())
{
if(isOpen())
read(readProperties);
@@ -97,16 +92,13 @@ APE::File::File(FileName file, bool readProperties, Properties::ReadStyle) :
APE::File::File(IOStream *stream, bool readProperties, Properties::ReadStyle) :
TagLib::File(stream),
d(new FilePrivate())
d(std::make_unique<FilePrivate>())
{
if(isOpen())
read(readProperties);
}
APE::File::~File()
{
delete d;
}
APE::File::~File() = default;
TagLib::Tag *APE::File::tag() const
{
@@ -133,7 +125,7 @@ PropertyMap APE::File::setProperties(const PropertyMap &properties)
APE::Properties *APE::File::audioProperties() const
{
return d->properties;
return d->properties.get();
}
bool APE::File::save()
@@ -186,7 +178,7 @@ bool APE::File::save()
insert(data, d->APELocation, d->APESize);
if(d->ID3v1Location >= 0)
d->ID3v1Location += (static_cast<long>(data.size()) - d->APESize);
d->ID3v1Location += static_cast<long>(data.size()) - d->APESize;
d->APESize = data.size();
}
@@ -221,10 +213,10 @@ APE::Tag *APE::File::APETag(bool create)
void APE::File::strip(int tags)
{
if(tags & ID3v1)
d->tag.set(ApeID3v1Index, 0);
d->tag.set(ApeID3v1Index, nullptr);
if(tags & APE)
d->tag.set(ApeAPEIndex, 0);
d->tag.set(ApeAPEIndex, nullptr);
if(!ID3v1Tag())
APETag(true);
@@ -232,12 +224,12 @@ void APE::File::strip(int tags)
bool APE::File::hasAPETag() const
{
return (d->APELocation >= 0);
return d->APELocation >= 0;
}
bool APE::File::hasID3v1Tag() const
{
return (d->ID3v1Location >= 0);
return d->ID3v1Location >= 0;
}
////////////////////////////////////////////////////////////////////////////////
@@ -252,7 +244,7 @@ void APE::File::read(bool readProperties)
if(d->ID3v2Location >= 0) {
seek(d->ID3v2Location);
d->ID3v2Header = new ID3v2::Header(readBlock(ID3v2::Header::size()));
d->ID3v2Header = std::make_unique<ID3v2::Header>(readBlock(ID3v2::Header::size()));
d->ID3v2Size = d->ID3v2Header->completeTagSize();
}
@@ -280,7 +272,7 @@ void APE::File::read(bool readProperties)
if(readProperties) {
long streamLength;
offset_t streamLength;
if(d->APELocation >= 0)
streamLength = d->APELocation;
@@ -291,12 +283,12 @@ void APE::File::read(bool readProperties)
if(d->ID3v2Location >= 0) {
seek(d->ID3v2Location + d->ID3v2Size);
streamLength -= (d->ID3v2Location + d->ID3v2Size);
streamLength -= d->ID3v2Location + d->ID3v2Size;
}
else {
seek(0);
}
d->properties = new Properties(this, streamLength);
d->properties = std::make_unique<Properties>(this, streamLength);
}
}

View File

@@ -48,7 +48,7 @@ namespace TagLib {
//! An implementation of APE metadata
/*!
* This is implementation of APE metadata.
* This is an implementation of APE metadata.
*
* This supports ID3v1 and APE (v1 and v2) style comments as well as reading stream
* properties from the file.
@@ -84,7 +84,7 @@ namespace TagLib {
};
/*!
* Constructs an APE file from \a file. If \a readProperties is true the
* Constructs an APE file from \a file. If \a readProperties is \c true the
* file's audio properties will also be read.
*
* \note In the current implementation, \a propertiesStyle is ignored.
@@ -93,7 +93,7 @@ namespace TagLib {
Properties::ReadStyle propertiesStyle = Properties::Average);
/*!
* Constructs an APE file from \a stream. If \a readProperties is true the
* Constructs an APE file from \a stream. If \a readProperties is \c true the
* file's audio properties will also be read.
*
* \note TagLib will *not* take ownership of the stream, the caller is
@@ -107,39 +107,42 @@ namespace TagLib {
/*!
* Destroys this instance of the File.
*/
virtual ~File();
~File() override;
File(const File &) = delete;
File &operator=(const File &) = delete;
/*!
* Returns the Tag for this file. This will be an APE tag, an ID3v1 tag
* or a combination of the two.
*/
virtual TagLib::Tag *tag() const;
TagLib::Tag *tag() const override;
/*!
* Implements the unified property interface -- export function.
* If the file contains both an APE and an ID3v1 tag, only APE
* will be converted to the PropertyMap.
*/
PropertyMap properties() const;
PropertyMap properties() const override;
/*!
* Removes unsupported properties. Forwards to the actual Tag's
* removeUnsupportedProperties() function.
*/
void removeUnsupportedProperties(const StringList &properties);
void removeUnsupportedProperties(const StringList &properties) override;
/*!
* Implements the unified property interface -- import function.
* Creates an APEv2 tag if necessary. A potentially existing ID3v1
* tag will be updated as well.
*/
PropertyMap setProperties(const PropertyMap &);
PropertyMap setProperties(const PropertyMap &) override;
/*!
* Returns the APE::Properties for this file. If no audio properties
* were read then this will return a null pointer.
*/
virtual Properties *audioProperties() const;
Properties *audioProperties() const override;
/*!
* Saves the file.
@@ -147,20 +150,20 @@ namespace TagLib {
* \note According to the official Monkey's Audio SDK, an APE file
* can only have either ID3V1 or APE tags, so a parameter is used here.
*/
virtual bool save();
bool save() override;
/*!
* Returns a pointer to the ID3v1 tag of the file.
*
* If \a create is false (the default) this may return a null pointer
* if there is no valid ID3v1 tag. If \a create is true it will create
* If \a create is \c false (the default) this may return a null pointer
* if there is no valid ID3v1 tag. If \a create is \c true it will create
* an ID3v1 tag if one does not exist and returns a valid pointer.
*
* \note This may return a valid pointer regardless of whether or not the
* file on disk has an ID3v1 tag. Use hasID3v1Tag() to check if the file
* on disk actually has an ID3v1 tag.
*
* \note The Tag <b>is still</b> owned by the MPEG::File and should not be
* \note The Tag <b>is still</b> owned by the APE::File and should not be
* deleted by the user. It will be deleted when the file (object) is
* destroyed.
*
@@ -171,15 +174,15 @@ namespace TagLib {
/*!
* Returns a pointer to the APE tag of the file.
*
* If \a create is false (the default) this may return a null pointer
* if there is no valid APE tag. If \a create is true it will create
* If \a create is \c false (the default) this may return a null pointer
* if there is no valid APE tag. If \a create is \c true it will create
* an APE tag if one does not exist and returns a valid pointer.
*
* \note This may return a valid pointer regardless of whether or not the
* file on disk has an APE tag. Use hasAPETag() to check if the file
* on disk actually has an APE tag.
*
* \note The Tag <b>is still</b> owned by the MPEG::File and should not be
* \note The Tag <b>is still</b> owned by the APE::File and should not be
* deleted by the user. It will be deleted when the file (object) is
* destroyed.
*
@@ -211,16 +214,23 @@ namespace TagLib {
*/
bool hasID3v1Tag() const;
private:
File(const File &);
File &operator=(const File &);
/*!
* Returns whether or not the given \a stream can be opened as an APE
* file.
*
* \note This method is designed to do a quick check. The result may
* not necessarily be correct.
*/
static bool isSupported(IOStream *stream);
private:
void read(bool readProperties);
class FilePrivate;
FilePrivate *d;
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
std::unique_ptr<FilePrivate> d;
};
}
}
} // namespace APE
} // namespace TagLib
#endif

View File

@@ -24,13 +24,11 @@
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#include <iostream>
#include "apefooter.h"
#include <bitset>
#include <tstring.h>
#include <tdebug.h>
#include "apefooter.h"
#include "taglib.h"
using namespace TagLib;
using namespace APE;
@@ -38,23 +36,15 @@ using namespace APE;
class APE::Footer::FooterPrivate
{
public:
FooterPrivate() :
version(0),
footerPresent(true),
headerPresent(false),
isHeader(false),
itemCount(0),
tagSize(0) {}
unsigned int version { 0 };
unsigned int version;
bool footerPresent { true };
bool headerPresent { false };
bool footerPresent;
bool headerPresent;
bool isHeader { false };
bool isHeader;
unsigned int itemCount;
unsigned int tagSize;
unsigned int itemCount { 0 };
unsigned int tagSize { 0 };
};
////////////////////////////////////////////////////////////////////////////////
@@ -76,20 +66,17 @@ ByteVector APE::Footer::fileIdentifier()
////////////////////////////////////////////////////////////////////////////////
APE::Footer::Footer() :
d(new FooterPrivate())
d(std::make_unique<FooterPrivate>())
{
}
APE::Footer::Footer(const ByteVector &data) :
d(new FooterPrivate())
d(std::make_unique<FooterPrivate>())
{
parse(data);
}
APE::Footer::~Footer()
{
delete d;
}
APE::Footer::~Footer() = default;
unsigned int APE::Footer::version() const
{
@@ -135,8 +122,7 @@ unsigned int APE::Footer::completeTagSize() const
{
if(d->headerPresent)
return d->tagSize + size();
else
return d->tagSize;
return d->tagSize;
}
void APE::Footer::setTagSize(unsigned int s)
@@ -158,8 +144,7 @@ ByteVector APE::Footer::renderHeader() const
{
if(!d->headerPresent)
return ByteVector();
else
return render(true);
return render(true);
}
////////////////////////////////////////////////////////////////////////////////
@@ -224,7 +209,7 @@ ByteVector APE::Footer::render(bool isHeader) const
flags[30] = false; // footer is always present
flags[29] = isHeader;
v.append(ByteVector::fromUInt(flags.to_ulong(), false));
v.append(ByteVector::fromUInt(static_cast<unsigned int>(flags.to_ulong()), false));
// add the reserved 64bit

View File

@@ -59,7 +59,10 @@ namespace TagLib {
/*!
* Destroys the footer.
*/
virtual ~Footer();
~Footer();
Footer(const Footer &) = delete;
Footer &operator=(const Footer &) = delete;
/*!
* Returns the version number. (Note: This is the 1000 or 2000.)
@@ -67,17 +70,17 @@ namespace TagLib {
unsigned int version() const;
/*!
* Returns true if a header is present in the tag.
* Returns \c true if a header is present in the tag.
*/
bool headerPresent() const;
/*!
* Returns true if a footer is present in the tag.
* Returns \c true if a footer is present in the tag.
*/
bool footerPresent() const;
/*!
* Returns true this is actually the header.
* Returns \c true if this is actually the header.
*/
bool isHeader() const;
@@ -142,8 +145,8 @@ namespace TagLib {
ByteVector renderFooter() const;
/*!
* Renders the header corresponding to the footer. If headerPresent is
* set to false, it returns an empty ByteVector.
* Renders the header corresponding to the footer. If headerPresent() is
* \c false, it returns an empty ByteVector.
*/
ByteVector renderHeader() const;
@@ -160,14 +163,12 @@ namespace TagLib {
ByteVector render(bool isHeader) const;
private:
Footer(const Footer &);
Footer &operator=(const Footer &);
class FooterPrivate;
FooterPrivate *d;
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
std::unique_ptr<FooterPrivate> d;
};
}
}
} // namespace APE
} // namespace TagLib
#endif

View File

@@ -23,26 +23,24 @@
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#include <tbytevectorlist.h>
#include <tdebug.h>
#include "apeitem.h"
#include <utility>
#include <numeric>
#include "tdebug.h"
using namespace TagLib;
using namespace APE;
class APE::Item::ItemPrivate
{
public:
ItemPrivate() :
type(Text),
readOnly(false) {}
Item::ItemTypes type;
Item::ItemTypes type { Text };
String key;
ByteVector value;
StringList text;
bool readOnly;
bool readOnly { false };
};
////////////////////////////////////////////////////////////////////////////////
@@ -50,26 +48,19 @@ public:
////////////////////////////////////////////////////////////////////////////////
APE::Item::Item() :
d(new ItemPrivate())
d(std::make_unique<ItemPrivate>())
{
}
APE::Item::Item(const String &key, const String &value) :
d(new ItemPrivate())
{
d->key = key;
d->text.append(value);
}
APE::Item::Item(const String &key, const StringList &values) :
d(new ItemPrivate())
d(std::make_unique<ItemPrivate>())
{
d->key = key;
d->text = values;
}
APE::Item::Item(const String &key, const ByteVector &value, bool binary) :
d(new ItemPrivate())
d(std::make_unique<ItemPrivate>())
{
d->key = key;
if(binary) {
@@ -82,14 +73,11 @@ APE::Item::Item(const String &key, const ByteVector &value, bool binary) :
}
APE::Item::Item(const Item &item) :
d(new ItemPrivate(*item.d))
d(std::make_unique<ItemPrivate>(*item.d))
{
}
APE::Item::~Item()
{
delete d;
}
APE::Item::~Item() = default;
Item &APE::Item::operator=(const Item &item)
{
@@ -97,7 +85,7 @@ Item &APE::Item::operator=(const Item &item)
return *this;
}
void APE::Item::swap(Item &item)
void APE::Item::swap(Item &item) noexcept
{
using std::swap;
@@ -141,14 +129,6 @@ void APE::Item::setBinaryData(const ByteVector &value)
d->text.clear();
}
ByteVector APE::Item::value() const
{
// This seems incorrect as it won't be actually rendering the value to keep it
// up to date.
return d->value;
}
void APE::Item::setKey(const String &key)
{
d->key = key;
@@ -161,10 +141,10 @@ void APE::Item::setValue(const String &value)
d->value.clear();
}
void APE::Item::setValues(const StringList &value)
void APE::Item::setValues(const StringList &values)
{
d->type = Text;
d->text = value;
d->text = values;
d->value.clear();
}
@@ -188,12 +168,10 @@ int APE::Item::size() const
switch(d->type) {
case Text:
if(!d->text.isEmpty()) {
StringList::ConstIterator it = d->text.begin();
result += it->data(String::UTF8).size();
it++;
for(; it != d->text.end(); ++it)
result += 1 + it->data(String::UTF8).size();
result = std::accumulate(d->text.cbegin(), d->text.cend(), result,
[](int sz, const String &t) {
return sz + 1 + t.data(String::UTF8).size();
}) - 1;
}
break;
@@ -205,11 +183,6 @@ int APE::Item::size() const
return result;
}
StringList APE::Item::toStringList() const
{
return d->text;
}
StringList APE::Item::values() const
{
return d->text;
@@ -219,8 +192,7 @@ String APE::Item::toString() const
{
if(d->type == Text && !isEmpty())
return d->text.front();
else
return String();
return String();
}
bool APE::Item::isEmpty() const
@@ -229,9 +201,7 @@ bool APE::Item::isEmpty() const
case Text:
if(d->text.isEmpty())
return true;
if(d->text.size() == 1 && d->text.front().isEmpty())
return true;
return false;
return d->text.size() == 1 && d->text.front().isEmpty();
case Binary:
case Locator:
return d->value.isEmpty();
@@ -257,45 +227,44 @@ void APE::Item::parse(const ByteVector &data)
d->key = String(&data[8], String::Latin1);
const ByteVector value = data.mid(8 + d->key.size() + 1, valueLength);
const ByteVector val = data.mid(8 + d->key.size() + 1, valueLength);
setReadOnly(flags & 1);
setType(ItemTypes((flags >> 1) & 3));
setType(static_cast<ItemTypes>((flags >> 1) & 3));
if(Text == d->type)
d->text = StringList(ByteVectorList::split(value, '\0'), String::UTF8);
d->text = StringList(ByteVectorList::split(val, '\0'), String::UTF8);
else
d->value = value;
d->value = val;
}
ByteVector APE::Item::render() const
{
ByteVector data;
unsigned int flags = ((d->readOnly) ? 1 : 0) | (d->type << 1);
ByteVector value;
unsigned int flags = (d->readOnly ? 1 : 0) | (d->type << 1);
ByteVector val;
if(isEmpty())
return data;
if(d->type == Text) {
StringList::ConstIterator it = d->text.begin();
auto it = d->text.cbegin();
value.append(it->data(String::UTF8));
it++;
for(; it != d->text.end(); ++it) {
value.append('\0');
value.append(it->data(String::UTF8));
val.append(it->data(String::UTF8));
for(it = std::next(it); it != d->text.cend(); ++it) {
val.append('\0');
val.append(it->data(String::UTF8));
}
d->value = value;
d->value = val;
}
else
value.append(d->value);
val.append(d->value);
data.append(ByteVector::fromUInt(value.size(), false));
data.append(ByteVector::fromUInt(val.size(), false));
data.append(ByteVector::fromUInt(flags, false));
data.append(d->key.data(String::Latin1));
data.append(ByteVector('\0'));
data.append(value);
data.append(val);
return data;
}

View File

@@ -31,9 +31,7 @@
#include "tstringlist.h"
namespace TagLib {
namespace APE {
//! An implementation of APE-items
/*!
@@ -58,12 +56,6 @@ namespace TagLib {
*/
Item();
/*!
* Constructs a text item with \a key and \a value.
*/
// BIC: Remove this, StringList has a constructor from a single string
Item(const String &key, const String &value);
/*!
* Constructs a text item with \a key and \a values.
*/
@@ -71,7 +63,7 @@ namespace TagLib {
/*!
* Constructs an item with \a key and \a value.
* If \a binary is true a Binary item will be created, otherwise \a value will be interpreted as text
* If \a binary is \c true a Binary item will be created, otherwise \a value will be interpreted as text
*/
Item(const String &key, const ByteVector &value, bool binary);
@@ -83,7 +75,7 @@ namespace TagLib {
/*!
* Destroys the item.
*/
virtual ~Item();
~Item();
/*!
* Copies the contents of \a item into this item.
@@ -91,9 +83,9 @@ namespace TagLib {
Item &operator=(const Item &item);
/*!
* Exchanges the content of this item by the content of \a item.
* Exchanges the content of this item with the content of \a item.
*/
void swap(Item &item);
void swap(Item &item) noexcept;
/*!
* Returns the key.
@@ -112,11 +104,6 @@ namespace TagLib {
*/
void setBinaryData(const ByteVector &value);
#ifndef DO_NOT_DOCUMENT
/* Remove in next binary incompatible release */
ByteVector value() const;
#endif
/*!
* Sets the key for the item to \a key.
*/
@@ -133,7 +120,7 @@ namespace TagLib {
* Sets the text value of the item to the list of values in \a value and clears
* any previous contents.
*
* \see toStringList()
* \see values()
*/
void setValues(const StringList &values);
@@ -147,7 +134,7 @@ namespace TagLib {
/*!
* Appends \a values to extend the current list of text values.
*
* \see toStringList()
* \see values()
*/
void appendValues(const StringList &values);
@@ -163,11 +150,6 @@ namespace TagLib {
*/
String toString() const;
#ifndef DO_NOT_DOCUMENT
/* Remove in next binary incompatible release */
StringList toStringList() const;
#endif
/*!
* Returns the list of text values. If the data type is not \a Text, always
* returns an empty StringList.
@@ -190,16 +172,16 @@ namespace TagLib {
void setReadOnly(bool readOnly);
/*!
* Return true if the item is read-only.
* Return \c true if the item is read-only.
*/
bool isReadOnly() const;
/*!
* Sets the type of the item to \a type.
* Sets the type of the item to \a val.
*
* \see ItemTypes
*/
void setType(ItemTypes type);
void setType(ItemTypes val);
/*!
* Returns the type of the item.
@@ -207,18 +189,16 @@ namespace TagLib {
ItemTypes type() const;
/*!
* Returns if the item has any real content.
* Returns \c false if the item has any real content.
*/
bool isEmpty() const;
private:
class ItemPrivate;
ItemPrivate *d;
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
std::unique_ptr<ItemPrivate> d;
};
}
}
} // namespace APE
} // namespace TagLib
#endif

View File

@@ -27,70 +27,39 @@
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#include <tstring.h>
#include <tdebug.h>
#include <bitset>
#include "id3v2tag.h"
#include "apeproperties.h"
#include "tdebug.h"
#include "apefile.h"
#include "apetag.h"
#include "apefooter.h"
#include "apetag.h"
using namespace TagLib;
class APE::Properties::PropertiesPrivate
{
public:
PropertiesPrivate() :
length(0),
bitrate(0),
sampleRate(0),
channels(0),
version(0),
bitsPerSample(0),
sampleFrames(0) {}
int length;
int bitrate;
int sampleRate;
int channels;
int version;
int bitsPerSample;
unsigned int sampleFrames;
int length { 0 };
int bitrate { 0 };
int sampleRate { 0 };
int channels { 0 };
int version { 0 };
int bitsPerSample { 0 };
unsigned int sampleFrames { 0 };
};
////////////////////////////////////////////////////////////////////////////////
// public members
////////////////////////////////////////////////////////////////////////////////
APE::Properties::Properties(File *, ReadStyle style) :
APE::Properties::Properties(File *file, offset_t streamLength, ReadStyle style) :
AudioProperties(style),
d(new PropertiesPrivate())
{
debug("APE::Properties::Properties() -- This constructor is no longer used.");
}
APE::Properties::Properties(File *file, long streamLength, ReadStyle style) :
AudioProperties(style),
d(new PropertiesPrivate())
d(std::make_unique<PropertiesPrivate>())
{
read(file, streamLength);
}
APE::Properties::~Properties()
{
delete d;
}
int APE::Properties::length() const
{
return lengthInSeconds();
}
int APE::Properties::lengthInSeconds() const
{
return d->length / 1000;
}
APE::Properties::~Properties() = default;
int APE::Properties::lengthInMilliseconds() const
{
@@ -140,27 +109,27 @@ namespace
return header.toUShort(4, false);
}
}
} // namespace
void APE::Properties::read(File *file, long streamLength)
void APE::Properties::read(File *file, offset_t streamLength)
{
// First, we assume that the file pointer is set at the first descriptor.
long offset = file->tell();
int version = headerVersion(file->readBlock(6));
offset_t offset = file->tell();
int vers = headerVersion(file->readBlock(6));
// Next, we look for the descriptor.
if(version < 0) {
if(vers < 0) {
offset = file->find("MAC ", offset);
file->seek(offset);
version = headerVersion(file->readBlock(6));
vers = headerVersion(file->readBlock(6));
}
if(version < 0) {
if(vers < 0) {
debug("APE::Properties::read() -- APE descriptor not found");
return;
}
d->version = version;
d->version = vers;
if(d->version >= 3980)
analyzeCurrent(file);
@@ -168,9 +137,9 @@ void APE::Properties::read(File *file, long streamLength)
analyzeOld(file);
if(d->sampleFrames > 0 && d->sampleRate > 0) {
const double length = d->sampleFrames * 1000.0 / d->sampleRate;
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);
}
}
@@ -184,9 +153,8 @@ void APE::Properties::analyzeCurrent(File *file)
return;
}
const unsigned int descriptorBytes = descriptor.toUInt(0, false);
if((descriptorBytes - 52) > 0)
if(const unsigned int descriptorBytes = descriptor.toUInt(0, false);
descriptorBytes - 52 > 0)
file->seek(descriptorBytes - 52, File::Current);
// Read the header

View File

@@ -30,6 +30,7 @@
#ifndef TAGLIB_APEPROPERTIES_H
#define TAGLIB_APEPROPERTIES_H
#include "taglib.h"
#include "taglib_export.h"
#include "audioproperties.h"
@@ -52,63 +53,38 @@ namespace TagLib {
/*!
* Create an instance of APE::Properties with the data read from the
* APE::File \a file.
*
* \deprecated
*/
Properties(File *file, ReadStyle style = Average);
/*!
* Create an instance of APE::Properties with the data read from the
* APE::File \a file.
*/
Properties(File *file, long streamLength, ReadStyle style = Average);
Properties(File *file, offset_t streamLength, ReadStyle style = Average);
/*!
* Destroys this APE::Properties instance.
*/
virtual ~Properties();
~Properties() override;
/*!
* Returns the length of the file in seconds. The length is rounded down to
* the nearest whole second.
*
* \note This method is just an alias of lengthInSeconds().
*
* \deprecated
*/
virtual int length() const;
/*!
* Returns the length of the file in seconds. The length is rounded down to
* the nearest whole second.
*
* \see lengthInMilliseconds()
*/
// BIC: make virtual
int lengthInSeconds() const;
Properties(const Properties &) = delete;
Properties &operator=(const Properties &) = delete;
/*!
* Returns the length of the file in milliseconds.
*
* \see lengthInSeconds()
*/
// BIC: make virtual
int lengthInMilliseconds() const;
int lengthInMilliseconds() const override;
/*!
* Returns the average bit rate of the file in kb/s.
*/
virtual int bitrate() const;
int bitrate() const override;
/*!
* Returns the sample rate in Hz.
*/
virtual int sampleRate() const;
int sampleRate() const override;
/*!
* Returns the number of audio channels.
*/
virtual int channels() const;
int channels() const override;
/*!
* Returns the number of bits per audio sample.
@@ -121,23 +97,21 @@ namespace TagLib {
unsigned int sampleFrames() const;
/*!
* Returns APE version.
* Returns the APE version.
*/
int version() const;
private:
Properties(const Properties &);
Properties &operator=(const Properties &);
void read(File *file, long streamLength);
void read(File *file, offset_t streamLength);
void analyzeCurrent(File *file);
void analyzeOld(File *file);
class PropertiesPrivate;
PropertiesPrivate *d;
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
std::unique_ptr<PropertiesPrivate> d;
};
}
}
} // namespace APE
} // namespace TagLib
#endif

View File

@@ -31,14 +31,14 @@
#define WANT_CLASS_INSTANTIATION_OF_MAP (1)
#endif
#include <tfile.h>
#include <tstring.h>
#include <tmap.h>
#include <tpropertymap.h>
#include <tdebug.h>
#include <tutils.h>
#include "apetag.h"
#include <array>
#include <utility>
#include "tdebug.h"
#include "tfile.h"
#include "tpropertymap.h"
#include "apefooter.h"
#include "apeitem.h"
@@ -47,39 +47,29 @@ using namespace APE;
namespace
{
bool isKeyValid(const char *key, size_t length)
{
const char *invalidKeys[] = { "ID3", "TAG", "OGGS", "MP+", 0 };
constexpr unsigned int MinKeyLength = 2;
constexpr unsigned int MaxKeyLength = 255;
if(length < 2 || length > 16)
return false;
const String FRONT_COVER("COVER ART (FRONT)");
const String BACK_COVER("COVER ART (BACK)");
bool isKeyValid(const ByteVector &key)
{
static constexpr std::array invalidKeys { "ID3", "TAG", "OGGS", "MP+" };
// only allow printable ASCII including space (32..126)
for(const char *p = key; p < key + length; ++p) {
const int c = static_cast<unsigned char>(*p);
if(c < 32 || c > 126)
return false;
}
for(size_t i = 0; invalidKeys[i] != 0; ++i) {
if(Utils::equalsIgnoreCase(key, invalidKeys[i]))
return false;
}
return true;
return std::none_of(key.begin(), key.end(),
[](unsigned char c) { return c < 32 || c > 126; })
&& std::none_of(invalidKeys.begin(), invalidKeys.end(),
[upperKey = String(key).upper()](auto k) { return upperKey == k; });
}
}
} // namespace
class APE::Tag::TagPrivate
{
public:
TagPrivate() :
file(0),
footerLocation(0) {}
File *file;
long footerLocation;
File *file { nullptr };
offset_t footerLocation { 0 };
Footer footer;
ItemListMap itemListMap;
@@ -90,14 +80,12 @@ public:
////////////////////////////////////////////////////////////////////////////////
APE::Tag::Tag() :
TagLib::Tag(),
d(new TagPrivate())
d(std::make_unique<TagPrivate>())
{
}
APE::Tag::Tag(TagLib::File *file, long footerLocation) :
TagLib::Tag(),
d(new TagPrivate())
APE::Tag::Tag(TagLib::File *file, offset_t footerLocation) :
d(std::make_unique<TagPrivate>())
{
d->file = file;
d->footerLocation = footerLocation;
@@ -105,10 +93,7 @@ APE::Tag::Tag(TagLib::File *file, long footerLocation) :
read();
}
APE::Tag::~Tag()
{
delete d;
}
APE::Tag::~Tag() = default;
ByteVector APE::Tag::fileIdentifier()
{
@@ -117,51 +102,44 @@ ByteVector APE::Tag::fileIdentifier()
String APE::Tag::title() const
{
if(d->itemListMap["TITLE"].isEmpty())
return String();
return d->itemListMap["TITLE"].values().toString();
Item val = d->itemListMap.value("TITLE");
return val.isEmpty() ? String() : joinTagValues(val.values());
}
String APE::Tag::artist() const
{
if(d->itemListMap["ARTIST"].isEmpty())
return String();
return d->itemListMap["ARTIST"].values().toString();
Item val = d->itemListMap.value("ARTIST");
return val.isEmpty() ? String() : joinTagValues(val.values());
}
String APE::Tag::album() const
{
if(d->itemListMap["ALBUM"].isEmpty())
return String();
return d->itemListMap["ALBUM"].values().toString();
Item val = d->itemListMap.value("ALBUM");
return val.isEmpty() ? String() : joinTagValues(val.values());
}
String APE::Tag::comment() const
{
if(d->itemListMap["COMMENT"].isEmpty())
return String();
return d->itemListMap["COMMENT"].values().toString();
Item val = d->itemListMap.value("COMMENT");
return val.isEmpty() ? String() : joinTagValues(val.values());
}
String APE::Tag::genre() const
{
if(d->itemListMap["GENRE"].isEmpty())
return String();
return d->itemListMap["GENRE"].values().toString();
Item val = d->itemListMap.value("GENRE");
return val.isEmpty() ? String() : joinTagValues(val.values());
}
unsigned int APE::Tag::year() const
{
if(d->itemListMap["YEAR"].isEmpty())
return 0;
return d->itemListMap["YEAR"].toString().toInt();
Item val = d->itemListMap.value("YEAR");
return val.isEmpty() ? 0 : val.toString().toInt();
}
unsigned int APE::Tag::track() const
{
if(d->itemListMap["TRACK"].isEmpty())
return 0;
return d->itemListMap["TRACK"].toString().toInt();
Item val = d->itemListMap.value("TRACK");
return val.isEmpty() ? 0 : val.toString().toInt();
}
void APE::Tag::setTitle(const String &s)
@@ -191,7 +169,7 @@ void APE::Tag::setGenre(const String &s)
void APE::Tag::setYear(unsigned int i)
{
if(i <= 0)
if(i == 0)
removeItem("YEAR");
else
addValue("YEAR", String::number(i), true);
@@ -199,7 +177,7 @@ void APE::Tag::setYear(unsigned int i)
void APE::Tag::setTrack(unsigned int i)
{
if(i <= 0)
if(i == 0)
removeItem("TRACK");
else
addValue("TRACK", String::number(i), true);
@@ -209,33 +187,35 @@ namespace
{
// conversions of tag keys between what we use in PropertyMap and what's usual
// for APE tags
// usual, APE
const char *keyConversions[][2] = {{"TRACKNUMBER", "TRACK" },
{"DATE", "YEAR" },
{"ALBUMARTIST", "ALBUM ARTIST"},
{"DISCNUMBER", "DISC" },
{"REMIXER", "MIXARTIST" }};
const size_t keyConversionsSize = sizeof(keyConversions) / sizeof(keyConversions[0]);
}
// usual, APE
constexpr std::array keyConversions {
std::pair("TRACKNUMBER", "TRACK"),
std::pair("DATE", "YEAR"),
std::pair("ALBUMARTIST", "ALBUM ARTIST"),
std::pair("DISCNUMBER", "DISC"),
std::pair("REMIXER", "MIXARTIST"),
std::pair("RELEASESTATUS", "MUSICBRAINZ_ALBUMSTATUS"),
std::pair("RELEASETYPE", "MUSICBRAINZ_ALBUMTYPE"),
};
} // namespace
PropertyMap APE::Tag::properties() const
{
PropertyMap properties;
ItemListMap::ConstIterator it = itemListMap().begin();
for(; it != itemListMap().end(); ++it) {
String tagName = it->first.upper();
for(const auto &[tag, item] : std::as_const(itemListMap())) {
// if the item is Binary or Locator, or if the key is an invalid string,
// add to unsupportedData
if(it->second.type() != Item::Text || tagName.isEmpty()) {
properties.unsupportedData().append(it->first);
if(String tagName = tag.upper();
item.type() != Item::Text || tagName.isEmpty()) {
properties.addUnsupportedData(tag);
}
else {
// Some tags need to be handled specially
for(size_t i = 0; i < keyConversionsSize; ++i) {
if(tagName == keyConversions[i][1])
tagName = keyConversions[i][0];
for(const auto &[k, t] : keyConversions) {
if(tagName == t)
tagName = k;
}
properties[tagName].append(it->second.toStringList());
properties[tagName].append(item.values());
}
}
return properties;
@@ -243,64 +223,145 @@ PropertyMap APE::Tag::properties() const
void APE::Tag::removeUnsupportedProperties(const StringList &properties)
{
StringList::ConstIterator it = properties.begin();
for(; it != properties.end(); ++it)
removeItem(*it);
for(const auto &property : properties)
removeItem(property);
}
PropertyMap APE::Tag::setProperties(const PropertyMap &origProps)
{
PropertyMap properties(origProps); // make a local copy that can be modified
PropertyMap props(origProps); // make a local copy that can be modified
// see comment in properties()
for(size_t i = 0; i < keyConversionsSize; ++i)
if(properties.contains(keyConversions[i][0])) {
properties.insert(keyConversions[i][1], properties[keyConversions[i][0]]);
properties.erase(keyConversions[i][0]);
for(const auto &[k, t] : keyConversions)
if(props.contains(k)) {
props.insert(t, props[k]);
props.erase(k);
}
// first check if tags need to be removed completely
StringList toRemove;
ItemListMap::ConstIterator remIt = itemListMap().begin();
for(; remIt != itemListMap().end(); ++remIt) {
String key = remIt->first.upper();
for(const auto &[k, t] : std::as_const(itemListMap())) {
// only remove if a) key is valid, b) type is text, c) key not contained in new properties
if(!key.isEmpty() && remIt->second.type() == APE::Item::Text && !properties.contains(key))
toRemove.append(remIt->first);
if(String key = k.upper();
!key.isEmpty() && t.type() == APE::Item::Text && !props.contains(key))
toRemove.append(k);
}
for(StringList::ConstIterator removeIt = toRemove.begin(); removeIt != toRemove.end(); removeIt++)
removeItem(*removeIt);
for(const auto &item : std::as_const(toRemove))
removeItem(item);
// now sync in the "forward direction"
PropertyMap::ConstIterator it = properties.begin();
PropertyMap invalid;
for(; it != properties.end(); ++it) {
const String &tagName = it->first;
for(const auto &[tagName, val] : std::as_const(props)) {
if(!checkKey(tagName))
invalid.insert(it->first, it->second);
else if(!(itemListMap().contains(tagName)) || !(itemListMap()[tagName].values() == it->second)) {
if(it->second.isEmpty())
invalid.insert(tagName, val);
else if(!itemListMap().contains(tagName) || itemListMap()[tagName].values() != val) {
if(val.isEmpty())
removeItem(tagName);
else {
StringList::ConstIterator valueIt = it->second.begin();
addValue(tagName, *valueIt, true);
++valueIt;
for(; valueIt != it->second.end(); ++valueIt)
addValue(tagName, *valueIt, false);
addValue(tagName, *val.begin(), true);
for(auto it = std::next(val.begin()); it != val.end(); ++it)
addValue(tagName, *it, false);
}
}
}
return invalid;
}
StringList APE::Tag::complexPropertyKeys() const
{
StringList keys;
if(d->itemListMap.contains(FRONT_COVER) ||
d->itemListMap.contains(BACK_COVER)) {
keys.append("PICTURE");
}
return keys;
}
List<VariantMap> APE::Tag::complexProperties(const String &key) const
{
List<VariantMap> props;
if(const String uppercaseKey = key.upper(); uppercaseKey == "PICTURE") {
const StringList itemNames = StringList(FRONT_COVER).append(BACK_COVER);
for(const auto &itemName: itemNames) {
if(d->itemListMap.contains(itemName)) {
if(Item picture = d->itemListMap.value(itemName);
picture.type() == Item::Binary) {
ByteVector data = picture.binaryData();
// Do not search for a description if the first byte could start JPG or PNG
// data.
int index = data.isEmpty() || data.at(0) == '\xff' || data.at(0) == '\x89'
? -1 : data.find('\0');
String description;
if(index >= 0) {
description = String(data.mid(0, index), String::UTF8);
data = data.mid(index + 1);
}
VariantMap property;
property.insert("data", data);
if(!description.isEmpty()) {
property.insert("description", description);
}
property.insert("pictureType",
itemName == BACK_COVER ? "Back Cover" : "Front Cover");
props.append(property);
}
}
}
}
return props;
}
bool APE::Tag::setComplexProperties(const String &key, const List<VariantMap> &value)
{
if(const String uppercaseKey = key.upper(); uppercaseKey == "PICTURE") {
removeItem(FRONT_COVER);
removeItem(BACK_COVER);
auto frontItems = List<Item>();
auto backItems = List<Item>();
for(const auto &property : value) {
auto data = property.value("description").value<String>().data(String::UTF8)
.append('\0')
.append(property.value("data").value<ByteVector>());
auto pictureType = property.value("pictureType").value<String>();
Item item;
item.setType(Item::Binary);
item.setBinaryData(data);
if(pictureType == "Back Cover") {
item.setKey(BACK_COVER);
backItems.append(item);
}
else if(pictureType == "Front Cover") {
item.setKey(FRONT_COVER);
// prioritize pictures with correct type
frontItems.prepend(item);
}
else {
item.setKey(FRONT_COVER);
frontItems.append(item);
}
}
if(!frontItems.isEmpty()) {
setItem(FRONT_COVER, frontItems.front());
}
if(!backItems.isEmpty()) {
setItem(BACK_COVER, backItems.front());
}
}
else {
return false;
}
return true;
}
bool APE::Tag::checkKey(const String &key)
{
if(!key.isLatin1())
if(key.size() < MinKeyLength || key.size() > MaxKeyLength)
return false;
const std::string data = key.to8Bit(false);
return isKeyValid(data.c_str(), data.size());
return isKeyValid(key.data(String::UTF8));
}
APE::Footer *APE::Tag::footer() const
@@ -329,7 +390,7 @@ void APE::Tag::addValue(const String &key, const String &value, bool replace)
// Text items may contain more than one value.
// Binary or locator items may have only one value, hence always replaced.
ItemListMap::Iterator it = d->itemListMap.find(key.upper());
auto it = d->itemListMap.find(key.upper());
if(it != d->itemListMap.end() && it->second.type() == Item::Text)
it->second.appendValue(value);
@@ -387,8 +448,8 @@ ByteVector APE::Tag::render() const
ByteVector data;
unsigned int itemCount = 0;
for(ItemListMap::ConstIterator it = d->itemListMap.begin(); it != d->itemListMap.end(); ++it) {
data.append(it->second.render());
for(const auto &[_, list] : std::as_const(d->itemListMap)) {
data.append(list.render());
itemCount++;
}
@@ -417,9 +478,17 @@ void APE::Tag::parse(const ByteVector &data)
}
const unsigned int keyLength = nullPos - pos - 8;
const unsigned int valLegnth = data.toUInt(pos, false);
const unsigned int valLength = data.toUInt(pos, false);
if(isKeyValid(&data[pos + 8], keyLength)){
if(valLength >= data.size() || pos > data.size() - valLength) {
debug("APE::Tag::parse() - Invalid val length. Stopped parsing.");
return;
}
if(keyLength >= MinKeyLength
&& keyLength <= MaxKeyLength
&& isKeyValid(data.mid(pos + 8, keyLength)))
{
APE::Item item;
item.parse(data.mid(pos));
@@ -429,6 +498,6 @@ void APE::Tag::parse(const ByteVector &data)
debug("APE::Tag::parse() - Skipped an item due to an invalid key.");
}
pos += keyLength + valLegnth + 9;
pos += keyLength + valLength + 9;
}
}

View File

@@ -26,12 +26,12 @@
#ifndef TAGLIB_APETAG_H
#define TAGLIB_APETAG_H
#include "tag.h"
#include "tbytevector.h"
#include "tmap.h"
#include "tstring.h"
#include "taglib.h"
#include "taglib_export.h"
#include "tag.h"
#include "apeitem.h"
namespace TagLib {
@@ -49,8 +49,7 @@ namespace TagLib {
*
* \see APE::Tag::itemListMap()
*/
typedef Map<const String, Item> ItemListMap;
using ItemListMap = Map<const String, Item>;
//! An APE tag implementation
@@ -66,12 +65,15 @@ namespace TagLib {
* Create an APE tag and parse the data in \a file with APE footer at
* \a tagOffset.
*/
Tag(TagLib::File *file, long footerLocation);
Tag(TagLib::File *file, offset_t footerLocation);
/*!
* Destroys this Tag instance.
*/
virtual ~Tag();
~Tag() override;
Tag(const Tag &) = delete;
Tag &operator=(const Tag &) = delete;
/*!
* Renders the in memory values to a ByteVector suitable for writing to
@@ -87,21 +89,21 @@ namespace TagLib {
// Reimplementations.
virtual String title() const;
virtual String artist() const;
virtual String album() const;
virtual String comment() const;
virtual String genre() const;
virtual unsigned int year() const;
virtual unsigned int track() const;
String title() const override;
String artist() const override;
String album() const override;
String comment() const override;
String genre() const override;
unsigned int year() const override;
unsigned int track() const override;
virtual void setTitle(const String &s);
virtual void setArtist(const String &s);
virtual void setAlbum(const String &s);
virtual void setComment(const String &s);
virtual void setGenre(const String &s);
virtual void setYear(unsigned int i);
virtual void setTrack(unsigned int i);
void setTitle(const String &s) override;
void setArtist(const String &s) override;
void setAlbum(const String &s) override;
void setComment(const String &s) override;
void setGenre(const String &s) override;
void setYear(unsigned int i) override;
void setTrack(unsigned int i) override;
/*!
* Implements the unified tag dictionary interface -- export function.
@@ -115,11 +117,12 @@ namespace TagLib {
*
* The only conversion done by this export function is to rename the APE tags
* TRACK to TRACKNUMBER, YEAR to DATE, and ALBUM ARTIST to ALBUMARTIST, respectively,
* (and a few other keys, see \ref p_propertymapping)
* in order to be compliant with the names used in other formats.
*/
PropertyMap properties() const;
PropertyMap properties() const override;
void removeUnsupportedProperties(const StringList &properties);
void removeUnsupportedProperties(const StringList &properties) override;
/*!
* Implements the unified tag dictionary interface -- import function. The same
@@ -127,7 +130,11 @@ namespace TagLib {
* specification requires keys to have between 2 and 16 printable ASCII characters
* with the exception of the fixed strings "ID3", "TAG", "OGGS", and "MP+".
*/
PropertyMap setProperties(const PropertyMap &);
PropertyMap setProperties(const PropertyMap &) override;
StringList complexPropertyKeys() const override;
List<VariantMap> complexProperties(const String &key) const override;
bool setComplexProperties(const String &key, const List<VariantMap> &value) override;
/*!
* Check if the given String is a valid APE tag key.
@@ -160,7 +167,7 @@ namespace TagLib {
/*!
* Adds to the text item specified by \a key the data \a value. If \a replace
* is true, then all of the other values on the same key will be removed
* is \c true, then all of the other values on the same key will be removed
* first. If a binary item exists for \a key it will be removed first.
*/
void addValue(const String &key, const String &value, bool replace = true);
@@ -179,9 +186,9 @@ namespace TagLib {
void setItem(const String &key, const Item &item);
/*!
* Returns true if the tag does not contain any data.
* Returns \c true if the tag does not contain any data.
*/
bool isEmpty() const;
bool isEmpty() const override;
protected:
@@ -196,13 +203,11 @@ namespace TagLib {
void parse(const ByteVector &data);
private:
Tag(const Tag &);
Tag &operator=(const Tag &);
class TagPrivate;
TagPrivate *d;
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
std::unique_ptr<TagPrivate> d;
};
}
}
} // namespace APE
} // namespace TagLib
#endif

View File

@@ -23,35 +23,29 @@
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#include <taglib.h>
#include <tdebug.h>
#include <trefcounter.h>
#include "asfattribute.h"
#include "tdebug.h"
#include "asffile.h"
#include "asfutils.h"
using namespace TagLib;
class ASF::Attribute::AttributePrivate : public RefCounter
class ASF::Attribute::AttributePrivate
{
public:
AttributePrivate()
: pictureValue(ASF::Picture::fromInvalid()),
stream(0),
language(0) {}
AttributeTypes type;
AttributePrivate() :
pictureValue(ASF::Picture::fromInvalid())
{
}
AttributeTypes type { UnicodeType };
String stringValue;
ByteVector byteVectorValue;
ASF::Picture pictureValue;
union {
unsigned int intValue;
unsigned short shortValue;
unsigned long long longLongValue;
bool boolValue;
};
int stream;
int language;
unsigned long long numericValue { 0 };
int stream { 0 };
int language { 0 };
};
////////////////////////////////////////////////////////////////////////////////
@@ -59,84 +53,72 @@ public:
////////////////////////////////////////////////////////////////////////////////
ASF::Attribute::Attribute() :
d(new AttributePrivate())
d(std::make_shared<AttributePrivate>())
{
d->type = UnicodeType;
}
ASF::Attribute::Attribute(const ASF::Attribute &other) :
d(other.d)
{
d->ref();
}
ASF::Attribute::Attribute(const ASF::Attribute &) = default;
ASF::Attribute::Attribute(const String &value) :
d(new AttributePrivate())
d(std::make_shared<AttributePrivate>())
{
d->type = UnicodeType;
d->stringValue = value;
}
ASF::Attribute::Attribute(const ByteVector &value) :
d(new AttributePrivate())
d(std::make_shared<AttributePrivate>())
{
d->type = BytesType;
d->byteVectorValue = value;
}
ASF::Attribute::Attribute(const ASF::Picture &value) :
d(new AttributePrivate())
d(std::make_shared<AttributePrivate>())
{
d->type = BytesType;
d->pictureValue = value;
}
ASF::Attribute::Attribute(unsigned int value) :
d(new AttributePrivate())
d(std::make_shared<AttributePrivate>())
{
d->type = DWordType;
d->intValue = value;
d->numericValue = value;
}
ASF::Attribute::Attribute(unsigned long long value) :
d(new AttributePrivate())
d(std::make_shared<AttributePrivate>())
{
d->type = QWordType;
d->longLongValue = value;
d->numericValue = value;
}
ASF::Attribute::Attribute(unsigned short value) :
d(new AttributePrivate())
d(std::make_shared<AttributePrivate>())
{
d->type = WordType;
d->shortValue = value;
d->numericValue = value;
}
ASF::Attribute::Attribute(bool value) :
d(new AttributePrivate())
d(std::make_shared<AttributePrivate>())
{
d->type = BoolType;
d->boolValue = value;
d->numericValue = value;
}
ASF::Attribute &ASF::Attribute::operator=(const ASF::Attribute &other)
{
Attribute(other).swap(*this);
return *this;
}
ASF::Attribute &ASF::Attribute::operator=(const ASF::Attribute &) = default;
void ASF::Attribute::swap(Attribute &other)
void ASF::Attribute::swap(Attribute &other) noexcept
{
using std::swap;
swap(d, other.d);
}
ASF::Attribute::~Attribute()
{
if(d->deref())
delete d;
}
ASF::Attribute::~Attribute() = default;
ASF::Attribute::AttributeTypes ASF::Attribute::type() const
{
@@ -157,22 +139,22 @@ ByteVector ASF::Attribute::toByteVector() const
unsigned short ASF::Attribute::toBool() const
{
return d->shortValue;
return d->numericValue ? 1 : 0;
}
unsigned short ASF::Attribute::toUShort() const
{
return d->shortValue;
return static_cast<unsigned short>(d->numericValue);
}
unsigned int ASF::Attribute::toUInt() const
{
return d->intValue;
return static_cast<unsigned int>(d->numericValue);
}
unsigned long long ASF::Attribute::toULongLong() const
{
return d->longLongValue;
return d->numericValue;
}
ASF::Picture ASF::Attribute::toPicture() const
@@ -180,30 +162,30 @@ ASF::Picture ASF::Attribute::toPicture() const
return d->pictureValue;
}
String ASF::Attribute::parse(ASF::File &f, int kind)
String ASF::Attribute::parse(ASF::File &file, int kind)
{
unsigned int size, nameLength;
String name;
d->pictureValue = Picture::fromInvalid();
// extended content descriptor
if(kind == 0) {
nameLength = readWORD(&f);
name = readString(&f, nameLength);
d->type = ASF::Attribute::AttributeTypes(readWORD(&f));
size = readWORD(&f);
nameLength = readWORD(&file);
name = readString(&file, nameLength);
d->type = static_cast<ASF::Attribute::AttributeTypes>(readWORD(&file));
size = readWORD(&file);
}
// metadata & metadata library
else {
int temp = readWORD(&f);
int temp = readWORD(&file);
// metadata library
if(kind == 2) {
d->language = temp;
}
d->stream = readWORD(&f);
nameLength = readWORD(&f);
d->type = ASF::Attribute::AttributeTypes(readWORD(&f));
size = readDWORD(&f);
name = readString(&f, nameLength);
d->stream = readWORD(&file);
nameLength = readWORD(&file);
d->type = static_cast<ASF::Attribute::AttributeTypes>(readWORD(&file));
size = readDWORD(&file);
name = readString(&file, nameLength);
}
if(kind != 2 && size > 65535) {
@@ -212,33 +194,33 @@ String ASF::Attribute::parse(ASF::File &f, int kind)
switch(d->type) {
case WordType:
d->shortValue = readWORD(&f);
d->numericValue = readWORD(&file);
break;
case BoolType:
if(kind == 0) {
d->boolValue = (readDWORD(&f) == 1);
d->numericValue = readDWORD(&file) != 0;
}
else {
d->boolValue = (readWORD(&f) == 1);
d->numericValue = readWORD(&file) != 0;
}
break;
case DWordType:
d->intValue = readDWORD(&f);
d->numericValue = readDWORD(&file);
break;
case QWordType:
d->longLongValue = readQWORD(&f);
d->numericValue = readQWORD(&file);
break;
case UnicodeType:
d->stringValue = readString(&f, size);
d->stringValue = readString(&file, size);
break;
case BytesType:
case GuidType:
d->byteVectorValue = f.readBlock(size);
d->byteVectorValue = file.readBlock(size);
break;
}
@@ -258,7 +240,6 @@ int ASF::Attribute::dataSize() const
case WordType:
return 2;
case BoolType:
return 4;
case DWordType:
return 4;
case QWordType:
@@ -266,8 +247,10 @@ int ASF::Attribute::dataSize() const
case UnicodeType:
return d->stringValue.size() * 2 + 2;
case BytesType:
if(d->pictureValue.isValid())
if(d->pictureValue.isValid()) {
return d->pictureValue.dataSize();
}
return d->byteVectorValue.size();
case GuidType:
return d->byteVectorValue.size();
}
@@ -280,24 +263,24 @@ ByteVector ASF::Attribute::render(const String &name, int kind) const
switch (d->type) {
case WordType:
data.append(ByteVector::fromShort(d->shortValue, false));
data.append(ByteVector::fromShort(toUShort(), false));
break;
case BoolType:
if(kind == 0) {
data.append(ByteVector::fromUInt(d->boolValue ? 1 : 0, false));
data.append(ByteVector::fromUInt(toBool(), false));
}
else {
data.append(ByteVector::fromShort(d->boolValue ? 1 : 0, false));
data.append(ByteVector::fromShort(toBool(), false));
}
break;
case DWordType:
data.append(ByteVector::fromUInt(d->intValue, false));
data.append(ByteVector::fromUInt(toUInt(), false));
break;
case QWordType:
data.append(ByteVector::fromLongLong(d->longLongValue, false));
data.append(ByteVector::fromLongLong(toULongLong(), false));
break;
case UnicodeType:
@@ -307,8 +290,11 @@ ByteVector ASF::Attribute::render(const String &name, int kind) const
case BytesType:
if(d->pictureValue.isValid()) {
data.append(d->pictureValue.render());
break;
}
else {
data.append(d->byteVectorValue);
}
break;
case GuidType:
data.append(d->byteVectorValue);
break;
@@ -316,16 +302,16 @@ ByteVector ASF::Attribute::render(const String &name, int kind) const
if(kind == 0) {
data = renderString(name, true) +
ByteVector::fromShort((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((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

@@ -33,13 +33,13 @@
namespace TagLib
{
namespace ASF
{
class File;
class Picture;
//! Attribute of ASF (WMA) metadata
class TAGLIB_EXPORT Attribute
{
public:
@@ -108,25 +108,25 @@ namespace TagLib
/*!
* Construct an attribute as a copy of \a other.
*/
Attribute(const Attribute &item);
Attribute(const Attribute &other);
/*!
* Copies the contents of \a other into this item.
*/
ASF::Attribute &operator=(const Attribute &other);
Attribute &operator=(const Attribute &other);
/*!
* Exchanges the content of the Attribute by the content of \a other.
* Exchanges the content of the Attribute with the content of \a other.
*/
void swap(Attribute &other);
void swap(Attribute &other) noexcept;
/*!
* Destroys the attribute.
*/
virtual ~Attribute();
~Attribute();
/*!
* Returns type of the value.
* Returns the type of the value.
*/
AttributeTypes type() const;
@@ -199,10 +199,10 @@ namespace TagLib
ByteVector render(const String &name, int kind = 0) const;
class AttributePrivate;
AttributePrivate *d;
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
std::shared_ptr<AttributePrivate> d;
};
}
}
} // namespace ASF
} // namespace TagLib
#endif

View File

@@ -23,12 +23,14 @@
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#include <tdebug.h>
#include <tbytevectorlist.h>
#include <tpropertymap.h>
#include <tstring.h>
#include "asffile.h"
#include <utility>
#include "tdebug.h"
#include "tpropertymap.h"
#include "tbytevectorlist.h"
#include "tagutils.h"
#include "asftag.h"
#include "asfproperties.h"
#include "asfutils.h"
@@ -49,37 +51,28 @@ public:
class MetadataObject;
class MetadataLibraryObject;
FilePrivate():
headerSize(0),
tag(0),
properties(0),
contentDescriptionObject(0),
extendedContentDescriptionObject(0),
headerExtensionObject(0),
metadataObject(0),
metadataLibraryObject(0)
FilePrivate()
{
objects.setAutoDelete(true);
}
~FilePrivate()
{
delete tag;
delete properties;
}
~FilePrivate() = default;
unsigned long long headerSize;
FilePrivate(const FilePrivate &) = delete;
FilePrivate &operator=(const FilePrivate &) = delete;
ASF::Tag *tag;
ASF::Properties *properties;
unsigned long long headerSize { 0 };
std::unique_ptr<ASF::Tag> tag;
std::unique_ptr<ASF::Properties> properties;
List<BaseObject *> objects;
ContentDescriptionObject *contentDescriptionObject;
ExtendedContentDescriptionObject *extendedContentDescriptionObject;
HeaderExtensionObject *headerExtensionObject;
MetadataObject *metadataObject;
MetadataLibraryObject *metadataLibraryObject;
ContentDescriptionObject *contentDescriptionObject { nullptr };
ExtendedContentDescriptionObject *extendedContentDescriptionObject { nullptr };
HeaderExtensionObject *headerExtensionObject { nullptr };
MetadataObject *metadataObject { nullptr };
MetadataLibraryObject *metadataLibraryObject { nullptr };
};
namespace
@@ -96,15 +89,15 @@ namespace
const ByteVector contentEncryptionGuid("\xFB\xB3\x11\x22\x23\xBD\xD2\x11\xB4\xB7\x00\xA0\xC9\x55\xFC\x6E", 16);
const ByteVector extendedContentEncryptionGuid("\x14\xE6\x8A\x29\x22\x26 \x17\x4C\xB9\x35\xDA\xE0\x7E\xE9\x28\x9C", 16);
const ByteVector advancedContentEncryptionGuid("\xB6\x9B\x07\x7A\xA4\xDA\x12\x4E\xA5\xCA\x91\xD3\x8D\xC1\x1A\x8D", 16);
}
} // namespace
class ASF::File::FilePrivate::BaseObject
{
public:
ByteVector data;
virtual ~BaseObject() {}
virtual ~BaseObject() = default;
virtual ByteVector guid() const = 0;
virtual void parse(ASF::File *file, unsigned int size);
virtual void parse(ASF::File *file, long long size);
virtual ByteVector render(ASF::File *file);
};
@@ -113,56 +106,56 @@ class ASF::File::FilePrivate::UnknownObject : public ASF::File::FilePrivate::Bas
ByteVector myGuid;
public:
UnknownObject(const ByteVector &guid);
ByteVector guid() const;
ByteVector guid() const override;
};
class ASF::File::FilePrivate::FilePropertiesObject : public ASF::File::FilePrivate::BaseObject
{
public:
ByteVector guid() const;
void parse(ASF::File *file, unsigned int size);
ByteVector guid() const override;
void parse(ASF::File *file, long long size) override;
};
class ASF::File::FilePrivate::StreamPropertiesObject : public ASF::File::FilePrivate::BaseObject
{
public:
ByteVector guid() const;
void parse(ASF::File *file, unsigned int size);
ByteVector guid() const override;
void parse(ASF::File *file, long long size) override;
};
class ASF::File::FilePrivate::ContentDescriptionObject : public ASF::File::FilePrivate::BaseObject
{
public:
ByteVector guid() const;
void parse(ASF::File *file, unsigned int size);
ByteVector render(ASF::File *file);
ByteVector guid() const override;
void parse(ASF::File *file, long long size) override;
ByteVector render(ASF::File *file) override;
};
class ASF::File::FilePrivate::ExtendedContentDescriptionObject : public ASF::File::FilePrivate::BaseObject
{
public:
ByteVectorList attributeData;
ByteVector guid() const;
void parse(ASF::File *file, unsigned int size);
ByteVector render(ASF::File *file);
ByteVector guid() const override;
void parse(ASF::File *file, long long size) override;
ByteVector render(ASF::File *file) override;
};
class ASF::File::FilePrivate::MetadataObject : public ASF::File::FilePrivate::BaseObject
{
public:
ByteVectorList attributeData;
ByteVector guid() const;
void parse(ASF::File *file, unsigned int size);
ByteVector render(ASF::File *file);
ByteVector guid() const override;
void parse(ASF::File *file, long long size) override;
ByteVector render(ASF::File *file) override;
};
class ASF::File::FilePrivate::MetadataLibraryObject : public ASF::File::FilePrivate::BaseObject
{
public:
ByteVectorList attributeData;
ByteVector guid() const;
void parse(ASF::File *file, unsigned int size);
ByteVector render(ASF::File *file);
ByteVector guid() const override;
void parse(ASF::File *file, long long size) override;
ByteVector render(ASF::File *file) override;
};
class ASF::File::FilePrivate::HeaderExtensionObject : public ASF::File::FilePrivate::BaseObject
@@ -170,16 +163,16 @@ class ASF::File::FilePrivate::HeaderExtensionObject : public ASF::File::FilePriv
public:
List<ASF::File::FilePrivate::BaseObject *> objects;
HeaderExtensionObject();
ByteVector guid() const;
void parse(ASF::File *file, unsigned int size);
ByteVector render(ASF::File *file);
ByteVector guid() const override;
void parse(ASF::File *file, long long size) override;
ByteVector render(ASF::File *file) override;
};
class ASF::File::FilePrivate::CodecListObject : public ASF::File::FilePrivate::BaseObject
{
public:
ByteVector guid() const;
void parse(ASF::File *file, unsigned int size);
ByteVector guid() const override;
void parse(ASF::File *file, long long size) override;
private:
enum CodecType
@@ -190,10 +183,10 @@ private:
};
};
void ASF::File::FilePrivate::BaseObject::parse(ASF::File *file, unsigned int size)
void ASF::File::FilePrivate::BaseObject::parse(ASF::File *file, long long size)
{
data.clear();
if(size > 24 && size <= (unsigned int)(file->length()))
if(size > 24 && size <= file->length())
data = file->readBlock(size - 24);
else
data = ByteVector();
@@ -218,7 +211,7 @@ ByteVector ASF::File::FilePrivate::FilePropertiesObject::guid() const
return filePropertiesGuid;
}
void ASF::File::FilePrivate::FilePropertiesObject::parse(ASF::File *file, unsigned int size)
void ASF::File::FilePrivate::FilePropertiesObject::parse(ASF::File *file, long long size)
{
BaseObject::parse(file, size);
if(data.size() < 64) {
@@ -228,7 +221,8 @@ void ASF::File::FilePrivate::FilePropertiesObject::parse(ASF::File *file, unsign
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
@@ -236,7 +230,7 @@ ByteVector ASF::File::FilePrivate::StreamPropertiesObject::guid() const
return streamPropertiesGuid;
}
void ASF::File::FilePrivate::StreamPropertiesObject::parse(ASF::File *file, unsigned int size)
void ASF::File::FilePrivate::StreamPropertiesObject::parse(ASF::File *file, long long size)
{
BaseObject::parse(file, size);
if(data.size() < 70) {
@@ -256,9 +250,8 @@ ByteVector ASF::File::FilePrivate::ContentDescriptionObject::guid() const
return contentDescriptionGuid;
}
void ASF::File::FilePrivate::ContentDescriptionObject::parse(ASF::File *file, unsigned int /*size*/)
void ASF::File::FilePrivate::ContentDescriptionObject::parse(ASF::File *file, long long /*size*/)
{
file->d->contentDescriptionObject = this;
const int titleLength = readWORD(file);
const int artistLength = readWORD(file);
const int copyrightLength = readWORD(file);
@@ -279,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);
@@ -297,9 +290,8 @@ ByteVector ASF::File::FilePrivate::ExtendedContentDescriptionObject::guid() cons
return extendedContentDescriptionGuid;
}
void ASF::File::FilePrivate::ExtendedContentDescriptionObject::parse(ASF::File *file, unsigned int /*size*/)
void ASF::File::FilePrivate::ExtendedContentDescriptionObject::parse(ASF::File *file, long long /*size*/)
{
file->d->extendedContentDescriptionObject = this;
int count = readWORD(file);
while(count--) {
ASF::Attribute attribute;
@@ -311,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);
}
@@ -321,9 +313,8 @@ ByteVector ASF::File::FilePrivate::MetadataObject::guid() const
return metadataGuid;
}
void ASF::File::FilePrivate::MetadataObject::parse(ASF::File *file, unsigned int /*size*/)
void ASF::File::FilePrivate::MetadataObject::parse(ASF::File *file, long long /*size*/)
{
file->d->metadataObject = this;
int count = readWORD(file);
while(count--) {
ASF::Attribute attribute;
@@ -335,7 +326,7 @@ void ASF::File::FilePrivate::MetadataObject::parse(ASF::File *file, unsigned int
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);
}
@@ -345,9 +336,8 @@ ByteVector ASF::File::FilePrivate::MetadataLibraryObject::guid() const
return metadataLibraryGuid;
}
void ASF::File::FilePrivate::MetadataLibraryObject::parse(ASF::File *file, unsigned int /*size*/)
void ASF::File::FilePrivate::MetadataLibraryObject::parse(ASF::File *file, long long /*size*/)
{
file->d->metadataLibraryObject = this;
int count = readWORD(file);
while(count--) {
ASF::Attribute attribute;
@@ -359,7 +349,7 @@ void ASF::File::FilePrivate::MetadataLibraryObject::parse(ASF::File *file, unsig
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);
}
@@ -374,35 +364,36 @@ ByteVector ASF::File::FilePrivate::HeaderExtensionObject::guid() const
return headerExtensionGuid;
}
void ASF::File::FilePrivate::HeaderExtensionObject::parse(ASF::File *file, unsigned int /*size*/)
void ASF::File::FilePrivate::HeaderExtensionObject::parse(ASF::File *file, long long /*size*/)
{
file->d->headerExtensionObject = this;
file->seek(18, File::Current);
long long dataSize = readDWORD(file);
long long dataPos = 0;
while(dataPos < dataSize) {
ByteVector guid = file->readBlock(16);
if(guid.size() != 16) {
ByteVector uid = file->readBlock(16);
if(uid.size() != 16) {
file->setValid(false);
break;
}
bool ok;
long long size = readQWORD(file, &ok);
if(!ok) {
if(!ok || size < 0 || size > dataSize - dataPos) {
file->setValid(false);
break;
}
BaseObject *obj;
if(guid == metadataGuid) {
obj = new MetadataObject();
if(uid == metadataGuid) {
file->d->metadataObject = new MetadataObject();
obj = file->d->metadataObject;
}
else if(guid == metadataLibraryGuid) {
obj = new MetadataLibraryObject();
else if(uid == metadataLibraryGuid) {
file->d->metadataLibraryObject = new MetadataLibraryObject();
obj = file->d->metadataLibraryObject;
}
else {
obj = new UnknownObject(guid);
obj = new UnknownObject(uid);
}
obj->parse(file, (unsigned int)size);
obj->parse(file, size);
objects.append(obj);
dataPos += size;
}
@@ -411,8 +402,8 @@ void ASF::File::FilePrivate::HeaderExtensionObject::parse(ASF::File *file, unsig
ByteVector ASF::File::FilePrivate::HeaderExtensionObject::render(ASF::File *file)
{
data.clear();
for(List<BaseObject *>::ConstIterator it = objects.begin(); it != objects.end(); ++it) {
data.append((*it)->render(file));
for(const auto &object : std::as_const(objects)) {
data.append(object->render(file));
}
data = ByteVector("\x11\xD2\xD3\xAB\xBA\xA9\xcf\x11\x8E\xE6\x00\xC0\x0C\x20\x53\x65\x06\x00", 18) + ByteVector::fromUInt(data.size(), false) + data;
return BaseObject::render(file);
@@ -423,7 +414,7 @@ ByteVector ASF::File::FilePrivate::CodecListObject::guid() const
return codecListGuid;
}
void ASF::File::FilePrivate::CodecListObject::parse(ASF::File *file, unsigned int size)
void ASF::File::FilePrivate::CodecListObject::parse(ASF::File *file, long long size)
{
BaseObject::parse(file, size);
if(data.size() <= 20) {
@@ -441,7 +432,7 @@ void ASF::File::FilePrivate::CodecListObject::parse(ASF::File *file, unsigned in
if(pos >= data.size())
break;
const CodecType type = static_cast<CodecType>(data.toUShort(pos, false));
const auto type = static_cast<CodecType>(data.toUShort(pos, false));
pos += 2;
int nameLength = data.toUShort(pos, false);
@@ -473,13 +464,25 @@ void ASF::File::FilePrivate::CodecListObject::parse(ASF::File *file, unsigned in
}
}
////////////////////////////////////////////////////////////////////////////////
// static members
////////////////////////////////////////////////////////////////////////////////
bool ASF::File::isSupported(IOStream *stream)
{
// An ASF file has to start with the designated GUID.
const ByteVector id = Utils::readHeader(stream, 16, false);
return id == headerGuid;
}
////////////////////////////////////////////////////////////////////////////////
// public members
////////////////////////////////////////////////////////////////////////////////
ASF::File::File(FileName file, bool, Properties::ReadStyle) :
TagLib::File(file),
d(new FilePrivate())
d(std::make_unique<FilePrivate>())
{
if(isOpen())
read();
@@ -487,20 +490,17 @@ ASF::File::File(FileName file, bool, Properties::ReadStyle) :
ASF::File::File(IOStream *stream, bool, Properties::ReadStyle) :
TagLib::File(stream),
d(new FilePrivate())
d(std::make_unique<FilePrivate>())
{
if(isOpen())
read();
}
ASF::File::~File()
{
delete d;
}
ASF::File::~File() = default;
ASF::Tag *ASF::File::tag() const
{
return d->tag;
return d->tag.get();
}
PropertyMap ASF::File::properties() const
@@ -520,7 +520,7 @@ PropertyMap ASF::File::setProperties(const PropertyMap &properties)
ASF::Properties *ASF::File::audioProperties() const
{
return d->properties;
return d->properties.get();
}
bool ASF::File::save()
@@ -560,21 +560,13 @@ bool ASF::File::save()
d->metadataObject->attributeData.clear();
d->metadataLibraryObject->attributeData.clear();
const AttributeListMap allAttributes = d->tag->attributeListMap();
for(AttributeListMap::ConstIterator it = allAttributes.begin(); it != allAttributes.end(); ++it) {
const String &name = it->first;
const AttributeList &attributes = it->second;
for(const auto &[name, attributes] : std::as_const(d->tag->attributeListMap())) {
bool inExtendedContentDescriptionObject = false;
bool inMetadataObject = false;
for(AttributeList::ConstIterator jt = attributes.begin(); jt != attributes.end(); ++jt) {
const Attribute &attribute = *jt;
const bool largeValue = (attribute.dataSize() > 65535);
const bool guid = (attribute.type() == Attribute::GuidType);
for(const auto &attribute : attributes) {
const bool largeValue = attribute.dataSize() > 65535;
const bool guid = attribute.type() == Attribute::GuidType;
if(!inExtendedContentDescriptionObject && !guid && !largeValue && attribute.language() == 0 && attribute.stream() == 0) {
d->extendedContentDescriptionObject->attributeData.append(attribute.render(name));
@@ -591,8 +583,8 @@ bool ASF::File::save()
}
ByteVector data;
for(List<FilePrivate::BaseObject *>::ConstIterator it = d->objects.begin(); it != d->objects.end(); ++it) {
data.append((*it)->render(this));
for(const auto &object : std::as_const(d->objects)) {
data.append(object->render(this));
}
seek(16);
@@ -616,15 +608,14 @@ void ASF::File::read()
if(!isValid())
return;
ByteVector guid = readBlock(16);
if(guid != headerGuid) {
debug("ASF: Not an ASF file.");
if(readBlock(16) != headerGuid) {
debug("ASF::File::read(): Not an ASF file.");
setValid(false);
return;
}
d->tag = new ASF::Tag();
d->properties = new ASF::Properties();
d->tag = std::make_unique<ASF::Tag>();
d->properties = std::make_unique<ASF::Properties>();
bool ok;
d->headerSize = readQWORD(this, &ok);
@@ -639,32 +630,39 @@ void ASF::File::read()
}
seek(2, Current);
FilePrivate::FilePropertiesObject *filePropertiesObject = nullptr;
FilePrivate::StreamPropertiesObject *streamPropertiesObject = nullptr;
for(int i = 0; i < numObjects; i++) {
guid = readBlock(16);
const ByteVector guid = readBlock(16);
if(guid.size() != 16) {
setValid(false);
break;
}
long size = (long)readQWORD(this, &ok);
auto size = readQWORD(this, &ok);
if(!ok) {
setValid(false);
break;
}
FilePrivate::BaseObject *obj;
if(guid == filePropertiesGuid) {
obj = new FilePrivate::FilePropertiesObject();
filePropertiesObject = new FilePrivate::FilePropertiesObject();
obj = filePropertiesObject;
}
else if(guid == streamPropertiesGuid) {
obj = new FilePrivate::StreamPropertiesObject();
streamPropertiesObject = new FilePrivate::StreamPropertiesObject();
obj = streamPropertiesObject;
}
else if(guid == contentDescriptionGuid) {
obj = new FilePrivate::ContentDescriptionObject();
d->contentDescriptionObject = new FilePrivate::ContentDescriptionObject();
obj = d->contentDescriptionObject;
}
else if(guid == extendedContentDescriptionGuid) {
obj = new FilePrivate::ExtendedContentDescriptionObject();
d->extendedContentDescriptionObject = new FilePrivate::ExtendedContentDescriptionObject();
obj = d->extendedContentDescriptionObject;
}
else if(guid == headerExtensionGuid) {
obj = new FilePrivate::HeaderExtensionObject();
d->headerExtensionObject = new FilePrivate::HeaderExtensionObject();
obj = d->headerExtensionObject;
}
else if(guid == codecListGuid) {
obj = new FilePrivate::CodecListObject();
@@ -680,4 +678,9 @@ void ASF::File::read()
obj->parse(this, size);
d->objects.append(obj);
}
if(!filePropertiesObject || !streamPropertiesObject) {
debug("ASF::File::read(): Missing mandatory header objects.");
setValid(false);
}
}

View File

@@ -26,16 +26,16 @@
#ifndef TAGLIB_ASFFILE_H
#define TAGLIB_ASFFILE_H
#include "tag.h"
#include "tfile.h"
#include "taglib_export.h"
#include "tag.h"
#include "asfproperties.h"
#include "asftag.h"
namespace TagLib {
//! An implementation of ASF (WMA) metadata
namespace ASF {
//! An implementation of TagLib::File with ASF specific methods
/*!
* This implements and provides an interface for ASF files to the
@@ -73,7 +73,10 @@ namespace TagLib {
/*!
* Destroys this instance of the File.
*/
virtual ~File();
~File() override;
File(const File &) = delete;
File &operator=(const File &) = delete;
/*!
* Returns a pointer to the ASF tag of the file.
@@ -85,45 +88,53 @@ namespace TagLib {
* deleted by the user. It will be deleted when the file (object) is
* destroyed.
*/
virtual Tag *tag() const;
Tag *tag() const override;
/*!
* Implements the unified property interface -- export function.
*/
PropertyMap properties() const;
PropertyMap properties() const override;
/*!
* Removes unsupported properties. Forwards to the actual Tag's
* removeUnsupportedProperties() function.
*/
void removeUnsupportedProperties(const StringList &properties);
void removeUnsupportedProperties(const StringList &properties) override;
/*!
* Implements the unified property interface -- import function.
*/
PropertyMap setProperties(const PropertyMap &);
PropertyMap setProperties(const PropertyMap &) override;
/*!
* Returns the ASF audio properties for this file.
*/
virtual Properties *audioProperties() const;
Properties *audioProperties() const override;
/*!
* Save the file.
*
* This returns true if the save was successful.
* This returns \c true if the save was successful.
*/
virtual bool save();
bool save() override;
/*!
* Returns whether or not the given \a stream can be opened as an ASF
* file.
*
* \note This method is designed to do a quick check. The result may
* not necessarily be correct.
*/
static bool isSupported(IOStream *stream);
private:
void read();
class FilePrivate;
FilePrivate *d;
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
std::unique_ptr<FilePrivate> d;
};
}
}
} // namespace ASF
} // namespace TagLib
#endif

View File

@@ -23,18 +23,14 @@
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#include <taglib.h>
#include <tdebug.h>
#include <trefcounter.h>
#include "asfattribute.h"
#include "asffile.h"
#include "asfpicture.h"
#include "asffile.h"
#include "asfutils.h"
using namespace TagLib;
class ASF::Picture::PicturePrivate : public RefCounter
class ASF::Picture::PicturePrivate
{
public:
bool valid;
@@ -49,22 +45,13 @@ public:
////////////////////////////////////////////////////////////////////////////////
ASF::Picture::Picture() :
d(new PicturePrivate())
d(std::make_shared<PicturePrivate>())
{
d->valid = true;
}
ASF::Picture::Picture(const Picture& other) :
d(other.d)
{
d->ref();
}
ASF::Picture::~Picture()
{
if(d->deref())
delete d;
}
ASF::Picture::Picture(const Picture &) = default;
ASF::Picture::~Picture() = default;
bool ASF::Picture::isValid() const
{
@@ -118,13 +105,9 @@ int ASF::Picture::dataSize() const
d->picture.size();
}
ASF::Picture& ASF::Picture::operator=(const ASF::Picture& other)
{
Picture(other).swap(*this);
return *this;
}
ASF::Picture &ASF::Picture::operator=(const ASF::Picture &) = default;
void ASF::Picture::swap(Picture &other)
void ASF::Picture::swap(Picture &other) noexcept
{
using std::swap;
@@ -137,7 +120,7 @@ ByteVector ASF::Picture::render() const
return ByteVector();
return
ByteVector((char)d->type) +
ByteVector(static_cast<char>(d->type)) +
ByteVector::fromUInt(d->picture.size(), false) +
renderString(d->mimeType) +
renderString(d->description) +
@@ -150,7 +133,7 @@ void ASF::Picture::parse(const ByteVector& bytes)
if(bytes.size() < 9)
return;
int pos = 0;
d->type = (Type)bytes[0]; ++pos;
d->type = static_cast<Type>(bytes[0]); ++pos;
const unsigned int dataLen = bytes.toUInt(pos, false); pos+=4;
const ByteVector nullStringTerminator(2, 0);
@@ -172,7 +155,6 @@ void ASF::Picture::parse(const ByteVector& bytes)
d->picture = bytes.mid(pos, dataLen);
d->valid = true;
return;
}
ASF::Picture ASF::Picture::fromInvalid()

View File

@@ -28,8 +28,8 @@
#include "tstring.h"
#include "tbytevector.h"
#include "tpicturetype.h"
#include "taglib_export.h"
#include "attachedpictureframe.h"
namespace TagLib
{
@@ -39,9 +39,9 @@ namespace TagLib
//! An ASF attached picture interface implementation
/*!
* This is an implementation of ASF attached pictures interface. Pictures may be
* This is an implementation of ASF attached pictures. Pictures may be
* included in attributes, one per WM/Picture attribute (but there may be multiple WM/Picture
* attribute in a single tag). These pictures are usually in either JPEG or
* attributes in a single tag). These pictures are usually in either JPEG or
* PNG format.
* \see Attribute::toPicture()
* \see Attribute::Attribute(const Picture& picture)
@@ -49,53 +49,10 @@ namespace TagLib
class TAGLIB_EXPORT Picture {
public:
/*!
/*
* This describes the function or content of the picture.
*/
enum Type {
//! A type not enumerated below
Other = 0x00,
//! 32x32 PNG image that should be used as the file icon
FileIcon = 0x01,
//! File icon of a different size or format
OtherFileIcon = 0x02,
//! Front cover image of the album
FrontCover = 0x03,
//! Back cover image of the album
BackCover = 0x04,
//! Inside leaflet page of the album
LeafletPage = 0x05,
//! Image from the album itself
Media = 0x06,
//! Picture of the lead artist or soloist
LeadArtist = 0x07,
//! Picture of the artist or performer
Artist = 0x08,
//! Picture of the conductor
Conductor = 0x09,
//! Picture of the band or orchestra
Band = 0x0A,
//! Picture of the composer
Composer = 0x0B,
//! Picture of the lyricist or text writer
Lyricist = 0x0C,
//! Picture of the recording location or studio
RecordingLocation = 0x0D,
//! Picture of the artists during recording
DuringRecording = 0x0E,
//! Picture of the artists during performance
DuringPerformance = 0x0F,
//! Picture from a movie or video related to the track
MovieScreenCapture = 0x10,
//! Picture of a large, coloured fish
ColouredFish = 0x11,
//! Illustration related to the track
Illustration = 0x12,
//! Logo of the band or performer
BandLogo = 0x13,
//! Logo of the publisher (record company)
PublisherLogo = 0x14
};
DECLARE_PICTURE_TYPE_ENUM(Type)
/*!
* Constructs an empty picture.
@@ -103,14 +60,14 @@ namespace TagLib
Picture();
/*!
* Construct an picture as a copy of \a other.
* Construct a picture as a copy of \a other.
*/
Picture(const Picture& other);
/*!
* Destroys the picture.
*/
virtual ~Picture();
~Picture();
/*!
* Copies the contents of \a other into this picture.
@@ -118,12 +75,12 @@ namespace TagLib
Picture& operator=(const Picture& other);
/*!
* Exchanges the content of the Picture by the content of \a other.
* Exchanges the content of the Picture with the content of \a other.
*/
void swap(Picture &other);
void swap(Picture &other) noexcept;
/*!
* Returns true if Picture stores valid picture
* Returns \c true if Picture stores valid picture
*/
bool isValid() const;
@@ -178,7 +135,7 @@ namespace TagLib
/*!
* Returns the image data as a ByteVector.
*
* \note ByteVector has a data() method that returns a const char * which
* \note ByteVector has a data() method that returns a <tt>const char *</tt> which
* should make it easy to export this data to external programs.
*
* \see setPicture()
@@ -214,9 +171,10 @@ namespace TagLib
private:
class PicturePrivate;
PicturePrivate *d;
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
std::shared_ptr<PicturePrivate> d;
};
}
}
} // namespace ASF
} // namespace TagLib
#endif // ASFPICTURE_H

View File

@@ -23,8 +23,6 @@
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#include <tdebug.h>
#include <tstring.h>
#include "asfproperties.h"
using namespace TagLib;
@@ -32,24 +30,15 @@ using namespace TagLib;
class ASF::Properties::PropertiesPrivate
{
public:
PropertiesPrivate() :
length(0),
bitrate(0),
sampleRate(0),
channels(0),
bitsPerSample(0),
codec(ASF::Properties::Unknown),
encrypted(false) {}
int length;
int bitrate;
int sampleRate;
int channels;
int bitsPerSample;
ASF::Properties::Codec codec;
int length { 0 };
int bitrate { 0 };
int sampleRate { 0 };
int channels { 0 };
int bitsPerSample { 0 };
ASF::Properties::Codec codec { ASF::Properties::Unknown };
String codecName;
String codecDescription;
bool encrypted;
bool encrypted { false };
};
////////////////////////////////////////////////////////////////////////////////
@@ -58,24 +47,11 @@ public:
ASF::Properties::Properties() :
AudioProperties(AudioProperties::Average),
d(new PropertiesPrivate())
d(std::make_unique<PropertiesPrivate>())
{
}
ASF::Properties::~Properties()
{
delete d;
}
int ASF::Properties::length() const
{
return lengthInSeconds();
}
int ASF::Properties::lengthInSeconds() const
{
return d->length / 1000;
}
ASF::Properties::~Properties() = default;
int ASF::Properties::lengthInMilliseconds() const
{
@@ -126,11 +102,6 @@ bool ASF::Properties::isEncrypted() const
// private members
////////////////////////////////////////////////////////////////////////////////
void ASF::Properties::setLength(int /*length*/)
{
debug("ASF::Properties::setLength() -- This method is deprecated. Do not use.");
}
void ASF::Properties::setLengthInMilliseconds(int value)
{
d->length = value;

View File

@@ -26,21 +26,19 @@
#ifndef TAGLIB_ASFPROPERTIES_H
#define TAGLIB_ASFPROPERTIES_H
#include "audioproperties.h"
#include "tstring.h"
#include "taglib_export.h"
#include "audioproperties.h"
namespace TagLib {
namespace ASF {
//! An implementation of ASF audio properties
class TAGLIB_EXPORT Properties : public AudioProperties
{
public:
/*!
* Audio codec types can be used in ASF file.
* Audio codec types which can be used in ASF files.
*/
enum Codec
{
@@ -78,49 +76,32 @@ namespace TagLib {
/*!
* Destroys this ASF::Properties instance.
*/
virtual ~Properties();
~Properties() override;
/*!
* Returns the length of the file in seconds. The length is rounded down to
* the nearest whole second.
*
* \note This method is just an alias of lengthInSeconds().
*
* \deprecated
*/
virtual int length() const;
/*!
* Returns the length of the file in seconds. The length is rounded down to
* the nearest whole second.
*
* \see lengthInMilliseconds()
*/
// BIC: make virtual
int lengthInSeconds() const;
Properties(const Properties &) = delete;
Properties &operator=(const Properties &) = delete;
/*!
* Returns the length of the file in milliseconds.
*
* \see lengthInSeconds()
*/
// BIC: make virtual
int lengthInMilliseconds() const;
int lengthInMilliseconds() const override;
/*!
* Returns the average bit rate of the file in kb/s.
*/
virtual int bitrate() const;
int bitrate() const override;
/*!
* Returns the sample rate in Hz.
*/
virtual int sampleRate() const;
int sampleRate() const override;
/*!
* Returns the number of audio channels.
*/
virtual int channels() const;
int channels() const override;
/*!
* Returns the number of bits per audio sample.
@@ -160,9 +141,6 @@ namespace TagLib {
bool isEncrypted() const;
#ifndef DO_NOT_DOCUMENT
// deprecated
void setLength(int value);
void setLengthInMilliseconds(int value);
void setBitrate(int value);
void setSampleRate(int value);
@@ -176,11 +154,9 @@ namespace TagLib {
private:
class PropertiesPrivate;
PropertiesPrivate *d;
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
std::unique_ptr<PropertiesPrivate> d;
};
}
}
} // namespace ASF
} // namespace TagLib
#endif

View File

@@ -23,11 +23,29 @@
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#include <tpropertymap.h>
#include "asftag.h"
#include <array>
#include <utility>
#include "tpropertymap.h"
#include "asfattribute.h"
#include "asfpicture.h"
using namespace TagLib;
namespace
{
StringList attributeListToStringList(const ASF::AttributeList &attributes)
{
StringList strs;
for(const auto &attribute : attributes) {
strs.append(attribute.toString());
}
return strs;
}
} // namespace
class ASF::Tag::TagPrivate
{
public:
@@ -39,16 +57,12 @@ public:
AttributeListMap attributeListMap;
};
ASF::Tag::Tag()
: TagLib::Tag()
ASF::Tag::Tag() :
d(std::make_unique<TagPrivate>())
{
d = new TagPrivate;
}
ASF::Tag::~Tag()
{
delete d;
}
ASF::Tag::~Tag() = default;
String ASF::Tag::title() const
{
@@ -63,7 +77,8 @@ String ASF::Tag::artist() const
String ASF::Tag::album() const
{
if(d->attributeListMap.contains("WM/AlbumTitle"))
return d->attributeListMap["WM/AlbumTitle"][0].toString();
return joinTagValues(
attributeListToStringList(d->attributeListMap.value("WM/AlbumTitle")));
return String();
}
@@ -95,8 +110,7 @@ unsigned int ASF::Tag::track() const
const ASF::Attribute attr = d->attributeListMap["WM/TrackNumber"][0];
if(attr.type() == ASF::Attribute::DWordType)
return attr.toUInt();
else
return attr.toString().toInt();
return attr.toString().toInt();
}
if(d->attributeListMap.contains("WM/Track"))
return d->attributeListMap["WM/Track"][0].toUInt();
@@ -106,7 +120,8 @@ unsigned int ASF::Tag::track() const
String ASF::Tag::genre() const
{
if(d->attributeListMap.contains("WM/Genre"))
return d->attributeListMap["WM/Genre"][0].toString();
return joinTagValues(
attributeListToStringList(d->attributeListMap.value("WM/Genre")));
return String();
}
@@ -182,9 +197,9 @@ ASF::AttributeList ASF::Tag::attribute(const String &name) const
void ASF::Tag::setAttribute(const String &name, const Attribute &attribute)
{
AttributeList value;
value.append(attribute);
d->attributeListMap.insert(name, value);
AttributeList val;
val.append(attribute);
d->attributeListMap.insert(name, val);
}
void ASF::Tag::setAttribute(const String &name, const AttributeList &values)
@@ -212,59 +227,73 @@ bool ASF::Tag::isEmpty() const
namespace
{
const char *keyTranslation[][2] = {
{ "WM/AlbumTitle", "ALBUM" },
{ "WM/AlbumArtist", "ALBUMARTIST" },
{ "WM/Composer", "COMPOSER" },
{ "WM/Writer", "WRITER" },
{ "WM/Conductor", "CONDUCTOR" },
{ "WM/ModifiedBy", "REMIXER" },
{ "WM/Year", "DATE" },
{ "WM/OriginalReleaseYear", "ORIGINALDATE" },
{ "WM/Producer", "PRODUCER" },
{ "WM/ContentGroupDescription", "GROUPING" },
{ "WM/SubTitle", "SUBTITLE" },
{ "WM/SetSubTitle", "DISCSUBTITLE" },
{ "WM/TrackNumber", "TRACKNUMBER" },
{ "WM/PartOfSet", "DISCNUMBER" },
{ "WM/Genre", "GENRE" },
{ "WM/BeatsPerMinute", "BPM" },
{ "WM/Mood", "MOOD" },
{ "WM/ISRC", "ISRC" },
{ "WM/Lyrics", "LYRICS" },
{ "WM/Media", "MEDIA" },
{ "WM/Publisher", "LABEL" },
{ "WM/CatalogNo", "CATALOGNUMBER" },
{ "WM/Barcode", "BARCODE" },
{ "WM/EncodedBy", "ENCODEDBY" },
{ "WM/AlbumSortOrder", "ALBUMSORT" },
{ "WM/AlbumArtistSortOrder", "ALBUMARTISTSORT" },
{ "WM/ArtistSortOrder", "ARTISTSORT" },
{ "WM/TitleSortOrder", "TITLESORT" },
{ "WM/Script", "SCRIPT" },
{ "WM/Language", "LANGUAGE" },
{ "MusicBrainz/Track Id", "MUSICBRAINZ_TRACKID" },
{ "MusicBrainz/Artist Id", "MUSICBRAINZ_ARTISTID" },
{ "MusicBrainz/Album Id", "MUSICBRAINZ_ALBUMID" },
{ "MusicBrainz/Album Artist Id", "MUSICBRAINZ_ALBUMARTISTID" },
{ "MusicBrainz/Release Group Id", "MUSICBRAINZ_RELEASEGROUPID" },
{ "MusicBrainz/Work Id", "MUSICBRAINZ_WORKID" },
{ "MusicIP/PUID", "MUSICIP_PUID" },
{ "Acoustid/Id", "ACOUSTID_ID" },
{ "Acoustid/Fingerprint", "ACOUSTID_FINGERPRINT" },
constexpr std::array keyTranslation {
std::pair("WM/AlbumTitle", "ALBUM"),
std::pair("WM/AlbumArtist", "ALBUMARTIST"),
std::pair("WM/AuthorURL", "ARTISTWEBPAGE"),
std::pair("WM/Composer", "COMPOSER"),
std::pair("WM/Writer", "LYRICIST"),
std::pair("WM/Conductor", "CONDUCTOR"),
std::pair("WM/ModifiedBy", "REMIXER"),
std::pair("WM/Year", "DATE"),
std::pair("WM/OriginalAlbumTitle", "ORIGINALALBUM"),
std::pair("WM/OriginalArtist", "ORIGINALARTIST"),
std::pair("WM/OriginalFilename", "ORIGINALFILENAME"),
std::pair("WM/OriginalLyricist", "ORIGINALLYRICIST"),
std::pair("WM/OriginalReleaseYear", "ORIGINALDATE"),
std::pair("WM/Producer", "PRODUCER"),
std::pair("WM/ContentGroupDescription", "WORK"),
std::pair("WM/SubTitle", "SUBTITLE"),
std::pair("WM/SetSubTitle", "DISCSUBTITLE"),
std::pair("WM/TrackNumber", "TRACKNUMBER"),
std::pair("WM/PartOfSet", "DISCNUMBER"),
std::pair("WM/Genre", "GENRE"),
std::pair("WM/BeatsPerMinute", "BPM"),
std::pair("WM/Mood", "MOOD"),
std::pair("WM/InitialKey", "INITIALKEY"),
std::pair("WM/ISRC", "ISRC"),
std::pair("WM/Lyrics", "LYRICS"),
std::pair("WM/Media", "MEDIA"),
std::pair("WM/Publisher", "LABEL"),
std::pair("WM/CatalogNo", "CATALOGNUMBER"),
std::pair("WM/Barcode", "BARCODE"),
std::pair("WM/EncodedBy", "ENCODEDBY"),
std::pair("WM/EncodingSettings", "ENCODING"),
std::pair("WM/EncodingTime", "ENCODINGTIME"),
std::pair("WM/AudioFileURL", "FILEWEBPAGE"),
std::pair("WM/AlbumSortOrder", "ALBUMSORT"),
std::pair("WM/AlbumArtistSortOrder", "ALBUMARTISTSORT"),
std::pair("WM/ArtistSortOrder", "ARTISTSORT"),
std::pair("WM/TitleSortOrder", "TITLESORT"),
std::pair("WM/Script", "SCRIPT"),
std::pair("WM/Language", "LANGUAGE"),
std::pair("WM/ARTISTS", "ARTISTS"),
std::pair("ASIN", "ASIN"),
std::pair("MusicBrainz/Track Id", "MUSICBRAINZ_TRACKID"),
std::pair("MusicBrainz/Artist Id", "MUSICBRAINZ_ARTISTID"),
std::pair("MusicBrainz/Album Id", "MUSICBRAINZ_ALBUMID"),
std::pair("MusicBrainz/Album Artist Id", "MUSICBRAINZ_ALBUMARTISTID"),
std::pair("MusicBrainz/Album Release Country", "RELEASECOUNTRY"),
std::pair("MusicBrainz/Album Status", "RELEASESTATUS"),
std::pair("MusicBrainz/Album Type", "RELEASETYPE"),
std::pair("MusicBrainz/Release Group Id", "MUSICBRAINZ_RELEASEGROUPID"),
std::pair("MusicBrainz/Release Track Id", "MUSICBRAINZ_RELEASETRACKID"),
std::pair("MusicBrainz/Work Id", "MUSICBRAINZ_WORKID"),
std::pair("MusicIP/PUID", "MUSICIP_PUID"),
std::pair("Acoustid/Id", "ACOUSTID_ID"),
std::pair("Acoustid/Fingerprint", "ACOUSTID_FINGERPRINT"),
};
const size_t keyTranslationSize = sizeof(keyTranslation) / sizeof(keyTranslation[0]);
String translateKey(const String &key)
{
for(size_t i = 0; i < keyTranslationSize; ++i) {
if(key == keyTranslation[i][0])
return keyTranslation[i][1];
for(const auto &[k, t] : keyTranslation) {
if(key == k)
return t;
}
return String();
}
}
} // namespace
PropertyMap ASF::Tag::properties() const
{
@@ -283,25 +312,22 @@ PropertyMap ASF::Tag::properties() const
props["COMMENT"] = d->comment;
}
ASF::AttributeListMap::ConstIterator it = d->attributeListMap.begin();
for(; it != d->attributeListMap.end(); ++it) {
const String key = translateKey(it->first);
if(!key.isEmpty()) {
AttributeList::ConstIterator it2 = it->second.begin();
for(; it2 != it->second.end(); ++it2) {
for(const auto &[k, attributes] : std::as_const(d->attributeListMap)) {
if(const String key = translateKey(k); !key.isEmpty()) {
for(const auto &attr : attributes) {
if(key == "TRACKNUMBER") {
if(it2->type() == ASF::Attribute::DWordType)
props.insert(key, String::number(it2->toUInt()));
if(attr.type() == ASF::Attribute::DWordType)
props.insert(key, String::number(attr.toUInt()));
else
props.insert(key, it2->toString());
props.insert(key, attr.toString());
}
else {
props.insert(key, it2->toString());
props.insert(key, attr.toString());
}
}
}
else {
props.unsupportedData().append(it->first);
props.addUnsupportedData(k);
}
}
return props;
@@ -309,70 +335,114 @@ PropertyMap ASF::Tag::properties() const
void ASF::Tag::removeUnsupportedProperties(const StringList &props)
{
StringList::ConstIterator it = props.begin();
for(; it != props.end(); ++it)
d->attributeListMap.erase(*it);
for(const auto &prop : props)
d->attributeListMap.erase(prop);
}
PropertyMap ASF::Tag::setProperties(const PropertyMap &props)
{
static Map<String, String> reverseKeyMap;
if(reverseKeyMap.isEmpty()) {
int numKeys = sizeof(keyTranslation) / sizeof(keyTranslation[0]);
for(int i = 0; i < numKeys; i++) {
reverseKeyMap[keyTranslation[i][1]] = keyTranslation[i][0];
for(const auto &[k, t] : keyTranslation) {
reverseKeyMap[t] = k;
}
}
PropertyMap origProps = properties();
PropertyMap::ConstIterator it = origProps.begin();
for(; it != origProps.end(); ++it) {
if(!props.contains(it->first) || props[it->first].isEmpty()) {
if(it->first == "TITLE") {
const PropertyMap origProps = properties();
for(const auto &[prop, _] : origProps) {
if(!props.contains(prop) || props[prop].isEmpty()) {
if(prop == "TITLE") {
d->title.clear();
}
else if(it->first == "ARTIST") {
else if(prop == "ARTIST") {
d->artist.clear();
}
else if(it->first == "COMMENT") {
else if(prop == "COMMENT") {
d->comment.clear();
}
else if(it->first == "COPYRIGHT") {
else if(prop == "COPYRIGHT") {
d->copyright.clear();
}
else {
d->attributeListMap.erase(reverseKeyMap[it->first]);
d->attributeListMap.erase(reverseKeyMap[prop]);
}
}
}
PropertyMap ignoredProps;
it = props.begin();
for(; it != props.end(); ++it) {
if(reverseKeyMap.contains(it->first)) {
String name = reverseKeyMap[it->first];
for(const auto &[prop, attributes] : props) {
if(reverseKeyMap.contains(prop)) {
String name = reverseKeyMap[prop];
removeItem(name);
StringList::ConstIterator it2 = it->second.begin();
for(; it2 != it->second.end(); ++it2) {
addAttribute(name, *it2);
for(const auto &attr : attributes) {
addAttribute(name, attr);
}
}
else if(it->first == "TITLE") {
d->title = it->second.toString();
else if(prop == "TITLE") {
d->title = attributes.toString();
}
else if(it->first == "ARTIST") {
d->artist = it->second.toString();
else if(prop == "ARTIST") {
d->artist = attributes.toString();
}
else if(it->first == "COMMENT") {
d->comment = it->second.toString();
else if(prop == "COMMENT") {
d->comment = attributes.toString();
}
else if(it->first == "COPYRIGHT") {
d->copyright = it->second.toString();
else if(prop == "COPYRIGHT") {
d->copyright = attributes.toString();
}
else {
ignoredProps.insert(it->first, it->second);
ignoredProps.insert(prop, attributes);
}
}
return ignoredProps;
}
StringList ASF::Tag::complexPropertyKeys() const
{
StringList keys;
if(d->attributeListMap.contains("WM/Picture")) {
keys.append("PICTURE");
}
return keys;
}
List<VariantMap> ASF::Tag::complexProperties(const String &key) const
{
List<VariantMap> props;
if(const String uppercaseKey = key.upper(); uppercaseKey == "PICTURE") {
const AttributeList pictures = d->attributeListMap.value("WM/Picture");
for(const Attribute &attr : pictures) {
ASF::Picture picture = attr.toPicture();
VariantMap property;
property.insert("data", picture.picture());
property.insert("mimeType", picture.mimeType());
property.insert("description", picture.description());
property.insert("pictureType",
ASF::Picture::typeToString(picture.type()));
props.append(property);
}
}
return props;
}
bool ASF::Tag::setComplexProperties(const String &key, const List<VariantMap> &value)
{
if(const String uppercaseKey = key.upper(); uppercaseKey == "PICTURE") {
removeItem("WM/Picture");
for(const auto &property : value) {
ASF::Picture picture;
picture.setPicture(property.value("data").value<ByteVector>());
picture.setMimeType(property.value("mimeType").value<String>());
picture.setDescription(property.value("description").value<String>());
picture.setType(ASF::Picture::typeFromString(
property.value("pictureType").value<String>()));
addAttribute("WM/Picture", Attribute(picture));
}
}
else {
return false;
}
return true;
}

View File

@@ -26,18 +26,20 @@
#ifndef TAGLIB_ASFTAG_H
#define TAGLIB_ASFTAG_H
#include "tag.h"
#include "tlist.h"
#include "tmap.h"
#include "taglib_export.h"
#include "tag.h"
#include "asfattribute.h"
namespace TagLib {
namespace ASF {
typedef List<Attribute> AttributeList;
typedef Map<String, AttributeList> AttributeListMap;
using AttributeList = List<Attribute>;
using AttributeListMap = Map<String, AttributeList>;
//! An implementation of ASF (WMA) tags
class TAGLIB_EXPORT Tag : public TagLib::Tag {
@@ -47,34 +49,37 @@ namespace TagLib {
Tag();
virtual ~Tag();
~Tag() override;
Tag(const Tag &) = delete;
Tag &operator=(const Tag &) = delete;
/*!
* Returns the track name.
*/
virtual String title() const;
String title() const override;
/*!
* Returns the artist name.
*/
virtual String artist() const;
String artist() const override;
/*!
* Returns the album name; if no album name is present in the tag
* String::null will be returned.
* an empty string will be returned.
*/
virtual String album() const;
String album() const override;
/*!
* Returns the track comment.
*/
virtual String comment() const;
String comment() const override;
/*!
* Returns the genre name; if no genre is present in the tag String::null
* Returns the genre name; if no genre is present in the tag an empty string
* will be returned.
*/
virtual String genre() const;
String genre() const override;
/*!
* Returns the rating.
@@ -82,77 +87,79 @@ namespace TagLib {
virtual String rating() const;
/*!
* Returns the genre name; if no genre is present in the tag String::null
* will be returned.
* Returns the copyright information; if no copyright information is
* present in the tag an empty string will be returned.
*/
virtual String copyright() const;
/*!
* Returns the year; if there is no year set, this will return 0.
*/
virtual unsigned int year() const;
unsigned int year() const override;
/*!
* Returns the track number; if there is no track number set, this will
* return 0.
*/
virtual unsigned int track() const;
unsigned int track() const override;
/*!
* Sets the title to \a s.
* Sets the title to \a value.
*/
virtual void setTitle(const String &s);
void setTitle(const String &value) override;
/*!
* Sets the artist to \a s.
* Sets the artist to \a value.
*/
virtual void setArtist(const String &s);
void setArtist(const String &value) override;
/*!
* Sets the album to \a s. If \a s is String::null then this value will be
* Sets the album to \a value. If \a value is an empty string then this value will be
* cleared.
*/
virtual void setAlbum(const String &s);
void setAlbum(const String &value) override;
/*!
* Sets the comment to \a s.
* Sets the comment to \a value.
*/
virtual void setComment(const String &s);
void setComment(const String &value) override;
/*!
* Sets the rating to \a s.
* Sets the rating to \a value.
*/
virtual void setRating(const String &s);
virtual void setRating(const String &value);
/*!
* Sets the copyright to \a s.
* Sets the copyright to \a value.
*/
virtual void setCopyright(const String &s);
virtual void setCopyright(const String &value);
/*!
* Sets the genre to \a s.
* Sets the genre to \a value.
*/
virtual void setGenre(const String &s);
void setGenre(const String &value) override;
/*!
* Sets the year to \a i. If \a s is 0 then this value will be cleared.
* Sets the year to \a value. If \a value is 0 then this value will be cleared.
*/
virtual void setYear(unsigned int i);
void setYear(unsigned int value) override;
/*!
* Sets the track to \a i. If \a s is 0 then this value will be cleared.
* Sets the track to \a value. If \a value is 0 then this value will be cleared.
*/
virtual void setTrack(unsigned int i);
void setTrack(unsigned int value) override;
/*!
* Returns true if the tag does not contain any data. This should be
* Returns \c true if the tag does not contain any data. This should be
* reimplemented in subclasses that provide more than the basic tagging
* abilities in this class.
*/
virtual bool isEmpty() const;
bool isEmpty() const override;
/*!
* \deprecated
* \warning You should not modify this data structure directly, instead
* use attributeListMap() const, contains(), removeItem(),
* attribute(), setAttribute(), addAttribute().
*/
AttributeListMap &attributeListMap();
@@ -163,14 +170,14 @@ namespace TagLib {
const AttributeListMap &attributeListMap() const;
/*!
* \return True if a value for \a attribute is currently set.
* \return \c true if a value for \a key is currently set.
*/
bool contains(const String &name) const;
bool contains(const String &key) const;
/*!
* Removes the \a key attribute from the tag
*/
void removeItem(const String &name);
void removeItem(const String &key);
/*!
* \return The list of values for the key \a name, or an empty list if no
@@ -179,8 +186,8 @@ namespace TagLib {
AttributeList attribute(const String &name) const;
/*!
* Sets the \a key attribute to the value of \a attribute. If an attribute
* with the \a key is already present, it will be replaced.
* Sets the \a name attribute to the value of \a attribute. If an attribute
* with the \a name is already present, it will be replaced.
*/
void setAttribute(const String &name, const Attribute &attribute);
@@ -190,20 +197,25 @@ namespace TagLib {
void setAttribute(const String &name, const AttributeList &values);
/*!
* Sets the \a key attribute to the value of \a attribute. If an attribute
* with the \a key is already present, it will be added to the list.
* Sets the \a name attribute to the value of \a attribute. If an attribute
* with the \a name is already present, it will be added to the list.
*/
void addAttribute(const String &name, const Attribute &attribute);
PropertyMap properties() const;
void removeUnsupportedProperties(const StringList& properties);
PropertyMap setProperties(const PropertyMap &properties);
PropertyMap properties() const override;
void removeUnsupportedProperties(const StringList &props) override;
PropertyMap setProperties(const PropertyMap &props) override;
StringList complexPropertyKeys() const override;
List<VariantMap> complexProperties(const String &key) const override;
bool setComplexProperties(const String &key, const List<VariantMap> &value) override;
private:
class TagPrivate;
TagPrivate *d;
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
std::unique_ptr<TagPrivate> d;
};
}
}
} // namespace ASF
} // namespace TagLib
#endif

View File

@@ -37,7 +37,7 @@ namespace TagLib
namespace
{
inline unsigned short readWORD(File *file, bool *ok = 0)
inline unsigned short readWORD(File *file, bool *ok = nullptr)
{
const ByteVector v = file->readBlock(2);
if(v.size() != 2) {
@@ -48,7 +48,7 @@ namespace TagLib
return v.toUShort(false);
}
inline unsigned int readDWORD(File *file, bool *ok = 0)
inline unsigned int readDWORD(File *file, bool *ok = nullptr)
{
const ByteVector v = file->readBlock(4);
if(v.size() != 4) {
@@ -59,7 +59,7 @@ namespace TagLib
return v.toUInt(false);
}
inline long long readQWORD(File *file, bool *ok = 0)
inline long long readQWORD(File *file, bool *ok = nullptr)
{
const ByteVector v = file->readBlock(8);
if(v.size() != 8) {
@@ -90,14 +90,14 @@ 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;
}
}
}
}
} // namespace
} // namespace ASF
} // namespace TagLib
#endif

View File

@@ -23,142 +23,49 @@
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#include <tbytevector.h>
#include "aiffproperties.h"
#include "apeproperties.h"
#include "asfproperties.h"
#include "flacproperties.h"
#include "mp4properties.h"
#include "mpcproperties.h"
#include "mpegproperties.h"
#include "opusproperties.h"
#include "speexproperties.h"
#include "trueaudioproperties.h"
#include "vorbisproperties.h"
#include "wavproperties.h"
#include "wavpackproperties.h"
#include "audioproperties.h"
using namespace TagLib;
class AudioProperties::AudioPropertiesPrivate
{
};
////////////////////////////////////////////////////////////////////////////////
// public methods
////////////////////////////////////////////////////////////////////////////////
AudioProperties::~AudioProperties()
{
AudioProperties::~AudioProperties() = default;
int AudioProperties::length() const
{
return lengthInSeconds();
}
int AudioProperties::lengthInSeconds() const
{
// This is an ugly workaround but we can't add a virtual function.
// Should be virtual in taglib2.
if(dynamic_cast<const APE::Properties*>(this))
return dynamic_cast<const APE::Properties*>(this)->lengthInSeconds();
else if(dynamic_cast<const ASF::Properties*>(this))
return dynamic_cast<const ASF::Properties*>(this)->lengthInSeconds();
else if(dynamic_cast<const FLAC::Properties*>(this))
return dynamic_cast<const FLAC::Properties*>(this)->lengthInSeconds();
else if(dynamic_cast<const MP4::Properties*>(this))
return dynamic_cast<const MP4::Properties*>(this)->lengthInSeconds();
else if(dynamic_cast<const MPC::Properties*>(this))
return dynamic_cast<const MPC::Properties*>(this)->lengthInSeconds();
else if(dynamic_cast<const MPEG::Properties*>(this))
return dynamic_cast<const MPEG::Properties*>(this)->lengthInSeconds();
else if(dynamic_cast<const Ogg::Opus::Properties*>(this))
return dynamic_cast<const Ogg::Opus::Properties*>(this)->lengthInSeconds();
else if(dynamic_cast<const Ogg::Speex::Properties*>(this))
return dynamic_cast<const Ogg::Speex::Properties*>(this)->lengthInSeconds();
else if(dynamic_cast<const TrueAudio::Properties*>(this))
return dynamic_cast<const TrueAudio::Properties*>(this)->lengthInSeconds();
else if (dynamic_cast<const RIFF::AIFF::Properties*>(this))
return dynamic_cast<const RIFF::AIFF::Properties*>(this)->lengthInSeconds();
else if(dynamic_cast<const RIFF::WAV::Properties*>(this))
return dynamic_cast<const RIFF::WAV::Properties*>(this)->lengthInSeconds();
else if(dynamic_cast<const Vorbis::Properties*>(this))
return dynamic_cast<const Vorbis::Properties*>(this)->lengthInSeconds();
else if(dynamic_cast<const WavPack::Properties*>(this))
return dynamic_cast<const WavPack::Properties*>(this)->lengthInSeconds();
else
return 0;
return lengthInMilliseconds() / 1000;
}
int AudioProperties::lengthInMilliseconds() const
{
// This is an ugly workaround but we can't add a virtual function.
// Should be virtual in taglib2.
return 0;
}
if(dynamic_cast<const APE::Properties*>(this))
return dynamic_cast<const APE::Properties*>(this)->lengthInMilliseconds();
int AudioProperties::bitrate() const
{
return 0;
}
else if(dynamic_cast<const ASF::Properties*>(this))
return dynamic_cast<const ASF::Properties*>(this)->lengthInMilliseconds();
else if(dynamic_cast<const FLAC::Properties*>(this))
return dynamic_cast<const FLAC::Properties*>(this)->lengthInMilliseconds();
else if(dynamic_cast<const MP4::Properties*>(this))
return dynamic_cast<const MP4::Properties*>(this)->lengthInMilliseconds();
else if(dynamic_cast<const MPC::Properties*>(this))
return dynamic_cast<const MPC::Properties*>(this)->lengthInMilliseconds();
else if(dynamic_cast<const MPEG::Properties*>(this))
return dynamic_cast<const MPEG::Properties*>(this)->lengthInMilliseconds();
else if(dynamic_cast<const Ogg::Opus::Properties*>(this))
return dynamic_cast<const Ogg::Opus::Properties*>(this)->lengthInMilliseconds();
else if(dynamic_cast<const Ogg::Speex::Properties*>(this))
return dynamic_cast<const Ogg::Speex::Properties*>(this)->lengthInMilliseconds();
else if(dynamic_cast<const TrueAudio::Properties*>(this))
return dynamic_cast<const TrueAudio::Properties*>(this)->lengthInMilliseconds();
else if(dynamic_cast<const RIFF::AIFF::Properties*>(this))
return dynamic_cast<const RIFF::AIFF::Properties*>(this)->lengthInMilliseconds();
else if(dynamic_cast<const RIFF::WAV::Properties*>(this))
return dynamic_cast<const RIFF::WAV::Properties*>(this)->lengthInMilliseconds();
else if(dynamic_cast<const Vorbis::Properties*>(this))
return dynamic_cast<const Vorbis::Properties*>(this)->lengthInMilliseconds();
else if(dynamic_cast<const WavPack::Properties*>(this))
return dynamic_cast<const WavPack::Properties*>(this)->lengthInMilliseconds();
else
return 0;
int AudioProperties::sampleRate() const
{
return 0;
}
////////////////////////////////////////////////////////////////////////////////
// protected methods
////////////////////////////////////////////////////////////////////////////////
AudioProperties::AudioProperties(ReadStyle) :
d(0)
AudioProperties::AudioProperties(ReadStyle)
{
}

View File

@@ -26,6 +26,9 @@
#ifndef TAGLIB_AUDIOPROPERTIES_H
#define TAGLIB_AUDIOPROPERTIES_H
#include <memory>
#include "taglib.h"
#include "taglib_export.h"
namespace TagLib {
@@ -34,7 +37,7 @@ namespace TagLib {
/*!
* The values here are common to most audio formats. For more specific, codec
* dependent values, please see see the subclasses APIs. This is meant to
* dependent values, please see the subclasses APIs. This is meant to
* compliment the TagLib::File and TagLib::Tag APIs in providing a simple
* interface that is sufficient for most applications.
*/
@@ -64,10 +67,19 @@ namespace TagLib {
*/
virtual ~AudioProperties();
AudioProperties(const AudioProperties &) = delete;
AudioProperties &operator=(const AudioProperties &) = delete;
/*!
* Returns the length of the file in seconds.
*/
virtual int length() const = 0;
* Returns the length of the file in seconds. The length is rounded down to
* the nearest whole second.
*
* \note This method is just an alias of lengthInSeconds().
*
* \deprecated Use lengthInSeconds().
*/
TAGLIB_DEPRECATED
virtual int length() const;
/*!
* Returns the length of the file in seconds. The length is rounded down to
@@ -75,28 +87,26 @@ namespace TagLib {
*
* \see lengthInMilliseconds()
*/
// BIC: make virtual
int lengthInSeconds() const;
virtual int lengthInSeconds() const;
/*!
* Returns the length of the file in milliseconds.
*
* \see lengthInSeconds()
*/
// BIC: make virtual
int lengthInMilliseconds() const;
virtual int lengthInMilliseconds() const;
/*!
* Returns the most appropriate bit rate for the file in kb/s. For constant
* bitrate formats this is simply the bitrate of the file. For variable
* bitrate formats this is either the average or nominal bitrate.
*/
virtual int bitrate() const = 0;
virtual int bitrate() const;
/*!
* Returns the sample rate in Hz.
*/
virtual int sampleRate() const = 0;
virtual int sampleRate() const;
/*!
* Returns the number of audio channels.
@@ -115,13 +125,11 @@ namespace TagLib {
AudioProperties(ReadStyle style);
private:
AudioProperties(const AudioProperties &);
AudioProperties &operator=(const AudioProperties &);
class AudioPropertiesPrivate;
AudioPropertiesPrivate *d;
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
std::unique_ptr<AudioPropertiesPrivate> d;
};
}
} // namespace TagLib
#endif

View File

@@ -0,0 +1,156 @@
/***************************************************************************
copyright : (C) 2016 by Damien Plisson, Audirvana
email : damien78@audirvana.com
***************************************************************************/
/***************************************************************************
* 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 "dsdiffdiintag.h"
#include <utility>
#include "tstringlist.h"
#include "tpropertymap.h"
#include "tdebug.h"
using namespace TagLib;
using namespace DSDIFF::DIIN;
class DSDIFF::DIIN::Tag::TagPrivate
{
public:
String title;
String artist;
};
DSDIFF::DIIN::Tag::Tag() :
d(std::make_unique<TagPrivate>())
{
}
DSDIFF::DIIN::Tag::~Tag() = default;
String DSDIFF::DIIN::Tag::title() const
{
return d->title;
}
String DSDIFF::DIIN::Tag::artist() const
{
return d->artist;
}
String DSDIFF::DIIN::Tag::album() const
{
return String();
}
String DSDIFF::DIIN::Tag::comment() const
{
return String();
}
String DSDIFF::DIIN::Tag::genre() const
{
return String();
}
unsigned int DSDIFF::DIIN::Tag::year() const
{
return 0;
}
unsigned int DSDIFF::DIIN::Tag::track() const
{
return 0;
}
void DSDIFF::DIIN::Tag::setTitle(const String &title)
{
d->title = title;
}
void DSDIFF::DIIN::Tag::setArtist(const String &artist)
{
d->artist = artist;
}
void DSDIFF::DIIN::Tag::setAlbum(const String &)
{
debug("DSDIFF::DIIN::Tag::setAlbum() -- Ignoring unsupported tag.");
}
void DSDIFF::DIIN::Tag::setComment(const String &)
{
debug("DSDIFF::DIIN::Tag::setComment() -- Ignoring unsupported tag.");
}
void DSDIFF::DIIN::Tag::setGenre(const String &)
{
debug("DSDIFF::DIIN::Tag::setGenre() -- Ignoring unsupported tag.");
}
void DSDIFF::DIIN::Tag::setYear(unsigned int)
{
debug("DSDIFF::DIIN::Tag::setYear() -- Ignoring unsupported tag.");
}
void DSDIFF::DIIN::Tag::setTrack(unsigned int)
{
debug("DSDIFF::DIIN::Tag::setTrack() -- Ignoring unsupported tag.");
}
PropertyMap DSDIFF::DIIN::Tag::properties() const
{
PropertyMap properties;
properties["TITLE"] = d->title;
properties["ARTIST"] = d->artist;
return properties;
}
PropertyMap DSDIFF::DIIN::Tag::setProperties(const PropertyMap &origProps)
{
PropertyMap props(origProps);
props.removeEmpty();
StringList oneValueSet;
if(props.contains("TITLE")) {
d->title = props["TITLE"].front();
oneValueSet.append("TITLE");
} else
d->title.clear();
if(props.contains("ARTIST")) {
d->artist = props["ARTIST"].front();
oneValueSet.append("ARTIST");
} else
d->artist.clear();
// for each tag that has been set above, remove the first entry in the corresponding
// value list. The others will be returned as unsupported by this format.
for(const auto &entry : std::as_const(oneValueSet)) {
if(props[entry].size() == 1)
props.erase(entry);
else
props[entry].erase(props[entry].begin());
}
return props;
}

View File

@@ -0,0 +1,147 @@
/***************************************************************************
copyright : (C) 2016 by Damien Plisson, Audirvana
email : damien78@audirvana.com
***************************************************************************/
/***************************************************************************
* 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_DSDIFFDIINTAG_H
#define TAGLIB_DSDIFFDIINTAG_H
#include "tag.h"
namespace TagLib {
namespace DSDIFF {
namespace DIIN {
/*!
* Tags from the Edited Master Chunk Info
*
* Only Title and Artist tags are supported
*/
class TAGLIB_EXPORT Tag : public TagLib::Tag
{
public:
Tag();
~Tag() override;
/*!
* Returns the track name; if no track name is present in the tag
* String() will be returned.
*/
String title() const override;
/*!
* Returns the artist name; if no artist name is present in the tag
* String() will be returned.
*/
String artist() const override;
/*!
* Not supported. Therefore always returns String().
*/
String album() const override;
/*!
* Not supported. Therefore always returns String().
*/
String comment() const override;
/*!
* Not supported. Therefore always returns String().
*/
String genre() const override;
/*!
* Not supported. Therefore always returns 0.
*/
unsigned int year() const override;
/*!
* Not supported. Therefore always returns 0.
*/
unsigned int track() const override;
/*!
* Sets the title to \a title. If \a title is String() then this
* value will be cleared.
*/
void setTitle(const String &title) override;
/*!
* Sets the artist to \a artist. If \a artist is String() then this
* value will be cleared.
*/
void setArtist(const String &artist) override;
/*!
* Not supported and therefore ignored.
*/
void setAlbum(const String &album) override;
/*!
* Not supported and therefore ignored.
*/
void setComment(const String &comment) override;
/*!
* Not supported and therefore ignored.
*/
void setGenre(const String &genre) override;
/*!
* Not supported and therefore ignored.
*/
void setYear(unsigned int year) override;
/*!
* Not supported and therefore ignored.
*/
void setTrack(unsigned int track) override;
/*!
* Implements the unified property interface -- export function.
* Since the DIIN tag is very limited, the exported map is as well.
*/
PropertyMap properties() const override;
/*!
* Implements the unified property interface -- import function.
* Because of the limitations of the DIIN file tag, any tags besides
* TITLE and ARTIST, will be
* returned. Additionally, if the map contains tags with multiple values,
* all but the first will be contained in the returned map of unsupported
* properties.
*/
PropertyMap setProperties(const PropertyMap &) override;
private:
class TagPrivate;
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
std::unique_ptr<TagPrivate> d;
};
} // namespace DIIN
} // namespace DSDIFF
} // namespace TagLib
#endif

View File

@@ -0,0 +1,922 @@
/***************************************************************************
copyright : (C) 2016 by Damien Plisson, Audirvana
email : damien78@audirvana.com
***************************************************************************/
/***************************************************************************
* 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 "dsdifffile.h"
#include <array>
#include <memory>
#include "tbytevector.h"
#include "tstringlist.h"
#include "tpropertymap.h"
#include "tdebug.h"
#include "id3v2tag.h"
#include "tagutils.h"
#include "tagunion.h"
using namespace TagLib;
namespace
{
struct Chunk64
{
ByteVector name;
unsigned long long offset;
unsigned long long size;
char padding;
};
using ChunkList = std::vector<Chunk64>;
int chunkIndex(const ChunkList &chunks, const ByteVector &id)
{
for(size_t i = 0; i < chunks.size(); i++) {
if(chunks[i].name == id)
return static_cast<int>(i);
}
return -1;
}
bool isValidChunkID(const ByteVector &name)
{
if(name.size() != 4)
return false;
return std::none_of(name.cbegin(), name.cend(),
[](unsigned char c) { return c < 32 || c > 126; });
}
enum {
ID3v2Index = 0,
DIINIndex = 1
};
enum {
PROPChunk = 0,
DIINChunk = 1
};
} // namespace
class DSDIFF::File::FilePrivate
{
public:
FilePrivate(const ID3v2::FrameFactory *frameFactory)
: ID3v2FrameFactory(frameFactory ? frameFactory
: ID3v2::FrameFactory::instance())
{
}
~FilePrivate() = default;
const ID3v2::FrameFactory *ID3v2FrameFactory;
Endianness endianness { BigEndian };
ByteVector type;
unsigned long long size { 0 };
ByteVector format;
ChunkList chunks;
std::array<ChunkList, 2> childChunks;
std::array<int, 2> childChunkIndex { -1, -1 };
/*
* Two possibilities can be found: ID3V2 chunk inside PROP chunk or at root level
*/
bool isID3InPropChunk { false };
/*
* ID3 chunks are present. This is then the index of the one in PROP chunk that
* will be removed upon next save to remove duplicates.
*/
int duplicateID3V2chunkIndex { -1 };
std::unique_ptr<Properties> properties;
TagUnion tag;
ByteVector id3v2TagChunkID { "ID3 " };
bool hasID3v2 { false };
bool hasDiin { false };
};
////////////////////////////////////////////////////////////////////////////////
// static members
////////////////////////////////////////////////////////////////////////////////
bool DSDIFF::File::isSupported(IOStream *stream)
{
// A DSDIFF file has to start with "FRM8????????DSD ".
const ByteVector id = Utils::readHeader(stream, 16, false);
return id.startsWith("FRM8") && id.containsAt("DSD ", 12);
}
////////////////////////////////////////////////////////////////////////////////
// public members
////////////////////////////////////////////////////////////////////////////////
DSDIFF::File::File(FileName file, bool readProperties,
Properties::ReadStyle propertiesStyle,
ID3v2::FrameFactory *frameFactory) :
TagLib::File(file),
d(std::make_unique<FilePrivate>(frameFactory))
{
if(isOpen())
read(readProperties, propertiesStyle);
}
DSDIFF::File::File(IOStream *stream, bool readProperties,
Properties::ReadStyle propertiesStyle,
ID3v2::FrameFactory *frameFactory) :
TagLib::File(stream),
d(std::make_unique<FilePrivate>(frameFactory))
{
if(isOpen())
read(readProperties, propertiesStyle);
}
DSDIFF::File::~File() = default;
TagLib::Tag *DSDIFF::File::tag() const
{
return &d->tag;
}
ID3v2::Tag *DSDIFF::File::ID3v2Tag(bool create) const
{
return d->tag.access<ID3v2::Tag>(ID3v2Index, create, d->ID3v2FrameFactory);
}
bool DSDIFF::File::hasID3v2Tag() const
{
return d->hasID3v2;
}
DSDIFF::DIIN::Tag *DSDIFF::File::DIINTag(bool create) const
{
return d->tag.access<DSDIFF::DIIN::Tag>(DIINIndex, create);
}
bool DSDIFF::File::hasDIINTag() const
{
return d->hasDiin;
}
PropertyMap DSDIFF::File::properties() const
{
return d->tag.properties();
}
void DSDIFF::File::removeUnsupportedProperties(const StringList &properties)
{
d->tag.removeUnsupportedProperties(properties);
}
PropertyMap DSDIFF::File::setProperties(const PropertyMap &properties)
{
return ID3v2Tag(true)->setProperties(properties);
}
DSDIFF::Properties *DSDIFF::File::audioProperties() const
{
return d->properties.get();
}
bool DSDIFF::File::save()
{
return save(AllTags);
}
bool DSDIFF::File::save(int tags, StripTags strip, ID3v2::Version version)
{
if(readOnly()) {
debug("DSDIFF::File::save() -- File is read only.");
return false;
}
if(!isValid()) {
debug("DSDIFF::File::save() -- Trying to save invalid file.");
return false;
}
if(strip == StripOthers)
File::strip(~tags);
// First: save ID3V2 chunk
if(const ID3v2::Tag *id3v2Tag = ID3v2Tag(); (tags & ID3v2) && id3v2Tag) {
if(d->isID3InPropChunk) {
if(!id3v2Tag->isEmpty()) {
setChildChunkData(d->id3v2TagChunkID, id3v2Tag->render(version), PROPChunk);
d->hasID3v2 = true;
}
else {
// Empty tag: remove it
setChildChunkData(d->id3v2TagChunkID, ByteVector(), PROPChunk);
d->hasID3v2 = false;
}
}
else {
if(!id3v2Tag->isEmpty()) {
setRootChunkData(d->id3v2TagChunkID, id3v2Tag->render(version));
d->hasID3v2 = true;
}
else {
// Empty tag: remove it
setRootChunkData(d->id3v2TagChunkID, ByteVector());
d->hasID3v2 = false;
}
}
}
// Second: save the DIIN chunk
if(const DSDIFF::DIIN::Tag *diinTag = DIINTag(); (tags & DIIN) && diinTag) {
if(!diinTag->title().isEmpty()) {
ByteVector diinTitle;
diinTitle.append(ByteVector::fromUInt(diinTag->title().size(), d->endianness == BigEndian));
diinTitle.append(ByteVector::fromCString(diinTag->title().toCString()));
setChildChunkData("DITI", diinTitle, DIINChunk);
}
else
setChildChunkData("DITI", ByteVector(), DIINChunk);
if(!diinTag->artist().isEmpty()) {
ByteVector diinArtist;
diinArtist.append(ByteVector::fromUInt(diinTag->artist().size(), d->endianness == BigEndian));
diinArtist.append(ByteVector::fromCString(diinTag->artist().toCString()));
setChildChunkData("DIAR", diinArtist, DIINChunk);
}
else
setChildChunkData("DIAR", ByteVector(), DIINChunk);
}
// Third: remove the duplicate ID3V2 chunk (inside PROP chunk) if any
if(d->duplicateID3V2chunkIndex >= 0) {
setChildChunkData(d->duplicateID3V2chunkIndex, ByteVector(), PROPChunk);
d->duplicateID3V2chunkIndex = -1;
}
return true;
}
void DSDIFF::File::strip(int tags)
{
if(tags & ID3v2) {
removeRootChunk("ID3 ");
removeRootChunk("id3 ");
removeChildChunk("ID3 ", PROPChunk);
removeChildChunk("id3 ", PROPChunk);
d->hasID3v2 = false;
d->tag.set(ID3v2Index, new ID3v2::Tag(nullptr, 0, d->ID3v2FrameFactory));
d->duplicateID3V2chunkIndex = -1;
d->isID3InPropChunk = false;
d->id3v2TagChunkID.setData("ID3 ");
}
if(tags & DIIN) {
removeChildChunk("DITI", DIINChunk);
removeChildChunk("DIAR", DIINChunk);
if(d->childChunks[DIINIndex].empty()) {
removeRootChunk("DIIN");
}
d->hasDiin = false;
d->tag.set(DIINIndex, new DIIN::Tag);
}
}
////////////////////////////////////////////////////////////////////////////////
// private members
////////////////////////////////////////////////////////////////////////////////
void DSDIFF::File::removeRootChunk(unsigned int i)
{
unsigned long long chunkSize = d->chunks[i].size + d->chunks[i].padding + 12;
d->size -= chunkSize;
insert(ByteVector::fromLongLong(d->size, d->endianness == BigEndian), 4, 8);
removeBlock(d->chunks[i].offset - 12, chunkSize);
// Update the internal offsets
d->chunks.erase(d->chunks.begin() + i);
for(int &cci : d->childChunkIndex) {
if(cci > static_cast<int>(i)) {
--cci;
}
}
updateRootChunksStructure(i);
}
void DSDIFF::File::removeRootChunk(const ByteVector &id)
{
int i = chunkIndex(d->chunks, id);
if(i >= 0)
removeRootChunk(i);
}
void DSDIFF::File::setRootChunkData(unsigned int i, const ByteVector &data)
{
if(data.isEmpty()) {
removeRootChunk(i);
return;
}
// Non null data: update chunk
// First we update the global size
d->size += ((data.size() + 1) & ~1) - (d->chunks[i].size + d->chunks[i].padding);
insert(ByteVector::fromLongLong(d->size, d->endianness == BigEndian), 4, 8);
// Now update the specific chunk
writeChunk(d->chunks[i].name,
data,
d->chunks[i].offset - 12,
static_cast<unsigned long>(d->chunks[i].size + d->chunks[i].padding + 12));
d->chunks[i].size = data.size();
d->chunks[i].padding = (data.size() & 0x01) ? 1 : 0;
// Finally update the internal offsets
updateRootChunksStructure(i + 1);
}
void DSDIFF::File::setRootChunkData(const ByteVector &name, const ByteVector &data)
{
if(d->chunks.empty()) {
debug("DSDIFF::File::setRootChunkData('" + name + "') - No valid chunks found.");
return;
}
int i = chunkIndex(d->chunks, name);
if(i >= 0) {
setRootChunkData(i, data);
return;
}
// Couldn't find an existing chunk, so let's create a new one.
i = static_cast<int>(d->chunks.size()) - 1;
unsigned long long offset = d->chunks[i].offset + d->chunks[i].size + d->chunks[i].padding;
// First we update the global size
d->size += (offset & 1) + ((data.size() + 1) & ~1) + 12;
insert(ByteVector::fromLongLong(d->size, d->endianness == BigEndian), 4, 8);
// Now add the chunk to the file
const unsigned long long fileLength = length();
writeChunk(name,
data,
offset,
static_cast<unsigned long>(fileLength > offset ? fileLength - offset : 0),
(offset & 1) ? 1 : 0);
Chunk64 chunk;
chunk.name = name;
chunk.size = data.size();
chunk.offset = offset + 12;
chunk.padding = (data.size() & 0x01) ? 1 : 0;
d->chunks.push_back(chunk);
}
void DSDIFF::File::removeChildChunk(unsigned int i, unsigned int childChunkNum)
{
ChunkList &childChunks = d->childChunks[childChunkNum];
// Update global size
unsigned long long removedChunkTotalSize = childChunks[i].size + childChunks[i].padding + 12;
d->size -= removedChunkTotalSize;
insert(ByteVector::fromLongLong(d->size, d->endianness == BigEndian), 4, 8);
// Update child chunk size
d->chunks[d->childChunkIndex[childChunkNum]].size -= removedChunkTotalSize;
insert(ByteVector::fromLongLong(d->chunks[d->childChunkIndex[childChunkNum]].size,
d->endianness == BigEndian),
d->chunks[d->childChunkIndex[childChunkNum]].offset - 8, 8);
// Remove the chunk
removeBlock(childChunks[i].offset - 12, removedChunkTotalSize);
// Update the internal offsets
// For child chunks
if(i + 1 < childChunks.size()) {
childChunks[i + 1].offset = childChunks[i].offset;
for(unsigned int c = i + 2; c < childChunks.size(); ++c)
childChunks[c].offset = childChunks[c - 1].offset + 12
+ childChunks[c - 1].size + childChunks[c - 1].padding;
}
// And for root chunks
childChunks.erase(childChunks.begin() + i);
updateRootChunksStructure(d->childChunkIndex[childChunkNum] + 1);
}
void DSDIFF::File::removeChildChunk(const ByteVector &id, unsigned int childChunkNum)
{
int i = chunkIndex(d->childChunks[childChunkNum], id);
if(i >= 0)
removeChildChunk(i, childChunkNum);
}
void DSDIFF::File::setChildChunkData(unsigned int i,
const ByteVector &data,
unsigned int childChunkNum)
{
ChunkList &childChunks = d->childChunks[childChunkNum];
if(data.isEmpty()) {
removeChildChunk(i, childChunkNum);
return;
}
// Non null data: update chunk
// First we update the global size
d->size += ((data.size() + 1) & ~1) - (childChunks[i].size + childChunks[i].padding);
insert(ByteVector::fromLongLong(d->size, d->endianness == BigEndian), 4, 8);
// And the PROP chunk size
d->chunks[d->childChunkIndex[childChunkNum]].size +=
((data.size() + 1) & ~1) - (childChunks[i].size + childChunks[i].padding);
insert(ByteVector::fromLongLong(d->chunks[d->childChunkIndex[childChunkNum]].size,
d->endianness == BigEndian),
d->chunks[d->childChunkIndex[childChunkNum]].offset - 8, 8);
// Now update the specific chunk
writeChunk(childChunks[i].name,
data,
childChunks[i].offset - 12,
static_cast<unsigned long>(childChunks[i].size + childChunks[i].padding + 12));
childChunks[i].size = data.size();
childChunks[i].padding = (data.size() & 0x01) ? 1 : 0;
// Now update the internal offsets
// For child Chunks
for(i++; i < childChunks.size(); i++)
childChunks[i].offset = childChunks[i - 1].offset + 12
+ childChunks[i - 1].size + childChunks[i - 1].padding;
// And for root chunks
updateRootChunksStructure(d->childChunkIndex[childChunkNum] + 1);
}
void DSDIFF::File::setChildChunkData(const ByteVector &name,
const ByteVector &data,
unsigned int childChunkNum)
{
ChunkList &childChunks = d->childChunks[childChunkNum];
if(int i = chunkIndex(childChunks, name); i >= 0) {
setChildChunkData(i, data, childChunkNum);
return;
}
// Do not attempt to remove a non existing chunk
if(data.isEmpty())
return;
// Couldn't find an existing chunk, so let's create a new one.
unsigned long long offset = 0;
if(!childChunks.empty()) {
size_t i = childChunks.size() - 1;
offset = childChunks[i].offset + childChunks[i].size + childChunks[i].padding;
}
else if(childChunkNum == DIINChunk) {
int i = d->childChunkIndex[DIINChunk];
if(i < 0) {
setRootChunkData("DIIN", ByteVector());
if(const int lastChunkIndex = static_cast<int>(d->chunks.size()) - 1;
lastChunkIndex >= 0 && d->chunks[lastChunkIndex].name == "DIIN") {
i = lastChunkIndex;
d->childChunkIndex[DIINChunk] = lastChunkIndex;
d->hasDiin = true;
}
}
if(i >= 0) {
offset = d->chunks[i].offset; // 12 is already added in setRootChunkData()
}
}
if(offset == 0) {
debug("DSDIFF::File::setChildChunkData - No valid chunks found.");
return;
}
// First we update the global size
d->size += (offset & 1) + ((data.size() + 1) & ~1) + 12;
insert(ByteVector::fromLongLong(d->size, d->endianness == BigEndian), 4, 8);
// And the child chunk size
d->chunks[d->childChunkIndex[childChunkNum]].size += (offset & 1)
+ ((data.size() + 1) & ~1) + 12;
insert(ByteVector::fromLongLong(d->chunks[d->childChunkIndex[childChunkNum]].size,
d->endianness == BigEndian),
d->chunks[d->childChunkIndex[childChunkNum]].offset - 8, 8);
// Now add the chunk to the file
unsigned long long nextRootChunkIdx = length();
if(d->childChunkIndex[childChunkNum] + 1 < static_cast<int>(d->chunks.size()))
nextRootChunkIdx = d->chunks[d->childChunkIndex[childChunkNum] + 1].offset - 12;
writeChunk(name, data, offset,
static_cast<unsigned long>(
nextRootChunkIdx > offset ? nextRootChunkIdx - offset : 0),
offset & 1 ? 1 : 0);
// For root chunks
updateRootChunksStructure(d->childChunkIndex[childChunkNum] + 1);
Chunk64 chunk;
chunk.name = name;
chunk.size = data.size();
chunk.offset = offset + 12;
chunk.padding = data.size() & 0x01 ? 1 : 0;
childChunks.push_back(chunk);
}
void DSDIFF::File::updateRootChunksStructure(unsigned int startingChunk)
{
for(unsigned int i = startingChunk; i < d->chunks.size(); i++)
d->chunks[i].offset = d->chunks[i - 1].offset + 12
+ d->chunks[i - 1].size + d->chunks[i - 1].padding;
// Update child chunks structure as well
if(d->childChunkIndex[PROPChunk] >= static_cast<int>(startingChunk)) {
if(ChunkList &childChunksToUpdate = d->childChunks[PROPChunk];
!childChunksToUpdate.empty()) {
childChunksToUpdate[0].offset = d->chunks[d->childChunkIndex[PROPChunk]].offset + 12;
for(unsigned int i = 1; i < childChunksToUpdate.size(); i++)
childChunksToUpdate[i].offset = childChunksToUpdate[i - 1].offset + 12
+ childChunksToUpdate[i - 1].size + childChunksToUpdate[i - 1].padding;
}
}
if(d->childChunkIndex[DIINChunk] >= static_cast<int>(startingChunk)) {
if(ChunkList &childChunksToUpdate = d->childChunks[DIINChunk];
!childChunksToUpdate.empty()) {
childChunksToUpdate[0].offset = d->chunks[d->childChunkIndex[DIINChunk]].offset + 12;
for(unsigned int i = 1; i < childChunksToUpdate.size(); i++)
childChunksToUpdate[i].offset = childChunksToUpdate[i - 1].offset + 12
+ childChunksToUpdate[i - 1].size + childChunksToUpdate[i - 1].padding;
}
}
}
void DSDIFF::File::read(bool readProperties, Properties::ReadStyle propertiesStyle)
{
bool bigEndian = d->endianness == BigEndian;
d->type = readBlock(4);
d->size = readBlock(8).toLongLong(bigEndian);
d->format = readBlock(4);
// + 12: chunk header at least, fix for additional junk bytes
while(tell() + 12 <= length()) {
ByteVector chunkName = readBlock(4);
unsigned long long chunkSize = readBlock(8).toLongLong(bigEndian);
if(!isValidChunkID(chunkName)) {
debug("DSDIFF::File::read() -- Chunk '" + chunkName + "' has invalid ID");
setValid(false);
break;
}
if(static_cast<unsigned long long>(tell()) + chunkSize >
static_cast<unsigned long long>(length())) {
debug("DSDIFF::File::read() -- Chunk '" + chunkName
+ "' has invalid size (larger than the file size)");
setValid(false);
break;
}
Chunk64 chunk;
chunk.name = chunkName;
chunk.size = chunkSize;
chunk.offset = tell();
seek(chunk.size, Current);
// Check padding
chunk.padding = 0;
if(offset_t uPosNotPadded = tell(); (uPosNotPadded & 0x01) != 0) {
if(ByteVector iByte = readBlock(1);
iByte.size() != 1 || iByte[0] != 0)
// Not well formed, re-seek
seek(uPosNotPadded, Beginning);
else
chunk.padding = 1;
}
d->chunks.push_back(chunk);
}
// For DSD uncompressed
unsigned long long lengthDSDSamplesTimeChannels = 0;
// For computing bitrate
unsigned long long audioDataSizeinBytes = 0;
// For DST compressed frames
unsigned long dstNumFrames = 0;
// For DST compressed frames
unsigned short dstFrameRate = 0;
for(unsigned int i = 0; i < d->chunks.size(); i++) {
if(d->chunks[i].name == "DSD ") {
lengthDSDSamplesTimeChannels = d->chunks[i].size * 8;
audioDataSizeinBytes = d->chunks[i].size;
}
else if(d->chunks[i].name == "DST ") {
// Now decode the chunks inside the DST chunk to read the DST Frame Information one
long long dstChunkEnd = d->chunks[i].offset + d->chunks[i].size;
seek(d->chunks[i].offset);
audioDataSizeinBytes = d->chunks[i].size;
while(tell() + 12 <= dstChunkEnd) {
ByteVector dstChunkName = readBlock(4);
long long dstChunkSize = readBlock(8).toLongLong(bigEndian);
if(!isValidChunkID(dstChunkName)) {
debug("DSDIFF::File::read() -- DST Chunk '" + dstChunkName + "' has invalid ID");
setValid(false);
break;
}
if(static_cast<long long>(tell()) + dstChunkSize > dstChunkEnd) {
debug("DSDIFF::File::read() -- DST Chunk '" + dstChunkName
+ "' has invalid size (larger than the DST chunk)");
setValid(false);
break;
}
if(dstChunkName == "FRTE") {
// Found the DST frame information chunk
dstNumFrames = readBlock(4).toUInt(bigEndian);
dstFrameRate = readBlock(2).toUShort(bigEndian);
// Found the wanted one, no need to look at the others
break;
}
seek(dstChunkSize, Current);
// Check padding
if(offset_t uPosNotPadded = tell(); (uPosNotPadded & 0x01) != 0) {
if(ByteVector iByte = readBlock(1);
iByte.size() != 1 || iByte[0] != 0)
// Not well formed, re-seek
seek(uPosNotPadded, Beginning);
}
}
}
else if(d->chunks[i].name == "PROP") {
d->childChunkIndex[PROPChunk] = i;
// Now decodes the chunks inside the PROP chunk
long long propChunkEnd = d->chunks[i].offset + d->chunks[i].size;
// +4 to remove the 'SND ' marker at beginning of 'PROP' chunk
seek(d->chunks[i].offset + 4);
while(tell() + 12 <= propChunkEnd) {
ByteVector propChunkName = readBlock(4);
long long propChunkSize = readBlock(8).toLongLong(bigEndian);
if(!isValidChunkID(propChunkName)) {
debug("DSDIFF::File::read() -- PROP Chunk '" + propChunkName + "' has invalid ID");
setValid(false);
break;
}
if(static_cast<long long>(tell()) + propChunkSize > propChunkEnd) {
debug("DSDIFF::File::read() -- PROP Chunk '" + propChunkName
+ "' has invalid size (larger than the PROP chunk)");
setValid(false);
break;
}
Chunk64 chunk;
chunk.name = propChunkName;
chunk.size = propChunkSize;
chunk.offset = tell();
seek(chunk.size, Current);
// Check padding
chunk.padding = 0;
if(offset_t uPosNotPadded = tell(); (uPosNotPadded & 0x01) != 0) {
if(ByteVector iByte = readBlock(1);
iByte.size() != 1 || iByte[0] != 0)
// Not well formed, re-seek
seek(uPosNotPadded, Beginning);
else
chunk.padding = 1;
}
d->childChunks[PROPChunk].push_back(chunk);
}
}
else if(d->chunks[i].name == "DIIN") {
d->childChunkIndex[DIINChunk] = i;
d->hasDiin = true;
// Now decode the chunks inside the DIIN chunk
long long diinChunkEnd = d->chunks[i].offset + d->chunks[i].size;
seek(d->chunks[i].offset);
while(tell() + 12 <= diinChunkEnd) {
ByteVector diinChunkName = readBlock(4);
long long diinChunkSize = readBlock(8).toLongLong(bigEndian);
if(!isValidChunkID(diinChunkName)) {
debug("DSDIFF::File::read() -- DIIN Chunk '" + diinChunkName + "' has invalid ID");
setValid(false);
break;
}
if(static_cast<long long>(tell()) + diinChunkSize > diinChunkEnd) {
debug("DSDIFF::File::read() -- DIIN Chunk '" + diinChunkName
+ "' has invalid size (larger than the DIIN chunk)");
setValid(false);
break;
}
Chunk64 chunk;
chunk.name = diinChunkName;
chunk.size = diinChunkSize;
chunk.offset = tell();
seek(chunk.size, Current);
// Check padding
chunk.padding = 0;
if(offset_t uPosNotPadded = tell(); (uPosNotPadded & 0x01) != 0) {
if(ByteVector iByte = readBlock(1);
iByte.size() != 1 || iByte[0] != 0)
// Not well formed, re-seek
seek(uPosNotPadded, Beginning);
else
chunk.padding = 1;
}
d->childChunks[DIINChunk].push_back(chunk);
}
}
else if(d->chunks[i].name == "ID3 " || d->chunks[i].name == "id3 ") {
d->id3v2TagChunkID = d->chunks[i].name;
d->tag.set(ID3v2Index, new ID3v2::Tag(this, d->chunks[i].offset,
d->ID3v2FrameFactory));
d->isID3InPropChunk = false;
d->hasID3v2 = true;
}
}
if(!isValid())
return;
if(d->childChunkIndex[PROPChunk] < 0) {
debug("DSDIFF::File::read() -- no PROP chunk found");
setValid(false);
return;
}
// Read properties
unsigned int sampleRate = 0;
unsigned short channels = 0;
for(unsigned int i = 0; i < d->childChunks[PROPChunk].size(); i++) {
if(d->childChunks[PROPChunk][i].name == "ID3 " ||
d->childChunks[PROPChunk][i].name == "id3 ") {
if(d->hasID3v2) {
d->duplicateID3V2chunkIndex = i;
// ID3V2 tag has already been found at root level
continue;
}
d->id3v2TagChunkID = d->childChunks[PROPChunk][i].name;
d->tag.set(ID3v2Index, new ID3v2::Tag(this, d->childChunks[PROPChunk][i].offset,
d->ID3v2FrameFactory));
d->isID3InPropChunk = true;
d->hasID3v2 = true;
}
else if(d->childChunks[PROPChunk][i].name == "FS ") {
// Sample rate
seek(d->childChunks[PROPChunk][i].offset);
sampleRate = readBlock(4).toUInt(0, 4, bigEndian);
}
else if(d->childChunks[PROPChunk][i].name == "CHNL") {
// Channels
seek(d->childChunks[PROPChunk][i].offset);
channels = readBlock(2).toShort(0, bigEndian);
}
}
// Read title & artist from DIIN chunk
d->tag.access<DSDIFF::DIIN::Tag>(DIINIndex, true);
if(d->hasDiin) {
for(unsigned int i = 0; i < d->childChunks[DIINChunk].size(); i++) {
if(d->childChunks[DIINChunk][i].name == "DITI") {
seek(d->childChunks[DIINChunk][i].offset);
if(unsigned int titleStrLength = readBlock(4).toUInt(0, 4, bigEndian);
titleStrLength <= d->childChunks[DIINChunk][i].size) {
ByteVector titleStr = readBlock(titleStrLength);
d->tag.access<DSDIFF::DIIN::Tag>(DIINIndex, false)->setTitle(titleStr);
}
}
else if(d->childChunks[DIINChunk][i].name == "DIAR") {
seek(d->childChunks[DIINChunk][i].offset);
if(unsigned int artistStrLength = readBlock(4).toUInt(0, 4, bigEndian);
artistStrLength <= d->childChunks[DIINChunk][i].size) {
ByteVector artistStr = readBlock(artistStrLength);
d->tag.access<DSDIFF::DIIN::Tag>(DIINIndex, false)->setArtist(artistStr);
}
}
}
}
if(readProperties) {
if(lengthDSDSamplesTimeChannels == 0) {
// DST compressed signal : need to compute length of DSD uncompressed frames
if(dstFrameRate > 0)
lengthDSDSamplesTimeChannels = static_cast<unsigned long long>(dstNumFrames) *
static_cast<unsigned long long>(sampleRate) /
static_cast<unsigned long long>(dstFrameRate);
else
lengthDSDSamplesTimeChannels = 0;
}
else {
// In DSD uncompressed files, the read number of samples is the total for each channel
if(channels > 0)
lengthDSDSamplesTimeChannels /= channels;
}
int bitrate = 0;
if(lengthDSDSamplesTimeChannels > 0)
bitrate = static_cast<int>(
audioDataSizeinBytes * 8 * sampleRate / lengthDSDSamplesTimeChannels / 1000);
d->properties = std::make_unique<Properties>(sampleRate, channels,
lengthDSDSamplesTimeChannels, bitrate, propertiesStyle);
}
if(!ID3v2Tag()) {
d->tag.access<ID3v2::Tag>(ID3v2Index, true, d->ID3v2FrameFactory);
// By default, ID3 chunk is at root level
d->isID3InPropChunk = false;
d->hasID3v2 = false;
}
}
void DSDIFF::File::writeChunk(const ByteVector &name, const ByteVector &data,
unsigned long long offset, unsigned long replace,
unsigned int leadingPadding)
{
ByteVector combined;
if(leadingPadding)
combined.append(ByteVector(leadingPadding, '\x00'));
combined.append(name);
combined.append(ByteVector::fromLongLong(data.size(), d->endianness == BigEndian));
combined.append(data);
if((data.size() & 0x01) != 0)
combined.append('\x00');
insert(combined, offset, replace);
}

290
taglib/dsdiff/dsdifffile.h Normal file
View File

@@ -0,0 +1,290 @@
/***************************************************************************
copyright : (C) 2016 by Damien Plisson, Audirvana
email : damien78@audirvana.com
***************************************************************************/
/***************************************************************************
* 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_DSDIFFFILE_H
#define TAGLIB_DSDIFFFILE_H
#include "tfile.h"
#include "id3v2tag.h"
#include "dsdiffproperties.h"
#include "dsdiffdiintag.h"
namespace TagLib {
//! An implementation of DSDIFF metadata
/*!
* This is an implementation of DSDIFF metadata.
*
* This supports an ID3v2 tag as well as reading stream from the ID3 RIFF
* chunk as well as properties from the file.
* Description of the DSDIFF format is available at
* <a href="https://dsd-guide.com/sites/default/files/white-papers/DSDIFF_1.5_Spec.pdf">
* DSDIFF_1.5_Spec.pdf</a>.
* The DSDIFF standard does not explicitly specify the ID3 chunk.
* It can be found at the root level, but also sometimes inside the PROP chunk.
* In addition, title and artist info are stored as part of the standard.
*/
namespace DSDIFF {
//! An implementation of TagLib::File with DSDIFF specific methods.
/*!
* This implements and provides an interface for DSDIFF 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 DSDIFF files.
*/
class TAGLIB_EXPORT File : public TagLib::File
{
public:
/*!
* This set of flags is used for various operations and is suitable for
* being OR-ed together.
*/
enum TagTypes {
//! Empty set. Matches no tag types.
NoTags = 0x0000,
//! Matches DIIN tags.
DIIN = 0x0001,
//! Matches ID3v2 tags.
ID3v2 = 0x0002,
//! Matches all tag types.
AllTags = 0xffff
};
/*!
* Constructs a DSDIFF file from \a file. If \a readProperties is \c true
* the file's audio properties will also be read.
*
* \note In the current implementation, \a propertiesStyle is ignored.
*
* If this file contains an ID3v2 tag, the frames will be created using
* \a frameFactory (default if null).
*/
File(FileName file, bool readProperties = true,
Properties::ReadStyle propertiesStyle = Properties::Average,
ID3v2::FrameFactory *frameFactory = nullptr);
/*!
* Constructs a DSDIFF file from \a stream. If \a readProperties is \c true
* the file's audio properties will also be read.
*
* If this file contains an ID3v2 tag, the frames will be created using
* \a frameFactory (default if null).
*
* \note TagLib will *not* take ownership of the stream, the caller is
* responsible for deleting it after the File object.
*
* \note In the current implementation, \a propertiesStyle is ignored.
*/
File(IOStream *stream, bool readProperties = true,
Properties::ReadStyle propertiesStyle = Properties::Average,
ID3v2::FrameFactory *frameFactory = nullptr);
/*!
* Destroys this instance of the File.
*/
~File() override;
/*!
* Returns a pointer to a tag that is the union of the ID3v2 and DIIN
* tags. The ID3v2 tag is given priority in reading the information -- if
* requested information exists in both the ID3v2 tag and the ID3v1 tag,
* the information from the ID3v2 tag will be returned.
*
* If you would like more granular control over the content of the tags,
* with the concession of generality, use the tag-type specific calls.
*
* \note As this tag is not implemented as an ID3v2 tag or a DIIN tag,
* but a union of the two this pointer may not be cast to the specific
* tag types.
*
* \see ID3v2Tag()
* \see DIINTag()
*/
Tag *tag() const override;
/*!
* Returns the ID3V2 Tag for this file.
*
* \note This always returns a valid pointer regardless of whether or not
* the file on disk has an ID3v2 tag. Use hasID3v2Tag() to check if the
* file on disk actually has an ID3v2 tag.
*
* \see hasID3v2Tag()
*/
ID3v2::Tag *ID3v2Tag(bool create = false) const;
/*!
* Returns the DSDIFF DIIN Tag for this file
*
*/
DSDIFF::DIIN::Tag *DIINTag(bool create = false) const;
/*!
* Implements the unified property interface -- export function.
* This method forwards to ID3v2::Tag::properties().
*/
PropertyMap properties() const override;
void removeUnsupportedProperties(const StringList &properties) override;
/*!
* Implements the unified property interface -- import function.
* This method forwards to ID3v2::Tag::setProperties().
*/
PropertyMap setProperties(const PropertyMap &) override;
/*!
* Returns the AIFF::Properties for this file. If no audio properties
* were read then this will return a null pointer.
*/
Properties *audioProperties() const override;
/*!
* Save the file. If at least one tag -- ID3v1 or DIIN -- exists this
* will duplicate its content into the other tag. This returns \c true
* if saving was successful.
*
* If neither exists or if both tags are empty, this will strip the tags
* from the file.
*
* This is the same as calling save(AllTags);
*
* If you would like more granular control over the content of the tags,
* with the concession of generality, use parameterized save call below.
*
* \see save(int tags)
*/
bool save() override;
/*!
* Save the file. If \a strip is specified, it is possible to choose if
* tags not specified in \a tags should be stripped from the file or
* retained. With \a version, it is possible to specify whether ID3v2.4
* or ID3v2.3 should be used.
*/
bool save(int tags, StripTags strip = StripOthers, ID3v2::Version version = ID3v2::v4);
/*!
* This will strip the tags that match the OR-ed together TagTypes from the
* file. By default it strips all tags. It returns \c true if the tags are
* successfully stripped.
*
* \note This will update the file immediately.
*/
void strip(int tags = AllTags);
/*!
* Returns whether or not the file on disk actually has an ID3v2 tag.
*
* \see ID3v2Tag()
*/
bool hasID3v2Tag() const;
/*!
* Returns whether or not the file on disk actually has the DSDIFF
* title and artist tags.
*
* \see DIINTag()
*/
bool hasDIINTag() const;
/*!
* Returns whether or not the given \a stream can be opened as a DSDIFF
* file.
*
* \note This method is designed to do a quick check. The result may
* not necessarily be correct.
*/
static bool isSupported(IOStream *stream);
protected:
enum Endianness { BigEndian, LittleEndian };
private:
void removeRootChunk(const ByteVector &id);
void removeRootChunk(unsigned int i);
void removeChildChunk(const ByteVector &id, unsigned int childChunkNum);
void removeChildChunk(unsigned int i, unsigned int childChunkNum);
/*!
* Sets the data for the specified chunk at root level to \a data.
*
* \warning This will update the file immediately.
*/
void setRootChunkData(unsigned int i, const ByteVector &data);
/*!
* Sets the data for the root-level chunk \a name to \a data.
* If a root-level chunk with the given name already exists
* it will be overwritten, otherwise it will be
* created after the existing chunks.
*
* \warning This will update the file immediately.
*/
void setRootChunkData(const ByteVector &name, const ByteVector &data);
/*!
* Sets the data for the specified child chunk to \a data.
*
* If data is null, then remove the chunk
*
* \warning This will update the file immediately.
*/
void setChildChunkData(unsigned int i, const ByteVector &data,
unsigned int childChunkNum);
/*!
* Sets the data for the child chunk \a name to \a data. If a chunk with
* the given name already exists it will be overwritten, otherwise it will
* be created after the existing chunks inside the child chunk.
*
* If data is null, then remove the chunks with \a name name
*
* \warning This will update the file immediately.
*/
void setChildChunkData(const ByteVector &name, const ByteVector &data,
unsigned int childChunkNum);
void updateRootChunksStructure(unsigned int startingChunk);
void read(bool readProperties, Properties::ReadStyle propertiesStyle);
void writeChunk(const ByteVector &name, const ByteVector &data,
unsigned long long offset, unsigned long replace = 0,
unsigned int leadingPadding = 0);
class FilePrivate;
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
std::unique_ptr<FilePrivate> d;
};
} // namespace DSDIFF
} // namespace TagLib
#endif

View File

@@ -0,0 +1,100 @@
/***************************************************************************
copyright : (C) 2016 by Damien Plisson, Audirvana
email : damien78@audirvana.com
***************************************************************************/
/***************************************************************************
* 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 "dsdiffproperties.h"
#include "tstring.h"
using namespace TagLib;
class DSDIFF::Properties::PropertiesPrivate
{
public:
int length { 0 };
int bitrate { 0 };
int sampleRate { 0 };
int channels { 0 };
int sampleWidth { 0 };
unsigned long long sampleCount { 0 };
};
////////////////////////////////////////////////////////////////////////////////
// public members
////////////////////////////////////////////////////////////////////////////////
DSDIFF::Properties::Properties(unsigned int sampleRate,
unsigned short channels,
unsigned long long samplesCount,
int bitrate,
ReadStyle style) :
AudioProperties(style),
d(std::make_unique<PropertiesPrivate>())
{
d->channels = channels;
d->sampleCount = samplesCount;
d->sampleWidth = 1;
d->sampleRate = sampleRate;
d->bitrate = bitrate;
d->length = d->sampleRate > 0
? static_cast<int>(static_cast<double>(d->sampleCount) * 1000.0 / d->sampleRate + 0.5)
: 0;
}
DSDIFF::Properties::~Properties() = default;
int DSDIFF::Properties::lengthInSeconds() const
{
return d->length / 1000;
}
int DSDIFF::Properties::lengthInMilliseconds() const
{
return d->length;
}
int DSDIFF::Properties::bitrate() const
{
return d->bitrate;
}
int DSDIFF::Properties::sampleRate() const
{
return d->sampleRate;
}
int DSDIFF::Properties::channels() const
{
return d->channels;
}
int DSDIFF::Properties::bitsPerSample() const
{
return d->sampleWidth;
}
long long DSDIFF::Properties::sampleCount() const
{
return d->sampleCount;
}

View File

@@ -0,0 +1,79 @@
/***************************************************************************
copyright : (C) 2016 by Damien Plisson, Audirvana
email : damien78@audirvana.com
***************************************************************************/
/***************************************************************************
* 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_DSDIFFPROPERTIES_H
#define TAGLIB_DSDIFFPROPERTIES_H
#include "audioproperties.h"
namespace TagLib {
namespace DSDIFF {
class File;
//! An implementation of audio property reading for DSDIFF
/*!
* This reads the data from a DSDIFF stream found in the AudioProperties
* API.
*/
class TAGLIB_EXPORT Properties : public AudioProperties
{
public:
/*!
* Create an instance of DSDIFF::Properties with the data read from the
* ByteVector \a data.
*/
Properties(unsigned int sampleRate, unsigned short channels,
unsigned long long samplesCount, int bitrate,
ReadStyle style);
/*!
* Destroys this DSDIFF::Properties instance.
*/
~Properties() override;
// Reimplementations.
int lengthInSeconds() const override;
int lengthInMilliseconds() const override;
int bitrate() const override;
int sampleRate() const override;
int channels() const override;
int bitsPerSample() const;
long long sampleCount() const;
private:
class PropertiesPrivate;
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
std::unique_ptr<PropertiesPrivate> d;
};
} // namespace DSDIFF
} // namespace TagLib
#endif

230
taglib/dsf/dsffile.cpp Normal file
View File

@@ -0,0 +1,230 @@
/***************************************************************************
copyright : (C) 2013-2023 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 "dsffile.h"
#include "tdebug.h"
#include "tpropertymap.h"
#include "tagutils.h"
using namespace TagLib;
class DSF::File::FilePrivate
{
public:
FilePrivate(const ID3v2::FrameFactory *frameFactory)
: ID3v2FrameFactory(frameFactory ? frameFactory
: ID3v2::FrameFactory::instance())
{
}
~FilePrivate() = default;
FilePrivate(const FilePrivate &) = delete;
FilePrivate &operator=(const FilePrivate &) = delete;
const ID3v2::FrameFactory *ID3v2FrameFactory;
long long fileSize = 0;
long long metadataOffset = 0;
std::unique_ptr<Properties> properties;
std::unique_ptr<ID3v2::Tag> tag;
};
bool DSF::File::isSupported(IOStream *stream)
{
// A DSF file has to start with "DSD "
const ByteVector id = Utils::readHeader(stream, 4, false);
return id.startsWith("DSD ");
}
DSF::File::File(FileName file, bool,
AudioProperties::ReadStyle propertiesStyle,
ID3v2::FrameFactory *frameFactory) :
TagLib::File(file),
d(std::make_unique<FilePrivate>(frameFactory))
{
if(isOpen())
read(propertiesStyle);
}
DSF::File::File(IOStream *stream, bool,
AudioProperties::ReadStyle propertiesStyle,
ID3v2::FrameFactory *frameFactory) :
TagLib::File(stream),
d(std::make_unique<FilePrivate>(frameFactory))
{
if(isOpen())
read(propertiesStyle);
}
DSF::File::~File() = default;
ID3v2::Tag *DSF::File::tag() const
{
return d->tag.get();
}
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.get();
}
bool DSF::File::save()
{
return save(ID3v2::v4);
}
bool DSF::File::save(ID3v2::Version version)
{
if(readOnly()) {
debug("DSF::File::save() - Cannot save to a read only file.");
return false;
}
// Three things must be updated: the file size, the tag data, and the metadata offset
if(d->tag->isEmpty()) {
long long newFileSize = d->metadataOffset ? d->metadataOffset : d->fileSize;
// Update the file size
if(d->fileSize != newFileSize) {
insert(ByteVector::fromLongLong(newFileSize, false), 12, 8);
d->fileSize = newFileSize;
}
// Update the metadata offset to 0 since there is no longer a tag
if(d->metadataOffset) {
insert(ByteVector::fromLongLong(0ULL, false), 20, 8);
d->metadataOffset = 0;
}
// Delete the old tag
truncate(newFileSize);
}
else {
ByteVector tagData = d->tag->render(version);
long long newMetadataOffset = d->metadataOffset ? d->metadataOffset : d->fileSize;
long long newFileSize = newMetadataOffset + tagData.size();
long long oldTagSize = d->fileSize - newMetadataOffset;
// Update the file size
if(d->fileSize != newFileSize) {
insert(ByteVector::fromLongLong(newFileSize, false), 12, 8);
d->fileSize = newFileSize;
}
// Update the metadata offset
if(d->metadataOffset != newMetadataOffset) {
insert(ByteVector::fromLongLong(newMetadataOffset, false), 20, 8);
d->metadataOffset = newMetadataOffset;
}
// Delete the old tag and write the new one
insert(tagData, newMetadataOffset, static_cast<size_t>(oldTagSize));
}
return true;
}
void DSF::File::read(AudioProperties::ReadStyle propertiesStyle)
{
if(!isOpen())
return;
// 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.");
setValid(false);
return;
}
long long dsdHeaderSize = readBlock(8).toLongLong(false);
// Integrity check
if(dsdHeaderSize != 28) {
debug("DSF::File::read() -- File is corrupted, wrong DSD header size");
setValid(false);
return;
}
d->fileSize = readBlock(8).toLongLong(false);
// File is malformed or corrupted, allow trailing garbage
if(d->fileSize > length()) {
debug("DSF::File::read() -- File is corrupted wrong length");
setValid(false);
return;
}
d->metadataOffset = readBlock(8).toLongLong(false);
// File is malformed or corrupted
if(d->metadataOffset > d->fileSize) {
debug("DSF::File::read() -- Invalid metadata offset.");
setValid(false);
return;
}
// Format chunk
chunkName = readBlock(4);
if(chunkName != "fmt ") {
debug("DSF::File::read() -- Missing 'fmt ' chunk.");
setValid(false);
return;
}
long long fmtHeaderSize = readBlock(8).toLongLong(false);
if(fmtHeaderSize != 52) {
debug("DSF::File::read() -- File is corrupted, wrong FMT header size");
setValid(false);
return;
}
d->properties = std::make_unique<Properties>(readBlock(fmtHeaderSize), propertiesStyle);
// Skip the data chunk
// A metadata offset of 0 indicates the absence of an ID3v2 tag
if(d->metadataOffset == 0)
d->tag = std::make_unique<ID3v2::Tag>(nullptr, 0, d->ID3v2FrameFactory);
else
d->tag = std::make_unique<ID3v2::Tag>(this, d->metadataOffset,
d->ID3v2FrameFactory);
}

156
taglib/dsf/dsffile.h Normal file
View File

@@ -0,0 +1,156 @@
/***************************************************************************
copyright : (C) 2013-2023 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 <memory>
#include "taglib_export.h"
#include "tfile.h"
#include "dsfproperties.h"
#include "id3v2tag.h"
namespace TagLib {
//! An implementation of DSF metadata
/*!
* This is an implementation of DSF metadata using an ID3v2 tag inside the
* metadata chunk.
* The DSF specification is located at
* http://dsd-guide.com/sites/default/files/white-papers/DSFFileFormatSpec_E.pdf
*/
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:
/*!
* Constructs a DSD stream file from \a file.
*
* \note In the current implementation, both \a readProperties and
* \a propertiesStyle are ignored. The audio properties are always
* read.
*
* If this file contains an ID3v2 tag, the frames will be created using
* \a frameFactory (default if null).
*/
File(FileName file, bool readProperties = true,
AudioProperties::ReadStyle propertiesStyle =
AudioProperties::Average,
ID3v2::FrameFactory *frameFactory = nullptr);
/*!
* Constructs a DSD stream file from \a stream.
*
* \note In the current implementation, both \a readProperties and
* \a propertiesStyle are ignored. The audio properties are always
* read.
*
* If this file contains an ID3v2 tag, the frames will be created using
* \a frameFactory (default if null).
*
* \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,
AudioProperties::ReadStyle propertiesStyle =
AudioProperties::Average,
ID3v2::FrameFactory *frameFactory = nullptr);
/*!
* Destroys this instance of the File.
*/
~File() override;
/*!
* Returns the ID3v2 Tag for this file.
*/
ID3v2::Tag *tag() const override;
/*!
* Implements the unified property interface -- export function.
* Forwards to ID3v2::Tag::properties().
*/
PropertyMap properties() const override;
/*!
* Implements the unified property interface -- import function.
* Forwards to ID3v2::Tag::setProperties().
*/
PropertyMap setProperties(const PropertyMap &) override;
/*!
* Returns the DSF::Properties for this file. If no audio properties
* were read then this will return a null pointer.
*/
Properties *audioProperties() const override;
/*!
* Save the file.
*
* This returns \c true if the save was successful.
*/
bool save() override;
/*!
* Save the file.
*
* \a version specifies the ID3v2 version to be used for writing tags.
*/
bool save(ID3v2::Version version);
/*!
* Returns whether or not the given \a stream can be opened as a DSF
* file.
*
* \note This method is designed to do a quick check. The result may
* not necessarily be correct.
*/
static bool isSupported(IOStream *stream);
private:
void read(AudioProperties::ReadStyle propertiesStyle);
class FilePrivate;
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
std::unique_ptr<FilePrivate> d;
};
} // namespace DSF
} // namespace TagLib
#endif

View File

@@ -0,0 +1,134 @@
/***************************************************************************
copyright : (C) 2013-2023 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 "dsfproperties.h"
using namespace TagLib;
class DSF::Properties::PropertiesPrivate
{
public:
PropertiesPrivate() = default;
~PropertiesPrivate() = default;
PropertiesPrivate(const PropertiesPrivate &) = delete;
PropertiesPrivate &operator=(const PropertiesPrivate &) = delete;
// Nomenclature is from DSF file format specification
unsigned int formatVersion = 0;
unsigned int formatID = 0;
unsigned int channelType = 0;
unsigned int channelNum = 0;
unsigned int samplingFrequency = 0;
unsigned int bitsPerSample = 0;
long long sampleCount = 0;
unsigned int blockSizePerChannel = 0;
// Computed
unsigned int bitrate = 0;
unsigned int length = 0;
};
DSF::Properties::Properties(const ByteVector &data, ReadStyle style) :
AudioProperties(style),
d(std::make_unique<PropertiesPrivate>())
{
read(data);
}
DSF::Properties::~Properties() = default;
int DSF::Properties::lengthInMilliseconds() 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;
}
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.toUInt(0U,false);
d->formatID = data.toUInt(4U,false);
d->channelType = data.toUInt(8U,false);
d->channelNum = data.toUInt(12U,false);
d->samplingFrequency = data.toUInt(16U,false);
d->bitsPerSample = data.toUInt(20U,false);
d->sampleCount = data.toLongLong(24U,false);
d->blockSizePerChannel = data.toUInt(32U,false);
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>(static_cast<double>(d->sampleCount) * 1000.0 / d->samplingFrequency + 0.5)
: 0;
}

View File

@@ -0,0 +1,73 @@
/***************************************************************************
copyright : (C) 2013-2023 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 <memory>
#include "taglib_export.h"
#include "tbytevector.h"
#include "audioproperties.h"
namespace TagLib {
namespace DSF {
//! An implementation of audio properties for DSF
class TAGLIB_EXPORT Properties : public AudioProperties {
public:
Properties(const ByteVector &data, ReadStyle style);
~Properties() override;
Properties(const Properties &) = delete;
Properties &operator=(const Properties &) = delete;
int lengthInMilliseconds() const override;
int bitrate() const override;
int sampleRate() const override;
int channels() const override;
int formatVersion() const;
int formatID() const;
/*!
* Channel type values: 1 = mono, 2 = stereo, 3 = 3 channels,
* 4 = quad, 5 = 4 channels, 6 = 5 channels, 7 = 5.1 channels
*/
int channelType() const;
int bitsPerSample() const;
long long sampleCount() const;
int blockSizePerChannel() const;
private:
void read(const ByteVector &data);
class PropertiesPrivate;
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
std::unique_ptr<PropertiesPrivate> d;
};
} // namespace DSF
} // namespace TagLib
#endif

View File

@@ -27,105 +27,122 @@
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#include <tfile.h>
#include <tstring.h>
#include <tdebug.h>
#include <trefcounter.h>
#include "fileref.h"
#include "asffile.h"
#include <cstring>
#include <utility>
#include "taglib_config.h"
#include "tfilestream.h"
#include "tpropertymap.h"
#include "tstringlist.h"
#include "tvariant.h"
#include "tdebug.h"
#include "mpegfile.h"
#include "vorbisfile.h"
#include "flacfile.h"
#include "oggflacfile.h"
#include "mpcfile.h"
#include "mp4file.h"
#include "wavpackfile.h"
#include "speexfile.h"
#include "opusfile.h"
#include "trueaudiofile.h"
#ifdef TAGLIB_WITH_RIFF
#include "aifffile.h"
#include "wavfile.h"
#endif
#ifdef TAGLIB_WITH_APE
#include "apefile.h"
#include "modfile.h"
#include "s3mfile.h"
#include "mpcfile.h"
#include "wavpackfile.h"
#endif
#ifdef TAGLIB_WITH_ASF
#include "asffile.h"
#endif
#ifdef TAGLIB_WITH_VORBIS
#include "flacfile.h"
#include "speexfile.h"
#include "vorbisfile.h"
#include "oggflacfile.h"
#include "opusfile.h"
#endif
#ifdef TAGLIB_WITH_MOD
#include "itfile.h"
#include "modfile.h"
#include "xmfile.h"
#include "s3mfile.h"
#endif
#ifdef TAGLIB_WITH_MP4
#include "mp4file.h"
#endif
#ifdef TAGLIB_WITH_TRUEAUDIO
#include "trueaudiofile.h"
#endif
#ifdef TAGLIB_WITH_DSF
#include "dsffile.h"
#include "dsdifffile.h"
#endif
#ifdef TAGLIB_WITH_SHORTEN
#include "shortenfile.h"
#endif
#ifdef TAGLIB_WITH_MATROSKA
#include "matroskafile.h"
#endif
using namespace TagLib;
class FileRef::FileTypeResolver::FileTypeResolverPrivate
{
};
class FileRef::StreamTypeResolver::StreamTypeResolverPrivate
{
};
namespace
{
typedef List<const FileRef::FileTypeResolver *> ResolverList;
ResolverList fileTypeResolvers;
List<const FileRef::FileTypeResolver *> fileTypeResolvers;
// Templatized internal functions. T should be String or IOStream*.
// Detect the file type by user-defined resolvers.
template <typename T>
FileName toFileName(T arg)
File *detectByResolvers(FileName fileName, bool readAudioProperties,
AudioProperties::ReadStyle audioPropertiesStyle)
{
debug("FileRef::toFileName<T>(): This version should never be called.");
return FileName(L"");
}
template <>
FileName toFileName<IOStream *>(IOStream *arg)
{
return arg->name();
}
template <>
FileName toFileName<FileName>(FileName arg)
{
return arg;
}
template <typename T>
File *resolveFileType(T arg, bool readProperties,
AudioProperties::ReadStyle style)
{
debug("FileRef::resolveFileType<T>(): This version should never be called.");
return 0;
}
template <>
File *resolveFileType<IOStream *>(IOStream *arg, bool readProperties,
AudioProperties::ReadStyle style)
{
return 0;
}
template <>
File *resolveFileType<FileName>(FileName arg, bool readProperties,
AudioProperties::ReadStyle style)
{
ResolverList::ConstIterator it = fileTypeResolvers.begin();
for(; it != fileTypeResolvers.end(); ++it) {
File *file = (*it)->createFile(arg, readProperties, style);
#ifdef _WIN32
if(::wcslen(fileName) == 0)
return nullptr;
#else
if(::strlen(fileName) == 0)
return nullptr;
#endif
for(const auto &resolver : std::as_const(fileTypeResolvers)) {
File *file = resolver->createFile(fileName, readAudioProperties, audioPropertiesStyle);
if(file)
return file;
}
return 0;
return nullptr;
}
template <typename T>
File* createInternal(T arg, bool readAudioProperties,
AudioProperties::ReadStyle audioPropertiesStyle)
File *detectByResolvers(IOStream* stream, bool readAudioProperties,
AudioProperties::ReadStyle audioPropertiesStyle)
{
File *file = resolveFileType(arg, readAudioProperties, audioPropertiesStyle);
if(file)
return file;
for(const auto &resolver : std::as_const(fileTypeResolvers)) {
if(auto streamResolver = dynamic_cast<const FileRef::StreamTypeResolver *>(resolver)) {
if(File *file = streamResolver->createFileFromStream(
stream, readAudioProperties, audioPropertiesStyle))
return file;
}
}
return nullptr;
}
// Detect the file type based on the file extension.
File* detectByExtension(IOStream *stream, bool readAudioProperties,
AudioProperties::ReadStyle audioPropertiesStyle)
{
#ifdef _WIN32
const String s = toFileName(arg).toString();
const String s = stream->name().toString();
#else
const String s(toFileName(arg));
const String s(stream->name());
#endif
String ext;
const int pos = s.rfind(".");
if(pos != -1)
if(const int pos = s.rfind("."); pos != -1)
ext = s.substr(pos + 1).upper();
// If this list is updated, the method defaultFileExtensions() should also be
@@ -133,68 +150,199 @@ namespace
// that a default file type resolver is created.
if(ext.isEmpty())
return 0;
return nullptr;
if(ext == "MP3")
return new MPEG::File(arg, ID3v2::FrameFactory::instance(), readAudioProperties, audioPropertiesStyle);
if(ext == "OGG")
return new Ogg::Vorbis::File(arg, readAudioProperties, audioPropertiesStyle);
if(ext == "OGA") {
// .oga can be any audio in the Ogg container. So leave it to content-based detection.
File *file = nullptr;
if(ext == "MP3" || ext == "MP2" || ext == "AAC")
file = new MPEG::File(stream, readAudioProperties, audioPropertiesStyle);
#ifdef TAGLIB_WITH_VORBIS
else if(ext == "OGG")
file = new Ogg::Vorbis::File(stream, readAudioProperties, audioPropertiesStyle);
else if(ext == "OGA") {
/* .oga can be any audio in the Ogg container. First try FLAC, then Vorbis. */
File *file = new Ogg::FLAC::File(arg, readAudioProperties, audioPropertiesStyle);
file = new Ogg::FLAC::File(stream, readAudioProperties, audioPropertiesStyle);
if(!file->isValid()) {
delete file;
file = new Ogg::Vorbis::File(stream, readAudioProperties, audioPropertiesStyle);
}
}
else if(ext == "FLAC")
file = new FLAC::File(stream, readAudioProperties, audioPropertiesStyle);
else if(ext == "SPX")
file = new Ogg::Speex::File(stream, readAudioProperties, audioPropertiesStyle);
else if(ext == "OPUS")
file = new Ogg::Opus::File(stream, readAudioProperties, audioPropertiesStyle);
#endif
#ifdef TAGLIB_WITH_APE
else if(ext == "MPC")
file = new MPC::File(stream, readAudioProperties, audioPropertiesStyle);
else if(ext == "WV")
file = new WavPack::File(stream, readAudioProperties, audioPropertiesStyle);
else if(ext == "APE")
file = new APE::File(stream, readAudioProperties, audioPropertiesStyle);
#endif
#ifdef TAGLIB_WITH_TRUEAUDIO
else if(ext == "TTA")
file = new TrueAudio::File(stream, readAudioProperties, audioPropertiesStyle);
#endif
#ifdef TAGLIB_WITH_MP4
else if(ext == "M4A" || ext == "M4R" || ext == "M4B" || ext == "M4P" || ext == "MP4" || ext == "3G2" || ext == "M4V")
file = new MP4::File(stream, readAudioProperties, audioPropertiesStyle);
#endif
#ifdef TAGLIB_WITH_ASF
else if(ext == "WMA" || ext == "ASF")
file = new ASF::File(stream, readAudioProperties, audioPropertiesStyle);
#endif
#ifdef TAGLIB_WITH_RIFF
else if(ext == "AIF" || ext == "AIFF" || ext == "AFC" || ext == "AIFC")
file = new RIFF::AIFF::File(stream, readAudioProperties, audioPropertiesStyle);
else if(ext == "WAV")
file = new RIFF::WAV::File(stream, readAudioProperties, audioPropertiesStyle);
#endif
#ifdef TAGLIB_WITH_MOD
// module, nst and wow are possible but uncommon extensions
else if(ext == "MOD" || ext == "MODULE" || ext == "NST" || ext == "WOW")
file = new Mod::File(stream, readAudioProperties, audioPropertiesStyle);
else if(ext == "S3M")
file = new S3M::File(stream, readAudioProperties, audioPropertiesStyle);
else if(ext == "IT")
file = new IT::File(stream, readAudioProperties, audioPropertiesStyle);
else if(ext == "XM")
file = new XM::File(stream, readAudioProperties, audioPropertiesStyle);
#endif
#ifdef TAGLIB_WITH_DSF
else if(ext == "DSF")
file = new DSF::File(stream, readAudioProperties, audioPropertiesStyle);
else if(ext == "DFF" || ext == "DSDIFF")
file = new DSDIFF::File(stream, readAudioProperties, audioPropertiesStyle);
#endif
#ifdef TAGLIB_WITH_SHORTEN
else if(ext == "SHN")
file = new Shorten::File(stream, readAudioProperties, audioPropertiesStyle);
#endif
#ifdef TAGLIB_WITH_MATROSKA
else if(ext == "MKA" || ext == "MKV" || ext == "WEBM")
file = new Matroska::File(stream, readAudioProperties);
#endif
// if file is not valid, leave it to content-based detection.
if(file) {
if(file->isValid())
return file;
delete file;
return new Ogg::Vorbis::File(arg, readAudioProperties, audioPropertiesStyle);
}
if(ext == "FLAC")
return new FLAC::File(arg, ID3v2::FrameFactory::instance(), readAudioProperties, audioPropertiesStyle);
if(ext == "MPC")
return new MPC::File(arg, readAudioProperties, audioPropertiesStyle);
if(ext == "WV")
return new WavPack::File(arg, readAudioProperties, audioPropertiesStyle);
if(ext == "SPX")
return new Ogg::Speex::File(arg, readAudioProperties, audioPropertiesStyle);
if(ext == "OPUS")
return new Ogg::Opus::File(arg, readAudioProperties, audioPropertiesStyle);
if(ext == "TTA")
return new TrueAudio::File(arg, readAudioProperties, audioPropertiesStyle);
if(ext == "M4A" || ext == "M4R" || ext == "M4B" || ext == "M4P" || ext == "MP4" || ext == "3G2" || ext == "M4V")
return new MP4::File(arg, readAudioProperties, audioPropertiesStyle);
if(ext == "WMA" || ext == "ASF")
return new ASF::File(arg, readAudioProperties, audioPropertiesStyle);
if(ext == "AIF" || ext == "AIFF" || ext == "AFC" || ext == "AIFC")
return new RIFF::AIFF::File(arg, readAudioProperties, audioPropertiesStyle);
if(ext == "WAV")
return new RIFF::WAV::File(arg, readAudioProperties, audioPropertiesStyle);
if(ext == "APE")
return new APE::File(arg, readAudioProperties, audioPropertiesStyle);
// module, nst and wow are possible but uncommon extensions
if(ext == "MOD" || ext == "MODULE" || ext == "NST" || ext == "WOW")
return new Mod::File(arg, readAudioProperties, audioPropertiesStyle);
if(ext == "S3M")
return new S3M::File(arg, readAudioProperties, audioPropertiesStyle);
if(ext == "IT")
return new IT::File(arg, readAudioProperties, audioPropertiesStyle);
if(ext == "XM")
return new XM::File(arg, readAudioProperties, audioPropertiesStyle);
return 0;
return nullptr;
}
}
class FileRef::FileRefPrivate : public RefCounter
// Detect the file type based on the actual content of the stream.
File *detectByContent(IOStream *stream, bool readAudioProperties,
AudioProperties::ReadStyle audioPropertiesStyle)
{
File *file = nullptr;
if(MPEG::File::isSupported(stream))
file = new MPEG::File(stream, readAudioProperties, audioPropertiesStyle);
#ifdef TAGLIB_WITH_VORBIS
else if(Ogg::Vorbis::File::isSupported(stream))
file = new Ogg::Vorbis::File(stream, readAudioProperties, audioPropertiesStyle);
else if(Ogg::FLAC::File::isSupported(stream))
file = new Ogg::FLAC::File(stream, readAudioProperties, audioPropertiesStyle);
else if(FLAC::File::isSupported(stream))
file = new FLAC::File(stream, readAudioProperties, audioPropertiesStyle);
else if(Ogg::Speex::File::isSupported(stream))
file = new Ogg::Speex::File(stream, readAudioProperties, audioPropertiesStyle);
else if(Ogg::Opus::File::isSupported(stream))
file = new Ogg::Opus::File(stream, readAudioProperties, audioPropertiesStyle);
#endif
#ifdef TAGLIB_WITH_APE
else if(MPC::File::isSupported(stream))
file = new MPC::File(stream, readAudioProperties, audioPropertiesStyle);
else if(WavPack::File::isSupported(stream))
file = new WavPack::File(stream, readAudioProperties, audioPropertiesStyle);
else if(APE::File::isSupported(stream))
file = new APE::File(stream, readAudioProperties, audioPropertiesStyle);
#endif
#ifdef TAGLIB_WITH_TRUEAUDIO
else if(TrueAudio::File::isSupported(stream))
file = new TrueAudio::File(stream, readAudioProperties, audioPropertiesStyle);
#endif
#ifdef TAGLIB_WITH_MP4
else if(MP4::File::isSupported(stream))
file = new MP4::File(stream, readAudioProperties, audioPropertiesStyle);
#endif
#ifdef TAGLIB_WITH_ASF
else if(ASF::File::isSupported(stream))
file = new ASF::File(stream, readAudioProperties, audioPropertiesStyle);
#endif
#ifdef TAGLIB_WITH_RIFF
else if(RIFF::AIFF::File::isSupported(stream))
file = new RIFF::AIFF::File(stream, readAudioProperties, audioPropertiesStyle);
else if(RIFF::WAV::File::isSupported(stream))
file = new RIFF::WAV::File(stream, readAudioProperties, audioPropertiesStyle);
#endif
#ifdef TAGLIB_WITH_DSF
else if(DSF::File::isSupported(stream))
file = new DSF::File(stream, readAudioProperties, audioPropertiesStyle);
else if(DSDIFF::File::isSupported(stream))
file = new DSDIFF::File(stream, readAudioProperties, audioPropertiesStyle);
#endif
#ifdef TAGLIB_WITH_SHORTEN
else if(Shorten::File::isSupported(stream))
file = new Shorten::File(stream, readAudioProperties, audioPropertiesStyle);
#endif
#ifdef TAGLIB_WITH_MATROSKA
else if(Matroska::File::isSupported(stream))
file = new Matroska::File(stream, readAudioProperties, audioPropertiesStyle);
#endif
// isSupported() only does a quick check, so double check the file here.
if(file) {
if(file->isValid())
return file;
delete file;
}
return nullptr;
}
} // namespace
class FileRef::FileRefPrivate
{
public:
FileRefPrivate(File *f) :
RefCounter(),
file(f) {}
~FileRefPrivate() {
FileRefPrivate() = default;
~FileRefPrivate()
{
delete file;
delete stream;
}
File *file;
FileRefPrivate(const FileRefPrivate &) = delete;
FileRefPrivate &operator=(const FileRefPrivate &) = delete;
bool isNull() const
{
return !file || !file->isValid();
}
bool isNullWithDebugMessage([[maybe_unused]] const String &methodName) const
{
if(isNull()) {
debug("FileRef::" + methodName + "() - Called without a valid file.");
return true;
}
return false;
}
File *file { nullptr };
IOStream *stream { nullptr };
};
////////////////////////////////////////////////////////////////////////////////
@@ -202,52 +350,93 @@ public:
////////////////////////////////////////////////////////////////////////////////
FileRef::FileRef() :
d(new FileRefPrivate(0))
d(std::make_shared<FileRefPrivate>())
{
}
FileRef::FileRef(FileName fileName, bool readAudioProperties,
AudioProperties::ReadStyle audioPropertiesStyle) :
d(new FileRefPrivate(createInternal(fileName, readAudioProperties, audioPropertiesStyle)))
d(std::make_shared<FileRefPrivate>())
{
parse(fileName, readAudioProperties, audioPropertiesStyle);
}
FileRef::FileRef(IOStream* stream, bool readAudioProperties, AudioProperties::ReadStyle audioPropertiesStyle) :
d(new FileRefPrivate(createInternal(stream, readAudioProperties, audioPropertiesStyle)))
FileRef::FileRef(IOStream *stream, bool readAudioProperties, AudioProperties::ReadStyle audioPropertiesStyle) :
d(std::make_shared<FileRefPrivate>())
{
parse(stream, readAudioProperties, audioPropertiesStyle);
}
FileRef::FileRef(File *file) :
d(new FileRefPrivate(file))
d(std::make_shared<FileRefPrivate>())
{
d->file = file;
}
FileRef::FileRef(const FileRef &ref) :
d(ref.d)
{
d->ref();
}
FileRef::FileRef(const FileRef &) = default;
FileRef::~FileRef()
{
if(d->deref())
delete d;
}
FileRef::~FileRef() = default;
Tag *FileRef::tag() const
{
if(isNull()) {
debug("FileRef::tag() - Called without a valid file.");
return 0;
if(d->isNullWithDebugMessage(__func__)) {
return nullptr;
}
return d->file->tag();
}
PropertyMap FileRef::properties() const
{
if(d->isNullWithDebugMessage(__func__)) {
return PropertyMap();
}
return d->file->properties();
}
void FileRef::removeUnsupportedProperties(const StringList& properties)
{
if(d->isNullWithDebugMessage(__func__)) {
return;
}
return d->file->removeUnsupportedProperties(properties);
}
PropertyMap FileRef::setProperties(const PropertyMap &properties)
{
if(d->isNullWithDebugMessage(__func__)) {
return PropertyMap();
}
return d->file->setProperties(properties);
}
StringList FileRef::complexPropertyKeys() const
{
if(d->isNullWithDebugMessage(__func__)) {
return StringList();
}
return d->file->complexPropertyKeys();
}
List<VariantMap> FileRef::complexProperties(const String &key) const
{
if(d->isNullWithDebugMessage(__func__)) {
return List<VariantMap>();
}
return d->file->complexProperties(key);
}
bool FileRef::setComplexProperties(const String &key, const List<VariantMap> &value)
{
if(d->isNullWithDebugMessage(__func__)) {
return false;
}
return d->file->setComplexProperties(key, value);
}
AudioProperties *FileRef::audioProperties() const
{
if(isNull()) {
debug("FileRef::audioProperties() - Called without a valid file.");
return 0;
if(d->isNullWithDebugMessage(__func__)) {
return nullptr;
}
return d->file->audioProperties();
}
@@ -259,8 +448,7 @@ File *FileRef::file() const
bool FileRef::save()
{
if(isNull()) {
debug("FileRef::save() - Called without a valid file.");
if(d->isNullWithDebugMessage(__func__)) {
return false;
}
return d->file->save();
@@ -272,18 +460,34 @@ const FileRef::FileTypeResolver *FileRef::addFileTypeResolver(const FileRef::Fil
return resolver;
}
void FileRef::clearFileTypeResolvers() // static
{
fileTypeResolvers.clear();
}
StringList FileRef::defaultFileExtensions()
{
StringList l;
l.append("mp3");
l.append("mp2");
l.append("aac");
#ifdef TAGLIB_WITH_VORBIS
l.append("ogg");
l.append("flac");
l.append("oga");
l.append("mp3");
l.append("opus");
l.append("spx");
#endif
#ifdef TAGLIB_WITH_APE
l.append("mpc");
l.append("wv");
l.append("spx");
l.append("ape");
#endif
#ifdef TAGLIB_WITH_TRUEAUDIO
l.append("tta");
#endif
#ifdef TAGLIB_WITH_MP4
l.append("m4a");
l.append("m4r");
l.append("m4b");
@@ -291,12 +495,19 @@ StringList FileRef::defaultFileExtensions()
l.append("3g2");
l.append("mp4");
l.append("m4v");
#endif
#ifdef TAGLIB_WITH_ASF
l.append("wma");
l.append("asf");
#endif
#ifdef TAGLIB_WITH_RIFF
l.append("aif");
l.append("aiff");
l.append("afc");
l.append("aifc");
l.append("wav");
l.append("ape");
#endif
#ifdef TAGLIB_WITH_MOD
l.append("mod");
l.append("module"); // alias for "mod"
l.append("nst"); // alias for "mod"
@@ -304,22 +515,32 @@ StringList FileRef::defaultFileExtensions()
l.append("s3m");
l.append("it");
l.append("xm");
#endif
#ifdef TAGLIB_WITH_DSF
l.append("dsf");
l.append("dff");
l.append("dsdiff"); // alias for "dff"
#endif
#ifdef TAGLIB_WITH_SHORTEN
l.append("shn");
#endif
#ifdef TAGLIB_WITH_MATROSKA
l.append("mkv");
l.append("mka");
l.append("webm");
#endif
return l;
}
bool FileRef::isNull() const
{
return (!d->file || !d->file->isValid());
return d->isNull();
}
FileRef &FileRef::operator=(const FileRef &ref)
{
FileRef(ref).swap(*this);
return *this;
}
FileRef &FileRef::operator=(const FileRef &) = default;
void FileRef::swap(FileRef &ref)
void FileRef::swap(FileRef &ref) noexcept
{
using std::swap;
@@ -328,16 +549,74 @@ void FileRef::swap(FileRef &ref)
bool FileRef::operator==(const FileRef &ref) const
{
return (ref.d->file == d->file);
return ref.d->file == d->file;
}
bool FileRef::operator!=(const FileRef &ref) const
{
return (ref.d->file != d->file);
return ref.d->file != d->file;
}
File *FileRef::create(FileName fileName, bool readAudioProperties,
AudioProperties::ReadStyle audioPropertiesStyle) // static
////////////////////////////////////////////////////////////////////////////////
// private members
////////////////////////////////////////////////////////////////////////////////
void FileRef::parse(FileName fileName, bool readAudioProperties,
AudioProperties::ReadStyle audioPropertiesStyle)
{
return createInternal(fileName, readAudioProperties, audioPropertiesStyle);
// Try user-defined resolvers.
d->file = detectByResolvers(fileName, readAudioProperties, audioPropertiesStyle);
if(d->file)
return;
// Try to resolve file types based on the file extension.
d->stream = new FileStream(fileName);
d->file = detectByExtension(d->stream, readAudioProperties, audioPropertiesStyle);
if(d->file)
return;
// At last, try to resolve file types based on the actual content.
d->file = detectByContent(d->stream, readAudioProperties, audioPropertiesStyle);
if(d->file)
return;
// Stream have to be closed here if failed to resolve file types.
delete d->stream;
d->stream = nullptr;
}
void FileRef::parse(IOStream *stream, bool readAudioProperties,
AudioProperties::ReadStyle audioPropertiesStyle)
{
// Try user-defined stream resolvers.
d->file = detectByResolvers(stream, readAudioProperties, audioPropertiesStyle);
if(d->file)
return;
// Try user-defined resolvers.
d->file = detectByResolvers(stream->name(), readAudioProperties, audioPropertiesStyle);
if(d->file)
return;
// Try to resolve file types based on the file extension.
d->file = detectByExtension(stream, readAudioProperties, audioPropertiesStyle);
if(d->file)
return;
// At last, try to resolve file types based on the actual content of the file.
d->file = detectByContent(stream, readAudioProperties, audioPropertiesStyle);
}
FileRef::FileTypeResolver::FileTypeResolver() = default;
FileRef::FileTypeResolver::~FileTypeResolver() = default;
FileRef::StreamTypeResolver::StreamTypeResolver() = default;
FileRef::StreamTypeResolver::~StreamTypeResolver() = default;

View File

@@ -60,43 +60,29 @@ namespace TagLib {
{
public:
//! A class for pluggable file type resolution.
//! A class for pluggable file type resolution.
/*!
* This class is used to add extend TagLib's very basic file name based file
* type resolution.
*
* This can be accomplished with:
*
* \code
*
* class MyFileTypeResolver : FileTypeResolver
* {
* TagLib::File *createFile(TagLib::FileName *fileName, bool, AudioProperties::ReadStyle) const
* {
* if(someCheckForAnMP3File(fileName))
* return new TagLib::MPEG::File(fileName);
* return 0;
* }
* }
*
* FileRef::addFileTypeResolver(new MyFileTypeResolver);
*
* \endcode
*
* Naturally a less contrived example would be slightly more complex. This
* can be used to plug in mime-type detection systems or to add new file types
* to TagLib.
*/
/*!
* %File type resolver, better implement StreamTypeResolver in order to
* support both file and stream resolution.
*/
class TAGLIB_EXPORT FileTypeResolver
{
TAGLIB_IGNORE_MISSING_DESTRUCTOR
public:
FileTypeResolver();
/*!
* Destroys this FileTypeResolver instance.
*/
virtual ~FileTypeResolver() = 0;
FileTypeResolver(const FileTypeResolver &) = delete;
FileTypeResolver &operator=(const FileTypeResolver &) = delete;
/*!
* This method must be overridden to provide an additional file type
* resolver. If the resolver is able to determine the file type it should
* return a valid File object; if not it should return 0.
* return a valid File object; if not it should return nullptr.
*
* \note The created file is then owned by the FileRef and should not be
* deleted. Deletion will happen automatically when the FileRef passes
@@ -106,6 +92,81 @@ namespace TagLib {
bool readAudioProperties = true,
AudioProperties::ReadStyle
audioPropertiesStyle = AudioProperties::Average) const = 0;
private:
class FileTypeResolverPrivate;
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
std::unique_ptr<FileTypeResolverPrivate> d;
};
//! A class for pluggable stream type resolution.
/*!
* This class is used to extend TagLib's very basic file name based file
* type resolution.
*
* This can be accomplished with:
*
* \code
*
* class MyStreamTypeResolver : StreamTypeResolver
* {
* TagLib::File *createFile(TagLib::FileName *fileName, bool readProps,
* AudioProperties::ReadStyle readStyle) const override
* {
* if(someCheckForAnMP3File(fileName))
* return new TagLib::MPEG::File(fileName, readProps, readStyle);
* return nullptr;
* }
*
* TagLib::File *createFileFromStream(TagLib::IOStream *s, bool readProps,
* AudioProperties::ReadStyle readStyle) const override
* {
* if(someCheckForAnMP3Stream(s))
* return new TagLib::MPEG::File(s, readProps, readStyle);
* return nullptr;
* }
* }
*
* FileRef::addFileTypeResolver(new MyStreamTypeResolver);
*
* \endcode
*
* Naturally a less contrived example would be slightly more complex. This
* can be used to plug in mime-type detection systems or to add new file types
* to TagLib.
*/
class TAGLIB_EXPORT StreamTypeResolver : public FileTypeResolver
{
public:
StreamTypeResolver();
/*!
* Destroys this StreamTypeResolver instance.
*/
virtual ~StreamTypeResolver() override = 0; // virtual is needed by SWIG
StreamTypeResolver(const StreamTypeResolver &) = delete;
StreamTypeResolver &operator=(const StreamTypeResolver &) = delete;
/*!
* This method must be overridden to provide an additional stream type
* resolver. If the resolver is able to determine the file type it should
* return a valid File object; if not it should return nullptr.
*
* \note The created file is then owned by the FileRef and should not be
* deleted. Deletion will happen automatically when the FileRef passes
* out of scope.
*
* \see createFile()
*/
virtual File *createFileFromStream(IOStream *stream,
bool readAudioProperties = true,
AudioProperties::ReadStyle
audioPropertiesStyle = AudioProperties::Average) const = 0;
private:
class StreamTypeResolverPrivate;
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
std::unique_ptr<StreamTypeResolverPrivate> d;
};
/*!
@@ -114,9 +175,9 @@ namespace TagLib {
FileRef();
/*!
* Create a FileRef from \a fileName. If \a readAudioProperties is true then
* Create a FileRef from \a fileName. If \a readAudioProperties is \c true then
* the audio properties will be read using \a audioPropertiesStyle. If
* \a readAudioProperties is false then \a audioPropertiesStyle will be
* \a readAudioProperties is \c false then \a audioPropertiesStyle will be
* ignored.
*
* Also see the note in the class documentation about why you may not want to
@@ -129,8 +190,8 @@ namespace TagLib {
/*!
* Construct a FileRef from an opened \a IOStream. If \a readAudioProperties
* is true then the audio properties will be read using \a audioPropertiesStyle.
* If \a readAudioProperties is false then \a audioPropertiesStyle will be
* is \c true then the audio properties will be read using \a audioPropertiesStyle.
* If \a readAudioProperties is \c false then \a audioPropertiesStyle will be
* ignored.
*
* Also see the note in the class documentation about why you may not want to
@@ -158,24 +219,102 @@ namespace TagLib {
/*!
* Destroys this FileRef instance.
*/
virtual ~FileRef();
~FileRef();
/*!
* Returns a pointer to represented file's tag.
* Returns a pointer to the represented file's tag.
*
* \warning This pointer will become invalid when this FileRef and all
* copies pass out of scope.
*
* \warning Do not cast it to any subclasses of \class Tag.
* Use tag returning methods of appropriate subclasses of \class File instead.
* \warning Do not cast it to any subclasses of Tag.
* Use tag returning methods of appropriate subclasses of File instead.
*
* \see File::tag()
*/
Tag *tag() const;
/*!
* Exports the tags of the file as dictionary mapping (human readable) tag
* names (uppercase Strings) to StringLists of tag values. Calls this
* method on the wrapped File instance.
* For each metadata object of the file that could not be parsed into the PropertyMap
* format, the returned map's unsupportedData() list will contain one entry identifying
* that object (e.g. the frame type for ID3v2 tags). Use removeUnsupportedProperties()
* to remove (a subset of) them.
* For files that contain more than one tag (e.g. an MP3 with both an ID3v1 and an ID3v2
* tag) only the most "modern" one will be exported (ID3v2 in this case).
*/
PropertyMap properties() const;
/*!
* Removes unsupported properties, or a subset of them, from the file's metadata.
* The parameter \a properties must contain only entries from
* properties().unsupportedData().
*/
void removeUnsupportedProperties(const StringList& properties);
/*!
* Sets the tags of the wrapped File to those specified in \a properties.
* If some value(s) could not be written to the specific metadata format,
* the returned PropertyMap will contain those value(s). Otherwise it will be empty,
* indicating that no problems occurred.
* With file types that support several tag formats (for instance, MP3 files can have
* ID3v1, ID3v2, and APEv2 tags), this function will create the most appropriate one
* (ID3v2 for MP3 files). Older formats will be updated as well, if they exist, but won't
* be taken into account for the return value of this function.
* See the documentation of the subclass implementations for detailed descriptions.
*/
PropertyMap setProperties(const PropertyMap &properties);
/*!
* Get the keys of complex properties, i.e. properties which cannot be
* represented simply by a string.
* Because such properties might be expensive to fetch, there are separate
* operations to get the available keys - which is expected to be cheap -
* and getting and setting the property values.
* Calls the method on the wrapped File, which collects the keys from one
* or more of its tags.
*/
StringList complexPropertyKeys() const;
/*!
* Get the complex properties for a given \a key.
* In order to be flexible for different metadata formats, the properties
* are represented as variant maps. Despite this dynamic nature, some
* degree of standardization should be achieved between formats:
*
* - PICTURE
* - data: ByteVector with picture data
* - description: String with description
* - pictureType: String with type as specified for ID3v2,
* e.g. "Front Cover", "Back Cover", "Band"
* - mimeType: String with image format, e.g. "image/jpeg"
* - optionally more information found in the tag, such as
* "width", "height", "numColors", "colorDepth" int values
* in FLAC pictures
* - GENERALOBJECT
* - data: ByteVector with object data
* - description: String with description
* - fileName: String with file name
* - mimeType: String with MIME type
* - this is currently only implemented for ID3v2 GEOB frames
*
* Calls the method on the wrapped File, which gets the properties from one
* or more of its tags.
*/
List<VariantMap> complexProperties(const String &key) const;
/*!
* Set all complex properties for a given \a key using variant maps as
* \a value with the same format as returned by complexProperties().
* An empty list as \a value removes all complex properties for \a key.
*/
bool setComplexProperties(const String &key, const List<VariantMap> &value);
/*!
* Returns the audio properties for this FileRef. If no audio properties
* were read then this will returns a null pointer.
* were read then this will return a null pointer.
*/
AudioProperties *audioProperties() const;
@@ -197,7 +336,7 @@ namespace TagLib {
File *file() const;
/*!
* Saves the file. Returns true on success.
* Saves the file. Returns \c true on success.
*/
bool save();
@@ -215,6 +354,11 @@ namespace TagLib {
*/
static const FileTypeResolver *addFileTypeResolver(const FileTypeResolver *resolver);
/*!
* Remove all resolvers added by addFileTypeResolver().
*/
static void clearFileTypeResolvers();
/*!
* As is mentioned elsewhere in this class's documentation, the default file
* type resolution code provided by TagLib only works by comparing file
@@ -234,7 +378,7 @@ namespace TagLib {
static StringList defaultFileExtensions();
/*!
* Returns true if the file (and as such other pointers) are null.
* Returns \c true if the file (and as such other pointers) are null.
*/
bool isNull() const;
@@ -244,40 +388,28 @@ namespace TagLib {
FileRef &operator=(const FileRef &ref);
/*!
* Exchanges the content of the FileRef by the content of \a ref.
* Exchanges the content of the FileRef with the content of \a ref.
*/
void swap(FileRef &ref);
void swap(FileRef &ref) noexcept;
/*!
* Returns true if this FileRef and \a ref point to the same File object.
* Returns \c true if this FileRef and \a ref point to the same File object.
*/
bool operator==(const FileRef &ref) const;
/*!
* Returns true if this FileRef and \a ref do not point to the same File
* Returns \c true if this FileRef and \a ref do not point to the same File
* object.
*/
bool operator!=(const FileRef &ref) const;
/*!
* A simple implementation of file type guessing. If \a readAudioProperties
* is true then the audio properties will be read using
* \a audioPropertiesStyle. If \a readAudioProperties is false then
* \a audioPropertiesStyle will be ignored.
*
* \note You generally shouldn't use this method, but instead the constructor
* directly.
*
* \deprecated
*/
static File *create(FileName fileName,
bool readAudioProperties = true,
AudioProperties::ReadStyle audioPropertiesStyle = AudioProperties::Average);
private:
void parse(FileName fileName, bool readAudioProperties, AudioProperties::ReadStyle audioPropertiesStyle);
void parse(IOStream *stream, bool readAudioProperties, AudioProperties::ReadStyle audioPropertiesStyle);
class FileRefPrivate;
FileRefPrivate *d;
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
std::shared_ptr<FileRefPrivate> d;
};
} // namespace TagLib

View File

@@ -23,21 +23,19 @@
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#include <tbytevector.h>
#include <tstring.h>
#include <tlist.h>
#include <tdebug.h>
#include <tagunion.h>
#include <tpropertymap.h>
#include <tagutils.h>
#include <id3v2header.h>
#include <id3v2tag.h>
#include <id3v1tag.h>
#include <xiphcomment.h>
#include "flacpicture.h"
#include "flacfile.h"
#include <algorithm>
#include <utility>
#include "tdebug.h"
#include "tpropertymap.h"
#include "tagunion.h"
#include "tagutils.h"
#include "id3v2tag.h"
#include "id3v1tag.h"
#include "xiphcomment.h"
#include "flacpicture.h"
#include "flacmetadatablock.h"
#include "flacunknownmetadatablock.h"
@@ -45,63 +43,62 @@ using namespace TagLib;
namespace
{
typedef List<FLAC::MetadataBlock *> BlockList;
typedef BlockList::Iterator BlockIterator;
typedef BlockList::Iterator BlockConstIterator;
enum { FlacXiphIndex = 0, FlacID3v2Index = 1, FlacID3v1Index = 2 };
const long MinPaddingLength = 4096;
const long MaxPaddingLegnth = 1024 * 1024;
constexpr long MinPaddingLength = 4096;
constexpr long MaxPaddingLegnth = 1024 * 1024;
const char LastBlockFlag = '\x80';
}
constexpr char LastBlockFlag = '\x80';
} // namespace
class FLAC::File::FilePrivate
{
public:
FilePrivate(const ID3v2::FrameFactory *frameFactory = ID3v2::FrameFactory::instance()) :
ID3v2FrameFactory(frameFactory),
ID3v2Location(-1),
ID3v2OriginalSize(0),
ID3v1Location(-1),
properties(0),
flacStart(0),
streamStart(0),
scanned(false)
ID3v2FrameFactory(frameFactory)
{
blocks.setAutoDelete(true);
}
~FilePrivate()
{
delete properties;
}
const ID3v2::FrameFactory *ID3v2FrameFactory;
long ID3v2Location;
long ID3v2OriginalSize;
offset_t ID3v2Location { -1 };
long ID3v2OriginalSize { 0 };
long ID3v1Location;
offset_t ID3v1Location { -1 };
TagUnion tag;
Properties *properties;
std::unique_ptr<Properties> properties;
ByteVector xiphCommentData;
BlockList blocks;
List<FLAC::MetadataBlock *> blocks;
long flacStart;
long streamStart;
bool scanned;
offset_t flacStart { 0 };
offset_t streamStart { 0 };
bool scanned { false };
};
////////////////////////////////////////////////////////////////////////////////
// static members
////////////////////////////////////////////////////////////////////////////////
bool FLAC::File::isSupported(IOStream *stream)
{
// A FLAC file has an ID "fLaC" somewhere. An ID3v2 tag may precede.
const ByteVector buffer = Utils::readHeader(stream, bufferSize(), true);
return buffer.find("fLaC") >= 0;
}
////////////////////////////////////////////////////////////////////////////////
// public members
////////////////////////////////////////////////////////////////////////////////
FLAC::File::File(FileName file, bool readProperties, Properties::ReadStyle) :
FLAC::File::File(FileName file, bool readProperties,
Properties::ReadStyle,
ID3v2::FrameFactory *frameFactory) :
TagLib::File(file),
d(new FilePrivate())
d(std::make_unique<FilePrivate>(
frameFactory ? frameFactory : ID3v2::FrameFactory::instance()))
{
if(isOpen())
read(readProperties);
@@ -110,7 +107,18 @@ FLAC::File::File(FileName file, bool readProperties, Properties::ReadStyle) :
FLAC::File::File(FileName file, ID3v2::FrameFactory *frameFactory,
bool readProperties, Properties::ReadStyle) :
TagLib::File(file),
d(new FilePrivate(frameFactory))
d(std::make_unique<FilePrivate>(frameFactory))
{
if(isOpen())
read(readProperties);
}
FLAC::File::File(IOStream *stream, bool readProperties,
Properties::ReadStyle,
ID3v2::FrameFactory *frameFactory) :
TagLib::File(stream),
d(std::make_unique<FilePrivate>(
frameFactory ? frameFactory : ID3v2::FrameFactory::instance()))
{
if(isOpen())
read(readProperties);
@@ -119,16 +127,13 @@ FLAC::File::File(FileName file, ID3v2::FrameFactory *frameFactory,
FLAC::File::File(IOStream *stream, ID3v2::FrameFactory *frameFactory,
bool readProperties, Properties::ReadStyle) :
TagLib::File(stream),
d(new FilePrivate(frameFactory))
d(std::make_unique<FilePrivate>(frameFactory))
{
if(isOpen())
read(readProperties);
}
FLAC::File::~File()
{
delete d;
}
FLAC::File::~File() = default;
TagLib::Tag *FLAC::File::tag() const
{
@@ -150,9 +155,72 @@ PropertyMap FLAC::File::setProperties(const PropertyMap &properties)
return xiphComment(true)->setProperties(properties);
}
StringList FLAC::File::complexPropertyKeys() const
{
StringList keys = TagLib::File::complexPropertyKeys();
if(!keys.contains("PICTURE")) {
if(std::any_of(d->blocks.cbegin(), d->blocks.cend(),
[](MetadataBlock *block) {
return dynamic_cast<Picture *>(block) != nullptr;
})) {
keys.append("PICTURE");
}
}
return keys;
}
List<VariantMap> FLAC::File::complexProperties(const String &key) const
{
if(const String uppercaseKey = key.upper(); uppercaseKey == "PICTURE") {
List<VariantMap> props;
for(const auto &block : std::as_const(d->blocks)) {
if(auto picture = dynamic_cast<Picture *>(block)) {
VariantMap property;
property.insert("data", picture->data());
property.insert("mimeType", picture->mimeType());
property.insert("description", picture->description());
property.insert("pictureType",
FLAC::Picture::typeToString(picture->type()));
property.insert("width", picture->width());
property.insert("height", picture->height());
property.insert("numColors", picture->numColors());
property.insert("colorDepth", picture->colorDepth());
props.append(property);
}
}
return props;
}
return TagLib::File::complexProperties(key);
}
bool FLAC::File::setComplexProperties(const String &key, const List<VariantMap> &value)
{
if(const String uppercaseKey = key.upper(); uppercaseKey == "PICTURE") {
removePictures();
for(const auto &property : value) {
auto picture = new FLAC::Picture;
picture->setData(property.value("data").value<ByteVector>());
picture->setMimeType(property.value("mimeType").value<String>());
picture->setDescription(property.value("description").value<String>());
picture->setType(FLAC::Picture::typeFromString(
property.value("pictureType").value<String>()));
picture->setWidth(property.value("width").value<int>());
picture->setHeight(property.value("height").value<int>());
picture->setNumColors(property.value("numColors").value<int>());
picture->setColorDepth(property.value("colorDepth").value<int>());
addPicture(picture);
}
}
else {
return TagLib::File::setComplexProperties(key, value);
}
return true;
}
FLAC::Properties *FLAC::File::audioProperties() const
{
return d->properties;
return d->properties.get();
}
bool FLAC::File::save()
@@ -168,40 +236,54 @@ bool FLAC::File::save()
}
// Create new vorbis comments
Tag::duplicate(&d->tag, xiphComment(true), false);
if(!hasXiphComment())
Tag::duplicate(&d->tag, xiphComment(true), false);
d->xiphCommentData = xiphComment()->render(false);
// Replace metadata blocks
for(BlockIterator it = d->blocks.begin(); it != d->blocks.end(); ++it) {
MetadataBlock *commentBlock =
new UnknownMetadataBlock(MetadataBlock::VorbisComment, d->xiphCommentData);
for(auto it = d->blocks.begin(); it != d->blocks.end();) {
if((*it)->code() == MetadataBlock::VorbisComment) {
// Set the new Vorbis Comment block
// Remove the old Vorbis Comment block
delete *it;
d->blocks.erase(it);
break;
it = d->blocks.erase(it);
continue;
}
if(commentBlock && (*it)->code() == MetadataBlock::Picture) {
// Set the new Vorbis Comment block before the first picture block
d->blocks.insert(it, commentBlock);
commentBlock = nullptr;
}
++it;
}
d->blocks.append(new UnknownMetadataBlock(MetadataBlock::VorbisComment, d->xiphCommentData));
if(commentBlock)
d->blocks.append(commentBlock);
// Render data for the metadata blocks
ByteVector data;
for(BlockConstIterator it = d->blocks.begin(); it != d->blocks.end(); ++it) {
for(auto it = d->blocks.begin(); it != d->blocks.end();) {
ByteVector blockData = (*it)->render();
ByteVector blockHeader = ByteVector::fromUInt(blockData.size());
blockHeader[0] = (*it)->code();
if(blockHeader[0] != 0) {
debug("FLAC::File::save() -- Removing too large block.");
delete *it;
it = d->blocks.erase(it);
continue;
}
blockHeader[0] = static_cast<char>((*it)->code());
data.append(blockHeader);
data.append(blockData);
++it;
}
// Compute the amount of padding, and append that to data.
// TODO: Should be calculated in offset_t in taglib2.
long originalLength = d->streamStart - d->flacStart;
long paddingLength = originalLength - data.size() - 4;
offset_t originalLength = d->streamStart - d->flacStart;
offset_t paddingLength = originalLength - data.size() - 4;
if(paddingLength <= 0) {
paddingLength = MinPaddingLength;
@@ -209,15 +291,15 @@ bool FLAC::File::save()
else {
// Padding won't increase beyond 1% of the file size or 1MB.
long threshold = length() / 100;
threshold = std::max(threshold, MinPaddingLength);
threshold = std::min(threshold, MaxPaddingLegnth);
offset_t threshold = length() / 100;
threshold = std::max<offset_t>(threshold, MinPaddingLength);
threshold = std::min<offset_t>(threshold, MaxPaddingLegnth);
if(paddingLength > threshold)
paddingLength = MinPaddingLength;
}
ByteVector paddingHeader = ByteVector::fromUInt(paddingLength);
ByteVector paddingHeader = ByteVector::fromUInt(static_cast<unsigned int>(paddingLength));
paddingHeader[0] = static_cast<char>(MetadataBlock::Padding | LastBlockFlag);
data.append(paddingHeader);
data.resize(static_cast<unsigned int>(data.size() + paddingLength));
@@ -226,10 +308,10 @@ bool FLAC::File::save()
insert(data, d->flacStart, originalLength);
d->streamStart += (static_cast<long>(data.size()) - originalLength);
d->streamStart += static_cast<long>(data.size()) - originalLength;
if(d->ID3v1Location >= 0)
d->ID3v1Location += (static_cast<long>(data.size()) - originalLength);
d->ID3v1Location += static_cast<long>(data.size()) - originalLength;
// Update ID3 tags
@@ -243,11 +325,11 @@ bool FLAC::File::save()
data = ID3v2Tag()->render();
insert(data, d->ID3v2Location, d->ID3v2OriginalSize);
d->flacStart += (static_cast<long>(data.size()) - d->ID3v2OriginalSize);
d->streamStart += (static_cast<long>(data.size()) - d->ID3v2OriginalSize);
d->flacStart += static_cast<long>(data.size()) - d->ID3v2OriginalSize;
d->streamStart += static_cast<long>(data.size()) - d->ID3v2OriginalSize;
if(d->ID3v1Location >= 0)
d->ID3v1Location += (static_cast<long>(data.size()) - d->ID3v2OriginalSize);
d->ID3v1Location += static_cast<long>(data.size()) - d->ID3v2OriginalSize;
d->ID3v2OriginalSize = data.size();
}
@@ -298,11 +380,8 @@ bool FLAC::File::save()
ID3v2::Tag *FLAC::File::ID3v2Tag(bool create)
{
if(!create || d->tag[FlacID3v2Index])
return static_cast<ID3v2::Tag *>(d->tag[FlacID3v2Index]);
d->tag.set(FlacID3v2Index, new ID3v2::Tag);
return static_cast<ID3v2::Tag *>(d->tag[FlacID3v2Index]);
return d->tag.access<ID3v2::Tag>(FlacID3v2Index, create,
d->ID3v2FrameFactory);
}
ID3v1::Tag *FLAC::File::ID3v1Tag(bool create)
@@ -315,29 +394,11 @@ Ogg::XiphComment *FLAC::File::xiphComment(bool create)
return d->tag.access<Ogg::XiphComment>(FlacXiphIndex, create);
}
void FLAC::File::setID3v2FrameFactory(const ID3v2::FrameFactory *factory)
{
d->ID3v2FrameFactory = factory;
}
ByteVector FLAC::File::streamInfoData()
{
debug("FLAC::File::streamInfoData() -- This function is obsolete. Returning an empty ByteVector.");
return ByteVector();
}
long FLAC::File::streamLength()
{
debug("FLAC::File::streamLength() -- This function is obsolete. Returning zero.");
return 0;
}
List<FLAC::Picture *> FLAC::File::pictureList()
{
List<Picture *> pictures;
for(BlockConstIterator it = d->blocks.begin(); it != d->blocks.end(); ++it) {
Picture *picture = dynamic_cast<Picture *>(*it);
if(picture) {
for(const auto &block : std::as_const(d->blocks)) {
if(auto picture = dynamic_cast<Picture *>(block)) {
pictures.append(picture);
}
}
@@ -351,7 +412,7 @@ void FLAC::File::addPicture(Picture *picture)
void FLAC::File::removePicture(Picture *picture, bool del)
{
BlockIterator it = d->blocks.find(picture);
auto it = d->blocks.find(picture);
if(it != d->blocks.end())
d->blocks.erase(it);
@@ -361,7 +422,7 @@ void FLAC::File::removePicture(Picture *picture, bool del)
void FLAC::File::removePictures()
{
for(BlockIterator it = d->blocks.begin(); it != d->blocks.end(); ) {
for(auto it = d->blocks.begin(); it != d->blocks.end(); ) {
if(dynamic_cast<Picture *>(*it)) {
delete *it;
it = d->blocks.erase(it);
@@ -375,10 +436,10 @@ void FLAC::File::removePictures()
void FLAC::File::strip(int tags)
{
if(tags & ID3v1)
d->tag.set(FlacID3v1Index, 0);
d->tag.set(FlacID3v1Index, nullptr);
if(tags & ID3v2)
d->tag.set(FlacID3v2Index, 0);
d->tag.set(FlacID3v2Index, nullptr);
if(tags & XiphComment) {
xiphComment()->removeAllFields();
@@ -393,12 +454,12 @@ bool FLAC::File::hasXiphComment() const
bool FLAC::File::hasID3v1Tag() const
{
return (d->ID3v1Location >= 0);
return d->ID3v1Location >= 0;
}
bool FLAC::File::hasID3v2Tag() const
{
return (d->ID3v2Location >= 0);
return d->ID3v2Location >= 0;
}
////////////////////////////////////////////////////////////////////////////////
@@ -441,14 +502,14 @@ void FLAC::File::read(bool readProperties)
const ByteVector infoData = d->blocks.front()->render();
long streamLength;
offset_t streamLength;
if(d->ID3v1Location >= 0)
streamLength = d->ID3v1Location - d->streamStart;
else
streamLength = length() - d->streamStart;
d->properties = new Properties(infoData, streamLength);
d->properties = std::make_unique<Properties>(infoData, streamLength);
}
}
@@ -462,7 +523,7 @@ void FLAC::File::scan()
if(!isValid())
return;
long nextBlockOffset;
offset_t nextBlockOffset;
if(d->ID3v2Location >= 0)
nextBlockOffset = find("fLaC", d->ID3v2Location + d->ID3v2OriginalSize);
@@ -482,6 +543,11 @@ void FLAC::File::scan()
seek(nextBlockOffset);
const ByteVector header = readBlock(4);
if(header.size() != 4) {
debug("FLAC::File::scan() -- Failed to read a block header");
setValid(false);
return;
}
// Header format (from spec):
// <1> Last-metadata-block flag
@@ -507,7 +573,9 @@ void FLAC::File::scan()
return;
}
if(blockLength == 0 && blockType != MetadataBlock::Padding) {
if(blockLength == 0
&& blockType != MetadataBlock::Padding && blockType != MetadataBlock::SeekTable)
{
debug("FLAC::File::scan() -- Zero-sized metadata block found");
setValid(false);
return;
@@ -520,7 +588,7 @@ void FLAC::File::scan()
return;
}
MetadataBlock *block = 0;
MetadataBlock *block = nullptr;
// Found the vorbis-comment
if(blockType == MetadataBlock::VorbisComment) {
@@ -533,7 +601,7 @@ void FLAC::File::scan()
}
}
else if(blockType == MetadataBlock::Picture) {
FLAC::Picture *picture = new FLAC::Picture();
auto picture = new FLAC::Picture();
if(picture->parse(data)) {
block = picture;
}

View File

@@ -26,11 +26,10 @@
#ifndef TAGLIB_FLACFILE_H
#define TAGLIB_FLACFILE_H
#include "taglib_export.h"
#include "tfile.h"
#include "tlist.h"
#include "taglib_export.h"
#include "tag.h"
#include "flacpicture.h"
#include "flacproperties.h"
@@ -44,7 +43,7 @@ namespace TagLib {
//! An implementation of FLAC metadata
/*!
* This is implementation of FLAC metadata for non-Ogg FLAC files. At some
* This is an implementation of FLAC metadata for non-Ogg FLAC files. At some
* point when Ogg / FLAC is more common there will be a similar implementation
* under the Ogg hierarchy.
*
@@ -84,44 +83,65 @@ namespace TagLib {
};
/*!
* Constructs a FLAC file from \a file. If \a readProperties is true the
* Constructs a FLAC file from \a file. If \a readProperties is \c true the
* file's audio properties will also be read.
*
* \note In the current implementation, \a propertiesStyle is ignored.
*
* \deprecated This constructor will be dropped in favor of the one below
* in a future version.
* If this file contains an ID3v2 tag, the frames will be created using
* \a frameFactory (default if null).
*/
File(FileName file, bool readProperties = true,
Properties::ReadStyle propertiesStyle = Properties::Average);
Properties::ReadStyle propertiesStyle = Properties::Average,
ID3v2::FrameFactory *frameFactory = nullptr);
/*!
* Constructs an FLAC file from \a file. If \a readProperties is true the
* Constructs a FLAC file from \a file. If \a readProperties is \c true the
* file's audio properties will also be read.
*
* If this file contains and ID3v2 tag the frames will be created using
* If this file contains an ID3v2 tag, the frames will be created using
* \a frameFactory.
*
* \note In the current implementation, \a propertiesStyle is ignored.
*
* \deprecated Use the constructor above.
*/
// BIC: merge with the above constructor
TAGLIB_DEPRECATED
File(FileName file, ID3v2::FrameFactory *frameFactory,
bool readProperties = true,
Properties::ReadStyle propertiesStyle = Properties::Average);
/*!
* Constructs a FLAC file from \a stream. If \a readProperties is true the
* Constructs a FLAC file from \a stream. If \a readProperties is \c true the
* file's audio properties will also be read.
*
* \note TagLib will *not* take ownership of the stream, the caller is
* responsible for deleting it after the File object.
*
* If this file contains and ID3v2 tag the frames will be created using
* \a frameFactory.
* If this file contains an ID3v2 tag, the frames will be created using
* \a frameFactory (default if null).
*
* \note In the current implementation, \a propertiesStyle is ignored.
*/
// BIC: merge with the above constructor
File(IOStream *stream, bool readProperties = true,
Properties::ReadStyle propertiesStyle = Properties::Average,
ID3v2::FrameFactory *frameFactory = nullptr);
/*!
* Constructs a FLAC file from \a stream. If \a readProperties is \c true the
* file's audio properties will also be read.
*
* \note TagLib will *not* take ownership of the stream, the caller is
* responsible for deleting it after the File object.
*
* If this file contains an ID3v2 tag, the frames will be created using
* \a frameFactory.
*
* \note In the current implementation, \a propertiesStyle is ignored.
*
* \deprecated Use the constructor above.
*/
TAGLIB_DEPRECATED
File(IOStream *stream, ID3v2::FrameFactory *frameFactory,
bool readProperties = true,
Properties::ReadStyle propertiesStyle = Properties::Average);
@@ -129,7 +149,10 @@ namespace TagLib {
/*!
* Destroys this instance of the File.
*/
virtual ~File();
~File() override;
File(const File &) = delete;
File &operator=(const File &) = delete;
/*!
* Returns the Tag for this file. This will be a union of XiphComment,
@@ -139,7 +162,7 @@ namespace TagLib {
* \see ID3v1Tag()
* \see XiphComment()
*/
virtual TagLib::Tag *tag() const;
TagLib::Tag *tag() const override;
/*!
* Implements the unified property interface -- export function.
@@ -147,9 +170,9 @@ namespace TagLib {
* only the first one (in the order XiphComment, ID3v2, ID3v1) will be
* converted to the PropertyMap.
*/
PropertyMap properties() const;
PropertyMap properties() const override;
void removeUnsupportedProperties(const StringList &);
void removeUnsupportedProperties(const StringList &) override;
/*!
* Implements the unified property interface -- import function.
@@ -158,35 +181,52 @@ namespace TagLib {
* Ignores any changes to ID3v1 or ID3v2 comments since they are not allowed
* in the FLAC specification.
*/
PropertyMap setProperties(const PropertyMap &);
PropertyMap setProperties(const PropertyMap &) override;
/*!
* Returns ["PICTURE"] if any picture is stored in METADATA_BLOCK_PICTURE.
*/
StringList complexPropertyKeys() const override;
/*!
* Get the pictures stored in METADATA_BLOCK_PICTURE as complex properties
* for \a key "PICTURE".
*/
List<VariantMap> complexProperties(const String &key) const override;
/*!
* Set the complex properties \a value as pictures in METADATA_BLOCK_PICTURE
* for \a key "PICTURE".
*/
bool setComplexProperties(const String &key, const List<VariantMap> &value) override;
/*!
* Returns the FLAC::Properties for this file. If no audio properties
* were read then this will return a null pointer.
*/
virtual Properties *audioProperties() const;
Properties *audioProperties() const override;
/*!
* Save the file. This will primarily save the XiphComment, but
* will also keep any old ID3-tags up to date. If the file
* has no XiphComment, one will be constructed from the ID3-tags.
*
* This returns true if the save was successful.
* This returns \c true if the save was successful.
*/
virtual bool save();
bool save() override;
/*!
* Returns a pointer to the ID3v2 tag of the file.
*
* If \a create is false (the default) this returns a null pointer
* if there is no valid ID3v2 tag. If \a create is true it will create
* If \a create is \c false (the default) this returns a null pointer
* if there is no valid ID3v2 tag. If \a create is \c true it will create
* an ID3v2 tag if one does not exist and returns a valid pointer.
*
* \note This may return a valid pointer regardless of whether or not the
* file on disk has an ID3v2 tag. Use hasID3v2Tag() to check if the file
* on disk actually has an ID3v2 tag.
*
* \note The Tag <b>is still</b> owned by the MPEG::File and should not be
* \note The Tag <b>is still</b> owned by the FLAC::File and should not be
* deleted by the user. It will be deleted when the file (object) is
* destroyed.
*
@@ -197,15 +237,15 @@ namespace TagLib {
/*!
* Returns a pointer to the ID3v1 tag of the file.
*
* If \a create is false (the default) this returns a null pointer
* if there is no valid APE tag. If \a create is true it will create
* If \a create is \c false (the default) this returns a null pointer
* if there is no valid APE tag. If \a create is \c true it will create
* an APE tag if one does not exist and returns a valid pointer.
*
* \note This may return a valid pointer regardless of whether or not the
* file on disk has an ID3v1 tag. Use hasID3v1Tag() to check if the file
* on disk actually has an ID3v1 tag.
*
* \note The Tag <b>is still</b> owned by the MPEG::File and should not be
* \note The Tag <b>is still</b> owned by the FLAC::File and should not be
* deleted by the user. It will be deleted when the file (object) is
* destroyed.
*
@@ -216,8 +256,8 @@ namespace TagLib {
/*!
* Returns a pointer to the XiphComment for the file.
*
* If \a create is false (the default) this returns a null pointer
* if there is no valid XiphComment. If \a create is true it will create
* If \a create is \c false (the default) this returns a null pointer
* if there is no valid XiphComment. If \a create is \c true it will create
* a XiphComment if one does not exist and returns a valid pointer.
*
* \note This may return a valid pointer regardless of whether or not the
@@ -232,40 +272,14 @@ namespace TagLib {
*/
Ogg::XiphComment *xiphComment(bool create = false);
/*!
* Set the ID3v2::FrameFactory to something other than the default. This
* can be used to specify the way that ID3v2 frames will be interpreted
* when
*
* \see ID3v2FrameFactory
* \deprecated This value should be passed in via the constructor
*/
void setID3v2FrameFactory(const ID3v2::FrameFactory *factory);
/*!
* Returns the block of data used by FLAC::Properties for parsing the
* stream properties.
*
* \deprecated Always returns an empty vector.
*/
ByteVector streamInfoData(); // BIC: remove
/*!
* Returns the length of the audio-stream, used by FLAC::Properties for
* calculating the bitrate.
*
* \deprecated Always returns zero.
*/
long streamLength(); // BIC: remove
/*!
* Returns a list of pictures attached to the FLAC file.
*/
List<Picture *> pictureList();
/*!
* Removes an attached picture. If \a del is true the picture's memory
* will be freed; if it is false, it must be deleted by the user.
* Removes an attached picture. If \a del is \c true the picture's memory
* will be freed; if it is \c false, it must be deleted by the user.
*/
void removePicture(Picture *picture, bool del = true);
@@ -318,17 +332,24 @@ namespace TagLib {
*/
bool hasID3v2Tag() const;
private:
File(const File &);
File &operator=(const File &);
/*!
* Returns whether or not the given \a stream can be opened as a FLAC
* file.
*
* \note This method is designed to do a quick check. The result may
* not necessarily be correct.
*/
static bool isSupported(IOStream *stream);
private:
void read(bool readProperties);
void scan();
class FilePrivate;
FilePrivate *d;
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
std::unique_ptr<FilePrivate> d;
};
}
}
} // namespace FLAC
} // namespace TagLib
#endif

View File

@@ -23,25 +23,14 @@
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#include <taglib.h>
#include <tdebug.h>
#include "flacmetadatablock.h"
using namespace TagLib;
class FLAC::MetadataBlock::MetadataBlockPrivate
{
public:
MetadataBlockPrivate() {}
};
FLAC::MetadataBlock::MetadataBlock()
{
d = 0;
}
FLAC::MetadataBlock::~MetadataBlock()
{
}
FLAC::MetadataBlock::MetadataBlock() = default;
FLAC::MetadataBlock::~MetadataBlock() = default;

View File

@@ -26,20 +26,21 @@
#ifndef TAGLIB_FLACMETADATABLOCK_H
#define TAGLIB_FLACMETADATABLOCK_H
#include "tlist.h"
#include "tbytevector.h"
#include "taglib_export.h"
namespace TagLib {
namespace FLAC {
//! FLAC metadata block
class TAGLIB_EXPORT MetadataBlock
{
public:
MetadataBlock();
virtual ~MetadataBlock();
MetadataBlock(const MetadataBlock &item) = delete;
MetadataBlock &operator=(const MetadataBlock &item) = delete;
enum BlockType {
StreamInfo = 0,
Padding,
@@ -61,15 +62,10 @@ namespace TagLib {
virtual ByteVector render() const = 0;
private:
MetadataBlock(const MetadataBlock &item);
MetadataBlock &operator=(const MetadataBlock &item);
class MetadataBlockPrivate;
MetadataBlockPrivate *d;
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
std::unique_ptr<MetadataBlockPrivate> d;
};
}
}
} // namespace FLAC
} // namespace TagLib
#endif

View File

@@ -23,48 +23,37 @@
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#include <taglib.h>
#include <tdebug.h>
#include "flacpicture.h"
#include "tdebug.h"
using namespace TagLib;
class FLAC::Picture::PicturePrivate
{
public:
PicturePrivate() :
type(FLAC::Picture::Other),
width(0),
height(0),
colorDepth(0),
numColors(0)
{}
Type type;
Type type { FLAC::Picture::Other };
String mimeType;
String description;
int width;
int height;
int colorDepth;
int numColors;
int width { 0 };
int height { 0 };
int colorDepth { 0 };
int numColors { 0 };
ByteVector data;
};
FLAC::Picture::Picture()
FLAC::Picture::Picture() :
d(std::make_unique<PicturePrivate>())
{
d = new PicturePrivate;
}
FLAC::Picture::Picture(const ByteVector &data)
FLAC::Picture::Picture(const ByteVector &data) :
d(std::make_unique<PicturePrivate>())
{
d = new PicturePrivate;
parse(data);
}
FLAC::Picture::~Picture()
{
delete d;
}
FLAC::Picture::~Picture() = default;
int FLAC::Picture::code() const
{
@@ -79,7 +68,7 @@ bool FLAC::Picture::parse(const ByteVector &data)
}
unsigned int pos = 0;
d->type = FLAC::Picture::Type(data.toUInt(pos));
d->type = static_cast<FLAC::Picture::Type>(data.toUInt(pos));
pos += 4;
unsigned int mimeTypeLength = data.toUInt(pos);
pos += 4;
@@ -214,4 +203,3 @@ void FLAC::Picture::setData(const ByteVector &data)
{
d->data = data;
}

View File

@@ -29,68 +29,28 @@
#include "tlist.h"
#include "tstring.h"
#include "tbytevector.h"
#include "tpicturetype.h"
#include "taglib_export.h"
#include "flacmetadatablock.h"
namespace TagLib {
namespace FLAC {
//! FLAC picture
class TAGLIB_EXPORT Picture : public MetadataBlock
{
public:
/*!
/*
* This describes the function or content of the picture.
*/
enum Type {
//! A type not enumerated below
Other = 0x00,
//! 32x32 PNG image that should be used as the file icon
FileIcon = 0x01,
//! File icon of a different size or format
OtherFileIcon = 0x02,
//! Front cover image of the album
FrontCover = 0x03,
//! Back cover image of the album
BackCover = 0x04,
//! Inside leaflet page of the album
LeafletPage = 0x05,
//! Image from the album itself
Media = 0x06,
//! Picture of the lead artist or soloist
LeadArtist = 0x07,
//! Picture of the artist or performer
Artist = 0x08,
//! Picture of the conductor
Conductor = 0x09,
//! Picture of the band or orchestra
Band = 0x0A,
//! Picture of the composer
Composer = 0x0B,
//! Picture of the lyricist or text writer
Lyricist = 0x0C,
//! Picture of the recording location or studio
RecordingLocation = 0x0D,
//! Picture of the artists during recording
DuringRecording = 0x0E,
//! Picture of the artists during performance
DuringPerformance = 0x0F,
//! Picture from a movie or video related to the track
MovieScreenCapture = 0x10,
//! Picture of a large, coloured fish
ColouredFish = 0x11,
//! Illustration related to the track
Illustration = 0x12,
//! Logo of the band or performer
BandLogo = 0x13,
//! Logo of the publisher (record company)
PublisherLogo = 0x14
};
DECLARE_PICTURE_TYPE_ENUM(Type)
Picture();
Picture(const ByteVector &data);
~Picture();
~Picture() override;
Picture(const Picture &item) = delete;
Picture &operator=(const Picture &item) = delete;
/*!
* Returns the type of the image.
@@ -112,7 +72,7 @@ namespace TagLib {
* Sets the mime type of the image. This should in most cases be
* "image/png" or "image/jpeg".
*/
void setMimeType(const String &m);
void setMimeType(const String &mimeType);
/*!
* Returns a text description of the image.
@@ -121,10 +81,10 @@ namespace TagLib {
String description() const;
/*!
* Sets a textual description of the image to \a desc.
* Sets a textual description of the image to \a description.
*/
void setDescription(const String &desc);
void setDescription(const String &description);
/*!
* Returns the width of the image.
@@ -134,7 +94,7 @@ namespace TagLib {
/*!
* Sets the width of the image.
*/
void setWidth(int w);
void setWidth(int width);
/*!
* Returns the height of the image.
@@ -144,7 +104,7 @@ namespace TagLib {
/*!
* Sets the height of the image.
*/
void setHeight(int h);
void setHeight(int height);
/*!
* Returns the color depth (in bits-per-pixel) of the image.
@@ -154,7 +114,7 @@ namespace TagLib {
/*!
* Sets the color depth (in bits-per-pixel) of the image.
*/
void setColorDepth(int depth);
void setColorDepth(int colorDepth);
/*!
* Returns the number of colors used on the image..
@@ -179,30 +139,25 @@ namespace TagLib {
/*!
* Returns the FLAC metadata block type.
*/
int code() const;
int code() const override;
/*!
* Render the content to the FLAC picture block format.
*/
ByteVector render() const;
ByteVector render() const override;
/*!
* Parse the picture data in the FLAC picture block format.
*/
bool parse(const ByteVector &rawData);
bool parse(const ByteVector &data);
private:
Picture(const Picture &item);
Picture &operator=(const Picture &item);
class PicturePrivate;
PicturePrivate *d;
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
std::unique_ptr<PicturePrivate> d;
};
typedef List<Picture> PictureList;
}
}
using PictureList = List<Picture>;
} // namespace FLAC
} // namespace TagLib
#endif

View File

@@ -23,31 +23,22 @@
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#include <tstring.h>
#include <tdebug.h>
#include "flacproperties.h"
#include "flacfile.h"
#include "tstring.h"
#include "tdebug.h"
using namespace TagLib;
class FLAC::Properties::PropertiesPrivate
{
public:
PropertiesPrivate() :
length(0),
bitrate(0),
sampleRate(0),
bitsPerSample(0),
channels(0),
sampleFrames(0) {}
int length;
int bitrate;
int sampleRate;
int bitsPerSample;
int channels;
unsigned long long sampleFrames;
int length { 0 };
int bitrate { 0 };
int sampleRate { 0 };
int bitsPerSample { 0 };
int channels { 0 };
unsigned long long sampleFrames { 0 };
ByteVector signature;
};
@@ -55,34 +46,14 @@ public:
// public members
////////////////////////////////////////////////////////////////////////////////
FLAC::Properties::Properties(ByteVector data, long streamLength, ReadStyle style) :
FLAC::Properties::Properties(const ByteVector &data, offset_t streamLength, ReadStyle style) :
AudioProperties(style),
d(new PropertiesPrivate())
d(std::make_unique<PropertiesPrivate>())
{
read(data, streamLength);
}
FLAC::Properties::Properties(File *, ReadStyle style) :
AudioProperties(style),
d(new PropertiesPrivate())
{
debug("FLAC::Properties::Properties() - This constructor is no longer used.");
}
FLAC::Properties::~Properties()
{
delete d;
}
int FLAC::Properties::length() const
{
return lengthInSeconds();
}
int FLAC::Properties::lengthInSeconds() const
{
return d->length / 1000;
}
FLAC::Properties::~Properties() = default;
int FLAC::Properties::lengthInMilliseconds() const
{
@@ -104,11 +75,6 @@ int FLAC::Properties::bitsPerSample() const
return d->bitsPerSample;
}
int FLAC::Properties::sampleWidth() const
{
return bitsPerSample();
}
int FLAC::Properties::channels() const
{
return d->channels;
@@ -128,7 +94,7 @@ ByteVector FLAC::Properties::signature() const
// private members
////////////////////////////////////////////////////////////////////////////////
void FLAC::Properties::read(const ByteVector &data, long streamLength)
void FLAC::Properties::read(const ByteVector &data, offset_t streamLength)
{
if(data.size() < 18) {
debug("FLAC::Properties::read() - FLAC properties must contain at least 18 bytes.");
@@ -166,9 +132,9 @@ void FLAC::Properties::read(const ByteVector &data, long streamLength)
d->sampleFrames = (hi << 32) | lo;
if(d->sampleFrames > 0 && d->sampleRate > 0) {
const double length = d->sampleFrames * 1000.0 / d->sampleRate;
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

@@ -26,6 +26,7 @@
#ifndef TAGLIB_FLACPROPERTIES_H
#define TAGLIB_FLACPROPERTIES_H
#include "tbytevector.h"
#include "taglib_export.h"
#include "audioproperties.h"
@@ -33,12 +34,10 @@ namespace TagLib {
namespace FLAC {
class File;
//! An implementation of audio property reading for FLAC
/*!
* This reads the data from an FLAC stream found in the AudioProperties
* This reads the data from a FLAC stream found in the AudioProperties
* API.
*/
@@ -49,62 +48,37 @@ namespace TagLib {
* Create an instance of FLAC::Properties with the data read from the
* ByteVector \a data.
*/
// BIC: switch to const reference
Properties(ByteVector data, long streamLength, ReadStyle style = Average);
/*!
* Create an instance of FLAC::Properties with the data read from the
* FLAC::File \a file.
*/
// BIC: remove
Properties(File *file, ReadStyle style = Average);
Properties(const ByteVector &data, offset_t streamLength, ReadStyle style = Average);
/*!
* Destroys this FLAC::Properties instance.
*/
virtual ~Properties();
~Properties() override;
/*!
* Returns the length of the file in seconds. The length is rounded down to
* the nearest whole second.
*
* \note This method is just an alias of lengthInSeconds().
*
* \deprecated
*/
virtual int length() const;
/*!
* Returns the length of the file in seconds. The length is rounded down to
* the nearest whole second.
*
* \see lengthInMilliseconds()
*/
// BIC: make virtual
int lengthInSeconds() const;
Properties(const Properties &) = delete;
Properties &operator=(const Properties &) = delete;
/*!
* Returns the length of the file in milliseconds.
*
* \see lengthInSeconds()
*/
// BIC: make virtual
int lengthInMilliseconds() const;
int lengthInMilliseconds() const override;
/*!
* Returns the average bit rate of the file in kb/s.
*/
virtual int bitrate() const;
int bitrate() const override;
/*!
* Returns the sample rate in Hz.
*/
virtual int sampleRate() const;
int sampleRate() const override;
/*!
* Returns the number of audio channels.
*/
virtual int channels() const;
int channels() const override;
/*!
* Returns the number of bits per audio sample as read from the FLAC
@@ -112,16 +86,6 @@ namespace TagLib {
*/
int bitsPerSample() const;
/*!
* Returns the sample width as read from the FLAC identification
* header.
*
* \note This method is just an alias of bitsPerSample().
*
* \deprecated
*/
int sampleWidth() const;
/*!
* Return the number of sample frames.
*/
@@ -134,15 +98,13 @@ namespace TagLib {
ByteVector signature() const;
private:
Properties(const Properties &);
Properties &operator=(const Properties &);
void read(const ByteVector &data, long streamLength);
void read(const ByteVector &data, offset_t streamLength);
class PropertiesPrivate;
PropertiesPrivate *d;
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
std::unique_ptr<PropertiesPrivate> d;
};
}
}
} // namespace FLAC
} // namespace TagLib
#endif

View File

@@ -23,9 +23,6 @@
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#include <taglib.h>
#include <tdebug.h>
#include <tstring.h>
#include "flacunknownmetadatablock.h"
using namespace TagLib;
@@ -33,24 +30,18 @@ using namespace TagLib;
class FLAC::UnknownMetadataBlock::UnknownMetadataBlockPrivate
{
public:
UnknownMetadataBlockPrivate() : code(0) {}
int code;
int code { 0 };
ByteVector data;
};
FLAC::UnknownMetadataBlock::UnknownMetadataBlock(int code, const ByteVector &data)
FLAC::UnknownMetadataBlock::UnknownMetadataBlock(int code, const ByteVector &data) :
d(std::make_unique<UnknownMetadataBlockPrivate>())
{
d = new UnknownMetadataBlockPrivate;
d->code = code;
//debug(String(data.toHex()));
d->data = data;
}
FLAC::UnknownMetadataBlock::~UnknownMetadataBlock()
{
delete d;
}
FLAC::UnknownMetadataBlock::~UnknownMetadataBlock() = default;
int FLAC::UnknownMetadataBlock::code() const
{
@@ -76,4 +67,3 @@ ByteVector FLAC::UnknownMetadataBlock::render() const
{
return d->data;
}

View File

@@ -32,19 +32,21 @@
#include "flacmetadatablock.h"
namespace TagLib {
namespace FLAC {
//! Unknown FLAC metadata block
class TAGLIB_EXPORT UnknownMetadataBlock : public MetadataBlock
{
public:
UnknownMetadataBlock(int blockType, const ByteVector &data);
~UnknownMetadataBlock();
UnknownMetadataBlock(int code, const ByteVector &data);
~UnknownMetadataBlock() override;
UnknownMetadataBlock(const UnknownMetadataBlock &item) = delete;
UnknownMetadataBlock &operator=(const UnknownMetadataBlock &item) = delete;
/*!
* Returns the FLAC metadata block type.
*/
int code() const;
int code() const override;
/*!
* Sets the FLAC metadata block type.
@@ -64,18 +66,13 @@ namespace TagLib {
/*!
* Render the content of the block.
*/
ByteVector render() const;
ByteVector render() const override;
private:
UnknownMetadataBlock(const MetadataBlock &item);
UnknownMetadataBlock &operator=(const MetadataBlock &item);
class UnknownMetadataBlockPrivate;
UnknownMetadataBlockPrivate *d;
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
std::unique_ptr<UnknownMetadataBlockPrivate> d;
};
}
}
} // namespace FLAC
} // namespace TagLib
#endif

View File

@@ -23,12 +23,11 @@
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#include "itfile.h"
#include "tstringlist.h"
#include "itfile.h"
#include "tdebug.h"
#include "modfileprivate.h"
#include "tpropertymap.h"
using namespace TagLib;
using namespace IT;
@@ -37,7 +36,7 @@ class IT::File::FilePrivate
{
public:
FilePrivate(AudioProperties::ReadStyle propertiesStyle)
: tag(), properties(propertiesStyle)
: properties(propertiesStyle)
{
}
@@ -48,7 +47,7 @@ public:
IT::File::File(FileName file, bool readProperties,
AudioProperties::ReadStyle propertiesStyle) :
Mod::FileBase(file),
d(new FilePrivate(propertiesStyle))
d(std::make_unique<FilePrivate>(propertiesStyle))
{
if(isOpen())
read(readProperties);
@@ -57,32 +56,19 @@ IT::File::File(FileName file, bool readProperties,
IT::File::File(IOStream *stream, bool readProperties,
AudioProperties::ReadStyle propertiesStyle) :
Mod::FileBase(stream),
d(new FilePrivate(propertiesStyle))
d(std::make_unique<FilePrivate>(propertiesStyle))
{
if(isOpen())
read(readProperties);
}
IT::File::~File()
{
delete d;
}
IT::File::~File() = default;
Mod::Tag *IT::File::tag() const
{
return &d->tag;
}
PropertyMap IT::File::properties() const
{
return d->tag.properties();
}
PropertyMap IT::File::setProperties(const PropertyMap &properties)
{
return d->tag.setProperties(properties);
}
IT::Properties *IT::File::audioProperties() const
{
return &d->properties;
@@ -113,7 +99,7 @@ bool IT::File::save()
// write comment as instrument and sample names:
StringList lines = d->tag.comment().split("\n");
for(unsigned short i = 0; i < instrumentCount; ++ i) {
seek(192L + length + ((long)i << 2));
seek(192L + length + (static_cast<long>(i) << 2));
unsigned long instrumentOffset = 0;
if(!readU32L(instrumentOffset))
return false;
@@ -128,14 +114,14 @@ bool IT::File::save()
}
for(unsigned short i = 0; i < sampleCount; ++ i) {
seek(192L + length + ((long)instrumentCount << 2) + ((long)i << 2));
seek(192L + length + (static_cast<long>(instrumentCount) << 2) + (static_cast<long>(i) << 2));
unsigned long sampleOffset = 0;
if(!readU32L(sampleOffset))
return false;
seek(sampleOffset + 20);
if((unsigned int)(i + instrumentCount) < lines.size())
if(static_cast<unsigned int>(i + instrumentCount) < lines.size())
writeString(lines[i + instrumentCount], 25);
else
writeString(String(), 25);
@@ -152,7 +138,7 @@ bool IT::File::save()
// terminating NUL but it does not hurt to add one:
if(message.size() > 7999)
message.resize(7999);
message.append((char)0);
message.append(static_cast<char>(0));
unsigned short special = 0;
unsigned short messageLength = 0;
@@ -162,7 +148,7 @@ bool IT::File::save()
if(!readU16L(special))
return false;
unsigned long fileSize = File::length();
auto fileSize = static_cast<unsigned long>(File::length());
if(special & Properties::MessageAttached) {
seek(54);
if(!readU16L(messageLength) || !readU32L(messageOffset))
@@ -181,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);
@@ -239,7 +225,7 @@ void IT::File::read(bool)
seek(messageOffset);
ByteVector messageBytes = readBlock(messageLength);
READ_ASSERT(messageBytes.size() == messageLength);
int index = messageBytes.find((char) 0);
int index = messageBytes.find(static_cast<char>(0));
if(index > -1)
messageBytes.resize(index, 0);
messageBytes.replace('\r', '\n');
@@ -257,8 +243,8 @@ void IT::File::read(bool)
// I don't count disabled and muted channels.
// But this always gives 64 channels for all my files anyway.
// Strangely VLC does report other values. I wonder how VLC
// gets it's values.
if((unsigned char) pannings[i] < 128 && volumes[i] > 0)
// gets its values.
if(static_cast<unsigned char>(pannings[i]) < 128 && volumes[i] > 0)
++channels;
}
d->properties.setChannels(channels);
@@ -277,10 +263,10 @@ void IT::File::read(bool)
// in the instrument/sample names and more characters
// afterwards. The spec does not mention such a case.
// Currently I just discard anything after a nil, but
// e.g. VLC seems to interprete a nil as a space. I
// e.g. VLC seems to interpret a nil as a space. I
// don't know what is the proper behaviour.
for(unsigned short i = 0; i < instrumentCount; ++ i) {
seek(192L + length + ((long)i << 2));
seek(192L + length + (static_cast<long>(i) << 2));
READ_U32L_AS(instrumentOffset);
seek(instrumentOffset);
@@ -296,7 +282,7 @@ void IT::File::read(bool)
}
for(unsigned short i = 0; i < sampleCount; ++ i) {
seek(192L + length + ((long)instrumentCount << 2) + ((long)i << 2));
seek(192L + length + (static_cast<long>(instrumentCount) << 2) + (static_cast<long>(i) << 2));
READ_U32L_AS(sampleOffset);
seek(sampleOffset);
@@ -328,7 +314,7 @@ void IT::File::read(bool)
comment.append(sampleName);
}
if(message.size() > 0)
if(!message.isEmpty())
comment.append(message);
d->tag.setComment(comment.toString("\n"));
d->tag.setTrackerName("Impulse Tracker");

View File

@@ -23,20 +23,35 @@
#define TAGLIB_ITFILE_H
#include "tfile.h"
#include "audioproperties.h"
#include "taglib_export.h"
#include "audioproperties.h"
#include "modfilebase.h"
#include "modtag.h"
#include "itproperties.h"
namespace TagLib {
//! An implementation of Impulse Tracker metadata
/*!
* This is an implementation of Impulse Tracker metadata.
*/
namespace IT {
//! An implementation of TagLib::File with IT specific methods
/*!
* This implements and provides an interface for IT 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 IT files.
*/
class TAGLIB_EXPORT File : public Mod::FileBase {
public:
/*!
* Constructs a Impulse Tracker file from \a file.
* Constructs an Impulse Tracker file from \a file.
*
* \note In the current implementation, both \a readProperties and
* \a propertiesStyle are ignored. The audio properties are always
@@ -47,7 +62,7 @@ namespace TagLib {
AudioProperties::Average);
/*!
* Constructs a Impulse Tracker file from \a stream.
* Constructs an Impulse Tracker file from \a stream.
*
* \note In the current implementation, both \a readProperties and
* \a propertiesStyle are ignored. The audio properties are always
@@ -63,27 +78,18 @@ namespace TagLib {
/*!
* Destroys this instance of the File.
*/
virtual ~File();
~File() override;
Mod::Tag *tag() const;
File(const File &) = delete;
File &operator=(const File &) = delete;
/*!
* Forwards to Mod::Tag::properties().
* BIC: will be removed once File::toDict() is made virtual
*/
PropertyMap properties() const;
/*!
* Forwards to Mod::Tag::setProperties().
* BIC: will be removed once File::setProperties() is made virtual
*/
PropertyMap setProperties(const PropertyMap &);
Mod::Tag *tag() const override;
/*!
* Returns the IT::Properties for this file. If no audio properties
* were read then this will return a null pointer.
*/
IT::Properties *audioProperties() const;
IT::Properties *audioProperties() const override;
/*!
* Save the file.
@@ -91,19 +97,16 @@ namespace TagLib {
*
* \note Saving Impulse Tracker tags is not supported.
*/
bool save();
bool save() override;
private:
File(const File &);
File &operator=(const File &);
void read(bool readProperties);
class FilePrivate;
FilePrivate *d;
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
std::unique_ptr<FilePrivate> d;
};
}
}
} // namespace IT
} // namespace TagLib
#endif

View File

@@ -23,7 +23,6 @@
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#include "itproperties.h"
using namespace TagLib;
@@ -32,77 +31,30 @@ using namespace IT;
class IT::Properties::PropertiesPrivate
{
public:
PropertiesPrivate() :
channels(0),
lengthInPatterns(0),
instrumentCount(0),
sampleCount(0),
patternCount(0),
version(0),
compatibleVersion(0),
flags(0),
special(0),
globalVolume(0),
mixVolume(0),
tempo(0),
bpmSpeed(0),
panningSeparation(0),
pitchWheelDepth(0)
{
}
int channels;
unsigned short lengthInPatterns;
unsigned short instrumentCount;
unsigned short sampleCount;
unsigned short patternCount;
unsigned short version;
unsigned short compatibleVersion;
unsigned short flags;
unsigned short special;
unsigned char globalVolume;
unsigned char mixVolume;
unsigned char tempo;
unsigned char bpmSpeed;
unsigned char panningSeparation;
unsigned char pitchWheelDepth;
int channels { 0 };
unsigned short lengthInPatterns { 0 };
unsigned short instrumentCount { 0 };
unsigned short sampleCount { 0 };
unsigned short patternCount { 0 };
unsigned short version { 0 };
unsigned short compatibleVersion { 0 };
unsigned short flags { 0 };
unsigned short special { 0 };
unsigned char globalVolume { 0 };
unsigned char mixVolume { 0 };
unsigned char tempo { 0 };
unsigned char bpmSpeed { 0 };
unsigned char panningSeparation { 0 };
unsigned char pitchWheelDepth { 0 };
};
IT::Properties::Properties(AudioProperties::ReadStyle propertiesStyle) :
AudioProperties(propertiesStyle),
d(new PropertiesPrivate)
d(std::make_unique<PropertiesPrivate>())
{
}
IT::Properties::~Properties()
{
delete d;
}
int IT::Properties::length() const
{
return 0;
}
int IT::Properties::lengthInSeconds() const
{
return 0;
}
int IT::Properties::lengthInMilliseconds() const
{
return 0;
}
int IT::Properties::bitrate() const
{
return 0;
}
int IT::Properties::sampleRate() const
{
return 0;
}
IT::Properties::~Properties() = default;
int IT::Properties::channels() const
{

View File

@@ -26,13 +26,12 @@
#ifndef TAGLIB_ITPROPERTIES_H
#define TAGLIB_ITPROPERTIES_H
#include "taglib.h"
#include "audioproperties.h"
namespace TagLib {
namespace IT {
//! An implementation of audio property reading for IT
class TAGLIB_EXPORT Properties : public AudioProperties {
friend class File;
public:
/*! Flag bits. */
enum {
@@ -53,55 +52,51 @@ namespace TagLib {
};
Properties(AudioProperties::ReadStyle propertiesStyle);
virtual ~Properties();
~Properties() override;
int length() const;
int lengthInSeconds() const;
int lengthInMilliseconds() const;
int bitrate() const;
int sampleRate() const;
int channels() const;
Properties(const Properties &) = delete;
Properties &operator=(const Properties &) = delete;
unsigned short lengthInPatterns() const;
bool stereo() const;
unsigned short instrumentCount() const;
unsigned short sampleCount() const;
unsigned short patternCount() const;
unsigned short version() const;
int channels() const override;
unsigned short lengthInPatterns() const;
bool stereo() const;
unsigned short instrumentCount() const;
unsigned short sampleCount() const;
unsigned short patternCount() const;
unsigned short version() const;
unsigned short compatibleVersion() const;
unsigned short flags() const;
unsigned short special() const;
unsigned char globalVolume() const;
unsigned char mixVolume() const;
unsigned char tempo() const;
unsigned char bpmSpeed() const;
unsigned char panningSeparation() const;
unsigned char pitchWheelDepth() const;
unsigned short flags() const;
unsigned short special() const;
unsigned char globalVolume() const;
unsigned char mixVolume() const;
unsigned char tempo() const;
unsigned char bpmSpeed() const;
unsigned char panningSeparation() const;
unsigned char pitchWheelDepth() const;
void setChannels(int channels);
void setLengthInPatterns(unsigned short lengthInPatterns);
void setInstrumentCount(unsigned short instrumentCount);
void setSampleCount (unsigned short sampleCount);
void setSampleCount(unsigned short sampleCount);
void setPatternCount(unsigned short patternCount);
void setVersion (unsigned short version);
void setVersion(unsigned short version);
void setCompatibleVersion(unsigned short compatibleVersion);
void setFlags (unsigned short flags);
void setSpecial (unsigned short special);
void setFlags(unsigned short flags);
void setSpecial(unsigned short special);
void setGlobalVolume(unsigned char globalVolume);
void setMixVolume (unsigned char mixVolume);
void setTempo (unsigned char tempo);
void setBpmSpeed (unsigned char bpmSpeed);
void setMixVolume(unsigned char mixVolume);
void setTempo(unsigned char tempo);
void setBpmSpeed(unsigned char bpmSpeed);
void setPanningSeparation(unsigned char panningSeparation);
void setPitchWheelDepth (unsigned char pitchWheelDepth);
void setPitchWheelDepth(unsigned char pitchWheelDepth);
private:
Properties(const Properties&);
Properties &operator=(const Properties&);
class PropertiesPrivate;
PropertiesPrivate *d;
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
std::unique_ptr<PropertiesPrivate> d;
};
}
}
} // namespace IT
} // namespace TagLib
#endif

View File

@@ -0,0 +1,70 @@
/***************************************************************************
* 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 "ebmlbinaryelement.h"
#include "ebmlutils.h"
#include "tfile.h"
#include "tdebug.h"
using namespace TagLib;
EBML::BinaryElement::BinaryElement(Id id, int sizeLength, offset_t dataSize):
Element(id, sizeLength, dataSize)
{
}
EBML::BinaryElement::BinaryElement(Id id, int sizeLength, offset_t dataSize, offset_t):
Element(id, sizeLength, dataSize)
{
}
EBML::BinaryElement::BinaryElement(Id id):
Element(id, 0, 0)
{
}
const ByteVector& EBML::BinaryElement::getValue() const
{
return value;
}
void EBML::BinaryElement::setValue(const ByteVector& val)
{
value = val;
}
bool EBML::BinaryElement::read(File &file)
{
value = file.readBlock(dataSize);
if(value.size() != dataSize) {
debug("Failed to read binary element");
return false;
}
return true;
}
ByteVector EBML::BinaryElement::render()
{
ByteVector buffer = renderId();
dataSize = value.size();
buffer.append(renderVINT(dataSize, 0));
buffer.append(value);
return buffer;
}

View File

@@ -0,0 +1,51 @@
/***************************************************************************
* 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_EBMLBINARYELEMENT_H
#define TAGLIB_EBMLBINARYELEMENT_H
#ifndef DO_NOT_DOCUMENT
#include "ebmlelement.h"
#include "tbytevector.h"
namespace TagLib {
class File;
namespace EBML {
class BinaryElement : public Element
{
public:
BinaryElement(Id id, int sizeLength, offset_t dataSize);
BinaryElement(Id id, int sizeLength, offset_t dataSize, offset_t);
explicit BinaryElement(Id id);
const ByteVector &getValue() const;
void setValue(const ByteVector &val);
bool read(File &file) override;
ByteVector render() override;
private:
ByteVector value;
};
}
}
#endif
#endif

View File

@@ -0,0 +1,216 @@
/***************************************************************************
* 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 "ebmlelement.h"
#include "ebmlvoidelement.h"
#include "ebmlmasterelement.h"
#include "ebmlbinaryelement.h"
#include "ebmlfloatelement.h"
#include "ebmlmkseekhead.h"
#include "ebmlmksegment.h"
#include "ebmlmktags.h"
#include "ebmlmkattachments.h"
#include "ebmlmkchapters.h"
#include "ebmlmktracks.h"
#include "ebmlstringelement.h"
#include "ebmluintelement.h"
#include "ebmlutils.h"
#include "tfile.h"
#include "tdebug.h"
#include "tutils.h"
using namespace TagLib;
#define RETURN_ELEMENT_FOR_CASE(eid) \
case (eid): return make_unique_element<eid>(id, sizeLength, dataSize, offset)
std::unique_ptr<EBML::Element> EBML::Element::factory(File &file)
{
// Get the element ID
const offset_t offset = file.tell();
unsigned int uintId = readId(file);
if(!uintId) {
debug("Failed to parse EMBL ElementID");
return nullptr;
}
// Get the size length and data length
const auto &[sizeLength, dataSize] = readVINT(file);
if(!sizeLength)
return nullptr;
// Return the subclass
// The enum switch without default will give us a warning if an ID is missing
auto id = static_cast<Id>(uintId);
switch(id) {
RETURN_ELEMENT_FOR_CASE(Id::EBMLHeader);
RETURN_ELEMENT_FOR_CASE(Id::DocType);
RETURN_ELEMENT_FOR_CASE(Id::DocTypeVersion);
RETURN_ELEMENT_FOR_CASE(Id::MkSegment);
RETURN_ELEMENT_FOR_CASE(Id::MkInfo);
RETURN_ELEMENT_FOR_CASE(Id::MkTracks);
RETURN_ELEMENT_FOR_CASE(Id::MkTags);
RETURN_ELEMENT_FOR_CASE(Id::MkAttachments);
RETURN_ELEMENT_FOR_CASE(Id::MkTag);
RETURN_ELEMENT_FOR_CASE(Id::MkTagTargets);
RETURN_ELEMENT_FOR_CASE(Id::MkSimpleTag);
RETURN_ELEMENT_FOR_CASE(Id::MkAttachedFile);
RETURN_ELEMENT_FOR_CASE(Id::MkSeek);
RETURN_ELEMENT_FOR_CASE(Id::MkTrackEntry);
RETURN_ELEMENT_FOR_CASE(Id::MkAudio);
RETURN_ELEMENT_FOR_CASE(Id::MkTagName);
RETURN_ELEMENT_FOR_CASE(Id::MkTagString);
RETURN_ELEMENT_FOR_CASE(Id::MkAttachedFileName);
RETURN_ELEMENT_FOR_CASE(Id::MkAttachedFileDescription);
RETURN_ELEMENT_FOR_CASE(Id::MkTagLanguage);
RETURN_ELEMENT_FOR_CASE(Id::MkAttachedFileMediaType);
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);
RETURN_ELEMENT_FOR_CASE(Id::MkTimestampScale);
RETURN_ELEMENT_FOR_CASE(Id::MkBitDepth);
RETURN_ELEMENT_FOR_CASE(Id::MkChannels);
RETURN_ELEMENT_FOR_CASE(Id::MkAttachedFileData);
RETURN_ELEMENT_FOR_CASE(Id::MkSeekID);
RETURN_ELEMENT_FOR_CASE(Id::MkDuration);
RETURN_ELEMENT_FOR_CASE(Id::MkTitle);
RETURN_ELEMENT_FOR_CASE(Id::MkSamplingFrequency);
RETURN_ELEMENT_FOR_CASE(Id::MkSeekHead);
RETURN_ELEMENT_FOR_CASE(Id::VoidElement);
RETURN_ELEMENT_FOR_CASE(Id::MkCluster);
RETURN_ELEMENT_FOR_CASE(Id::MkCodecState);
RETURN_ELEMENT_FOR_CASE(Id::MkTagBinary);
RETURN_ELEMENT_FOR_CASE(Id::MkCues);
RETURN_ELEMENT_FOR_CASE(Id::MkCuePoint);
RETURN_ELEMENT_FOR_CASE(Id::MkCueTime);
RETURN_ELEMENT_FOR_CASE(Id::MkCueTrackPositions);
RETURN_ELEMENT_FOR_CASE(Id::MkCueTrack);
RETURN_ELEMENT_FOR_CASE(Id::MkCueClusterPosition);
RETURN_ELEMENT_FOR_CASE(Id::MkCueRelativePosition);
RETURN_ELEMENT_FOR_CASE(Id::MkCueDuration);
RETURN_ELEMENT_FOR_CASE(Id::MkCueBlockNumber);
RETURN_ELEMENT_FOR_CASE(Id::MkCueCodecState);
RETURN_ELEMENT_FOR_CASE(Id::MkCueReference);
RETURN_ELEMENT_FOR_CASE(Id::MkCueRefTime);
RETURN_ELEMENT_FOR_CASE(Id::MkChapters);
RETURN_ELEMENT_FOR_CASE(Id::MkEditionEntry);
RETURN_ELEMENT_FOR_CASE(Id::MkEditionUID);
RETURN_ELEMENT_FOR_CASE(Id::MkEditionFlagDefault);
RETURN_ELEMENT_FOR_CASE(Id::MkEditionFlagOrdered);
RETURN_ELEMENT_FOR_CASE(Id::MkChapterAtom);
RETURN_ELEMENT_FOR_CASE(Id::MkChapterUID);
RETURN_ELEMENT_FOR_CASE(Id::MkChapterTimeStart);
RETURN_ELEMENT_FOR_CASE(Id::MkChapterTimeEnd);
RETURN_ELEMENT_FOR_CASE(Id::MkChapterFlagHidden);
RETURN_ELEMENT_FOR_CASE(Id::MkChapterDisplay);
RETURN_ELEMENT_FOR_CASE(Id::MkChapString);
RETURN_ELEMENT_FOR_CASE(Id::MkChapLanguage);
}
return std::make_unique<Element>(id, sizeLength, dataSize);
}
unsigned int EBML::Element::readId(File &file)
{
auto buffer = file.readBlock(1);
if(buffer.size() != 1) {
debug("Failed to read VINT size");
return 0;
}
const unsigned int numBytes = VINTSizeLength<4>(*buffer.begin());
if(!numBytes)
return 0;
if(numBytes > 1)
buffer.append(file.readBlock(numBytes - 1));
if(buffer.size() != numBytes) {
debug("Failed to read VINT data");
return 0;
}
return buffer.toUInt(true);
}
EBML::Element::Element(Id id, int sizeLength, offset_t dataSize):
id(id), sizeLength(sizeLength), dataSize(dataSize)
{
}
EBML::Element::Element(Id id, int sizeLength, offset_t dataSize, offset_t):
id(id), sizeLength(sizeLength), dataSize(dataSize)
{
}
EBML::Element::~Element() = default;
bool EBML::Element::read(File& file)
{
skipData(file);
return true;
}
void EBML::Element::skipData(File &file)
{
file.seek(dataSize, File::Position::Current);
}
EBML::Element::Id EBML::Element::getId() const
{
return id;
}
offset_t EBML::Element::headSize() const
{
return idSize(id) + sizeLength;
}
offset_t EBML::Element::getSize() const
{
return headSize() + dataSize;
}
int EBML::Element::getSizeLength() const
{
return sizeLength;
}
int64_t EBML::Element::getDataSize() const
{
return dataSize;
}
ByteVector EBML::Element::render()
{
ByteVector buffer = renderId();
buffer.append(renderVINT(0, 0));
return buffer;
}
ByteVector EBML::Element::renderId() const
{
const int numBytes = idSize(id);
static const auto byteOrder = Utils::systemByteOrder();
const auto uintId = static_cast<uint32_t>(id);
uint32_t data = byteOrder == Utils::LittleEndian ? Utils::byteSwap(uintId) : uintId;
return ByteVector(reinterpret_cast<char *>(&data) + (4 - numBytes), numBytes);
}

View File

@@ -0,0 +1,255 @@
/***************************************************************************
* 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_EBMLELEMENT_H
#define TAGLIB_EBMLELEMENT_H
#ifndef DO_NOT_DOCUMENT
#include <cstdint>
#include <memory>
#include "taglib.h"
namespace TagLib
{
class File;
class ByteVector;
namespace EBML {
class Element
{
public:
enum class Id : unsigned int
{
EBMLHeader = 0x1A45DFA3,
DocType = 0x4282,
DocTypeVersion = 0x4287,
VoidElement = 0xEC,
MkSegment = 0x18538067,
MkTags = 0x1254C367,
MkTag = 0x7373,
MkTagTargets = 0x63C0,
MkTagTargetTypeValue = 0x68CA,
MkTagTrackUID = 0x63C5,
MkTagEditionUID = 0x63C9,
MkTagChapterUID = 0x63C4,
MkTagAttachmentUID = 0x63C6,
MkSimpleTag = 0x67C8,
MkTagName = 0x45A3,
MkTagLanguage = 0x447A,
MkTagBinary = 0x4485,
MkTagString = 0x4487,
MkTagsTagLanguage = 0x447A,
MkTagsLanguageDefault = 0x4484,
MkAttachments = 0x1941A469,
MkAttachedFile = 0x61A7,
MkAttachedFileDescription = 0x467E,
MkAttachedFileName = 0x466E,
MkAttachedFileMediaType = 0x4660,
MkAttachedFileData = 0x465C,
MkAttachedFileUID = 0x46AE,
MkSeekHead = 0x114D9B74,
MkSeek = 0x4DBB,
MkSeekID = 0x53AB,
MkSeekPosition = 0x53AC,
MkCluster = 0x1F43B675,
MkCodecState = 0xA4,
MkCues = 0x1C53BB6B,
MkCuePoint = 0xBB,
MkCueTime = 0xB3,
MkCueTrackPositions = 0xB7,
MkCueTrack = 0xF7,
MkCueClusterPosition = 0xF1,
MkCueRelativePosition = 0xF0,
MkCueDuration = 0xB2,
MkCueBlockNumber = 0x5378,
MkCueCodecState = 0xEA,
MkCueReference = 0xDB,
MkCueRefTime = 0x96,
MkInfo = 0x1549A966,
MkTimestampScale = 0x2AD7B1,
MkDuration = 0x4489,
MkTitle = 0x7BA9,
MkTracks = 0x1654AE6B,
MkTrackEntry = 0xAE,
MkCodecID = 0x86,
MkAudio = 0xE1,
MkSamplingFrequency = 0xB5,
MkBitDepth = 0x6264,
MkChannels = 0x9F,
MkChapters = 0x1043A770,
MkEditionEntry = 0x45B9,
MkEditionUID = 0x45BC,
MkEditionFlagDefault = 0x45DB,
MkEditionFlagOrdered = 0x45DD,
MkChapterAtom = 0xB6,
MkChapterUID = 0x73C4,
MkChapterTimeStart = 0x91,
MkChapterTimeEnd = 0x92,
MkChapterFlagHidden = 0x98,
MkChapterDisplay = 0x80,
MkChapString = 0x85,
MkChapLanguage = 0x437C,
};
Element(Id id, int sizeLength, offset_t dataSize);
Element(Id id, int sizeLength, offset_t dataSize, offset_t);
virtual ~Element();
virtual bool read(File &file);
void skipData(File &file);
Id getId() const;
offset_t headSize() const;
offset_t getSize() const;
int getSizeLength() const;
int64_t getDataSize() const;
ByteVector renderId() const;
virtual ByteVector render();
static std::unique_ptr<Element> factory(File &file);
static unsigned int readId(File &file);
protected:
Id id;
int sizeLength;
offset_t dataSize;
};
// Template specializations to ensure that elements for the different IDs
// are consistently created and cast. They provide a mapping between IDs
// and Element subclasses. The switch in factory() makes sure that the
// template for all IDs are instantiated, i.e. that every ID has its Element
// subclass mapped.
class MasterElement;
class UIntElement;
class BinaryElement;
class FloatElement;
class MkSegment;
class MkInfo;
class MkTracks;
class MkTags;
class MkAttachments;
class MkSeekHead;
class MkChapters;
class MkCues;
class VoidElement;
class UTF8StringElement;
class Latin1StringElement;
template <Element::Id ID>
struct GetElementTypeById;
template <> struct GetElementTypeById<Element::Id::EBMLHeader> { using type = MasterElement; };
template <> struct GetElementTypeById<Element::Id::DocType> { using type = Latin1StringElement; };
template <> struct GetElementTypeById<Element::Id::DocTypeVersion> { using type = UIntElement; };
template <> struct GetElementTypeById<Element::Id::MkSegment> { using type = MkSegment; };
template <> struct GetElementTypeById<Element::Id::MkInfo> { using type = MkInfo; };
template <> struct GetElementTypeById<Element::Id::MkTracks> { using type = MkTracks; };
template <> struct GetElementTypeById<Element::Id::MkTags> { using type = MkTags; };
template <> struct GetElementTypeById<Element::Id::MkAttachments> { using type = MkAttachments; };
template <> struct GetElementTypeById<Element::Id::MkTag> { using type = MasterElement; };
template <> struct GetElementTypeById<Element::Id::MkTagTargets> { using type = MasterElement; };
template <> struct GetElementTypeById<Element::Id::MkSimpleTag> { using type = MasterElement; };
template <> struct GetElementTypeById<Element::Id::MkAttachedFile> { using type = MasterElement; };
template <> struct GetElementTypeById<Element::Id::MkSeek> { using type = MasterElement; };
template <> struct GetElementTypeById<Element::Id::MkTrackEntry> { using type = MasterElement; };
template <> struct GetElementTypeById<Element::Id::MkAudio> { using type = MasterElement; };
template <> struct GetElementTypeById<Element::Id::MkCuePoint> { using type = MasterElement; };
template <> struct GetElementTypeById<Element::Id::MkCueTrackPositions> { using type = MasterElement; };
template <> struct GetElementTypeById<Element::Id::MkCueReference> { using type = MasterElement; };
template <> struct GetElementTypeById<Element::Id::MkCluster> { using type = MasterElement; };
template <> struct GetElementTypeById<Element::Id::MkCues> { using type = MkCues; };
template <> struct GetElementTypeById<Element::Id::MkTagName> { using type = UTF8StringElement; };
template <> struct GetElementTypeById<Element::Id::MkTagString> { using type = UTF8StringElement; };
template <> struct GetElementTypeById<Element::Id::MkAttachedFileName> { using type = UTF8StringElement; };
template <> struct GetElementTypeById<Element::Id::MkAttachedFileDescription> { using type = UTF8StringElement; };
template <> struct GetElementTypeById<Element::Id::MkTagLanguage> { using type = Latin1StringElement; };
template <> struct GetElementTypeById<Element::Id::MkAttachedFileMediaType> { using type = Latin1StringElement; };
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; };
template <> struct GetElementTypeById<Element::Id::MkBitDepth> { using type = UIntElement; };
template <> struct GetElementTypeById<Element::Id::MkChannels> { using type = UIntElement; };
template <> struct GetElementTypeById<Element::Id::MkCueTime> { using type = UIntElement; };
template <> struct GetElementTypeById<Element::Id::MkCueTrack> { using type = UIntElement; };
template <> struct GetElementTypeById<Element::Id::MkCueClusterPosition> { using type = UIntElement; };
template <> struct GetElementTypeById<Element::Id::MkCueRelativePosition> { using type = UIntElement; };
template <> struct GetElementTypeById<Element::Id::MkCueDuration> { using type = UIntElement; };
template <> struct GetElementTypeById<Element::Id::MkCueBlockNumber> { using type = UIntElement; };
template <> struct GetElementTypeById<Element::Id::MkCueCodecState> { using type = UIntElement; };
template <> struct GetElementTypeById<Element::Id::MkCueRefTime> { using type = UIntElement; };
template <> struct GetElementTypeById<Element::Id::MkTagsLanguageDefault> { using type = UIntElement; };
template <> struct GetElementTypeById<Element::Id::MkAttachedFileData> { using type = BinaryElement; };
template <> struct GetElementTypeById<Element::Id::MkSeekID> { using type = BinaryElement; };
template <> struct GetElementTypeById<Element::Id::MkTagBinary> { using type = BinaryElement; };
template <> struct GetElementTypeById<Element::Id::MkCodecState> { using type = BinaryElement; };
template <> struct GetElementTypeById<Element::Id::MkDuration> { using type = FloatElement; };
template <> struct GetElementTypeById<Element::Id::MkTitle> { using type = UTF8StringElement; };
template <> struct GetElementTypeById<Element::Id::MkSamplingFrequency> { using type = FloatElement; };
template <> struct GetElementTypeById<Element::Id::MkSeekHead> { using type = MkSeekHead; };
template <> struct GetElementTypeById<Element::Id::MkChapters> { using type = MkChapters; };
template <> struct GetElementTypeById<Element::Id::MkEditionEntry> { using type = MasterElement; };
template <> struct GetElementTypeById<Element::Id::MkEditionUID> { using type = UIntElement; };
template <> struct GetElementTypeById<Element::Id::MkEditionFlagDefault> { using type = UIntElement; };
template <> struct GetElementTypeById<Element::Id::MkEditionFlagOrdered> { using type = UIntElement; };
template <> struct GetElementTypeById<Element::Id::MkChapterAtom> { using type = MasterElement; };
template <> struct GetElementTypeById<Element::Id::MkChapterUID> { using type = UIntElement; };
template <> struct GetElementTypeById<Element::Id::MkChapterTimeStart> { using type = UIntElement; };
template <> struct GetElementTypeById<Element::Id::MkChapterTimeEnd> { using type = UIntElement; };
template <> struct GetElementTypeById<Element::Id::MkChapterFlagHidden> { using type = UIntElement; };
template <> struct GetElementTypeById<Element::Id::MkChapterDisplay> { using type = MasterElement; };
template <> struct GetElementTypeById<Element::Id::MkChapString> { using type = UTF8StringElement; };
template <> struct GetElementTypeById<Element::Id::MkChapLanguage> { using type = Latin1StringElement; };
template <> struct GetElementTypeById<Element::Id::VoidElement> { using type = VoidElement; };
template <Element::Id ID, typename T=typename GetElementTypeById<ID>::type>
const T *element_cast(const std::unique_ptr<Element> &ptr)
{
return static_cast<const T *>(ptr.get());
}
template <Element::Id ID, typename T=typename GetElementTypeById<ID>::type>
std::unique_ptr<T> element_cast(std::unique_ptr<Element> &&ptr)
{
return std::unique_ptr<T>(static_cast<T *>(ptr.release()));
}
template <Element::Id ID, typename T=typename GetElementTypeById<ID>::type>
std::unique_ptr<T> make_unique_element(Element::Id id, int sizeLength, offset_t dataSize, offset_t offset)
{
return std::make_unique<T>(id, sizeLength, dataSize, offset);
}
template <Element::Id ID, typename T=typename GetElementTypeById<ID>::type>
std::unique_ptr<T> make_unique_element()
{
return std::make_unique<T>(ID, 0, 0, 0);
}
}
}
#endif
#endif

View File

@@ -0,0 +1,109 @@
/***************************************************************************
copyright : (C) 2025 by Urs Fleisch
email : ufleisch@users.sourceforge.net
***************************************************************************/
/***************************************************************************
* 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 "ebmlfloatelement.h"
#include "ebmlutils.h"
#include "tbytevector.h"
#include "tfile.h"
#include "tdebug.h"
using namespace TagLib;
EBML::FloatElement::FloatElement(Id id, int sizeLength, offset_t dataSize):
Element(id, sizeLength, dataSize)
{
}
EBML::FloatElement::FloatElement(Id id, int sizeLength, offset_t dataSize, offset_t):
Element(id, sizeLength, dataSize)
{
}
EBML::FloatElement::FloatElement(Id id):
FloatElement(id, 0, 0)
{
}
EBML::FloatElement::FloatVariantType EBML::FloatElement::getValue() const
{ return value; }
double EBML::FloatElement::getValueAsDouble(double defaultValue) const
{
if(std::holds_alternative<double>(value)) {
// get_if() used instead of get() to support restricted compilers
return *std::get_if<double>(&value);
}
if(std::holds_alternative<float>(value)) {
// get_if() used instead of get() to support restricted compilers
return *std::get_if<float>(&value);
}
return defaultValue;
}
void EBML::FloatElement::setValue(FloatVariantType val)
{
value = val;
}
bool EBML::FloatElement::read(File &file)
{
const ByteVector buffer = file.readBlock(dataSize);
if(buffer.size() != dataSize) {
debug("Failed to read EBML Float element");
return false;
}
if(dataSize == 0) {
value = std::monostate();
}
else if(dataSize == 4) {
value = buffer.toFloat32BE(0);
}
else if(dataSize == 8) {
value = buffer.toFloat64BE(0);
}
else {
debug("Invalid size for EBML Float element");
return false;
}
return true;
}
ByteVector EBML::FloatElement::render()
{
ByteVector data;
if(std::holds_alternative<double>(value)) {
// get_if() used instead of get() to support restricted compilers
data = ByteVector::fromFloat64BE(*std::get_if<double>(&value));
}
else if(std::holds_alternative<float>(value)) {
// get_if() used instead of get() to support restricted compilers
data = ByteVector::fromFloat32BE(*std::get_if<float>(&value));
}
ByteVector buffer = renderId();
buffer.append(renderVINT(data.size(), 0));
buffer.append(data);
return buffer;
}

View File

@@ -0,0 +1,59 @@
/***************************************************************************
copyright : (C) 2025 by Urs Fleisch
email : ufleisch@users.sourceforge.net
***************************************************************************/
/***************************************************************************
* 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_EBMLFLOATELEMENT_H
#define TAGLIB_EBMLFLOATELEMENT_H
#ifndef DO_NOT_DOCUMENT
#include <variant>
#include "ebmlelement.h"
namespace TagLib {
class File;
namespace EBML {
class FloatElement : public Element
{
public:
using FloatVariantType = std::variant<std::monostate, float, double>;
FloatElement(Id id, int sizeLength, offset_t dataSize);
FloatElement(Id id, int sizeLength, offset_t dataSize, offset_t);
explicit FloatElement(Id id);
FloatVariantType getValue() const;
double getValueAsDouble(double defaultValue = 0.0) const;
void setValue(FloatVariantType val);
bool read(File &file) override;
ByteVector render() override;
private:
FloatVariantType value;
};
}
}
#endif
#endif

View File

@@ -0,0 +1,127 @@
/***************************************************************************
* 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 "ebmlmasterelement.h"
#include "ebmlvoidelement.h"
#include "ebmlutils.h"
#include "tfile.h"
using namespace TagLib;
EBML::MasterElement::MasterElement(Id id, int sizeLength, offset_t dataSize, offset_t offset):
Element(id, sizeLength, dataSize), offset(offset)
{
}
EBML::MasterElement::MasterElement(Id id):
Element(id, 0, 0), offset(0)
{
}
EBML::MasterElement::~MasterElement() = default;
offset_t EBML::MasterElement::getOffset() const
{
return offset;
}
void EBML::MasterElement::appendElement(std::unique_ptr<Element> &&element)
{
elements.push_back(std::move(element));
}
std::list<std::unique_ptr<EBML::Element>>::iterator EBML::MasterElement::begin()
{
return elements.begin();
}
std::list<std::unique_ptr<EBML::Element>>::iterator EBML::MasterElement::end()
{
return elements.end();
}
std::list<std::unique_ptr<EBML::Element>>::const_iterator EBML::MasterElement::begin() const
{
return elements.begin();
}
std::list<std::unique_ptr<EBML::Element>>::const_iterator EBML::MasterElement::end() const
{
return elements.end();
}
std::list<std::unique_ptr<EBML::Element>>::const_iterator EBML::MasterElement::cbegin() const
{
return elements.cbegin();
}
std::list<std::unique_ptr<EBML::Element>>::const_iterator EBML::MasterElement::cend() const
{
return elements.cend();
}
offset_t EBML::MasterElement::getPadding() const
{
return padding;
}
void EBML::MasterElement::setPadding(offset_t numBytes)
{
padding = numBytes;
}
offset_t EBML::MasterElement::getMinRenderSize() const
{
return minRenderSize;
}
void EBML::MasterElement::setMinRenderSize(offset_t minimumSize)
{
minRenderSize = minimumSize;
}
bool EBML::MasterElement::read(File &file)
{
const offset_t maxOffset = file.tell() + dataSize;
std::unique_ptr<Element> element;
while((element = findNextElement(file, maxOffset))) {
if(!element->read(file))
return false;
elements.push_back(std::move(element));
}
return file.tell() == maxOffset;
}
ByteVector EBML::MasterElement::render()
{
ByteVector buffer = renderId();
ByteVector data;
for(const auto &element : elements)
data.append(element->render());
dataSize = data.size();
buffer.append(renderVINT(dataSize, 0));
buffer.append(data);
if(minRenderSize) {
if(const auto bufferSize = buffer.size();
minRenderSize >= bufferSize + MIN_VOID_ELEMENT_SIZE)
buffer.append(VoidElement::renderSize(minRenderSize - bufferSize));
}
return buffer;
}

View File

@@ -0,0 +1,68 @@
/***************************************************************************
* 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_EBMLMASTERELEMENT_H
#define TAGLIB_EBMLMASTERELEMENT_H
#ifndef DO_NOT_DOCUMENT
#include <list>
#include "ebmlelement.h"
#include "taglib.h"
namespace TagLib
{
class ByteVector;
namespace EBML {
class MasterElement : public Element
{
public:
MasterElement(Id id, int sizeLength, offset_t dataSize, offset_t offset);
explicit MasterElement(Id id);
~MasterElement() override;
offset_t getOffset() const;
bool read(File &file) override;
ByteVector render() override;
void appendElement(std::unique_ptr<Element> &&element);
std::list<std::unique_ptr<Element>>::iterator begin();
std::list<std::unique_ptr<Element>>::iterator end();
std::list<std::unique_ptr<Element>>::const_iterator begin() const;
std::list<std::unique_ptr<Element>>::const_iterator end() const;
std::list<std::unique_ptr<Element>>::const_iterator cbegin() const;
std::list<std::unique_ptr<Element>>::const_iterator cend() const;
offset_t getPadding() const;
void setPadding(offset_t numBytes);
offset_t getMinRenderSize() const;
void setMinRenderSize(offset_t minimumSize);
protected:
offset_t offset;
offset_t padding = 0;
offset_t minRenderSize = 0;
std::list<std::unique_ptr<Element>> elements;
};
}
}
#endif
#endif

Some files were not shown because too many files have changed in this diff Show More