131 Commits

Author SHA1 Message Date
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
312 changed files with 14484 additions and 2498 deletions

View File

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

1
.gitignore vendored
View File

@@ -22,7 +22,6 @@ CMakeLists.txt.user*
/tests/test_runner
/tests/Testing
/taglib/libtag.a
/taglib_config.h
/taglib-config
/bindings/c/libtag_c.a
/bindings/c/taglib_c.pc

View File

@@ -1,8 +1,62 @@
TagLib 2.2 (Feb 18, 2026)
=========================
* Support for Matroska (MKA, MKV) and WebM files.
* Support for NI STEM in MP4 files.
* 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 source-compatible with the
latest 1.x release if no deprecated features are used.
* 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
@@ -74,6 +128,15 @@ TagLib 2.0 (Jan 24, 2024)
- 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 " ".

View File

@@ -1,4 +1,4 @@
cmake_minimum_required(VERSION 3.5.0 FATAL_ERROR)
cmake_minimum_required(VERSION 3.10...3.31)
project(taglib)
@@ -19,9 +19,6 @@ if(APPLE)
set(FRAMEWORK_INSTALL_DIR "/Library/Frameworks" CACHE STRING "Directory to install frameworks to.")
endif()
endif()
if(NOT BUILD_SHARED_LIBS)
add_definitions(-DTAGLIB_STATIC)
endif()
option(ENABLE_STATIC_RUNTIME "Visual Studio, link with runtime statically" OFF)
option(ENABLE_CCACHE "Use ccache when building libtag" OFF)
@@ -48,7 +45,10 @@ 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/")
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")
@@ -92,11 +92,22 @@ endif()
# 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 0)
set(TAGLIB_SOVERSION_MINOR 2)
set(TAGLIB_SOVERSION_PATCH 0)
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)
@@ -148,23 +159,69 @@ if(TRACE_IN_RELEASE)
set(TRACE_IN_RELEASE TRUE)
endif()
configure_file(taglib/taglib_config.h.cmake "${CMAKE_CURRENT_BINARY_DIR}/taglib_config.h")
find_package(utf8cpp QUIET)
if(NOT utf8cpp_FOUND)
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()
else()
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()
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")
add_subdirectory(taglib)
if(BUILD_BINDINGS)

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()

View File

@@ -12,7 +12,6 @@ OUTPUT_DIRECTORY = doc
CREATE_SUBDIRS = NO
ALLOW_UNICODE_NAMES = NO
OUTPUT_LANGUAGE = English
OUTPUT_TEXT_DIRECTION = None
BRIEF_MEMBER_DESC = YES
REPEAT_BRIEF = YES
ABBREVIATE_BRIEF = "The $name class" \
@@ -239,7 +238,6 @@ PDF_HYPERLINKS = YES
USE_PDFLATEX = YES
LATEX_BATCHMODE = NO
LATEX_HIDE_INDICES = NO
LATEX_SOURCE_CODE = NO
LATEX_BIB_STYLE = plain
LATEX_TIMESTAMP = NO
LATEX_EMOJI_DIRECTORY =
@@ -252,7 +250,6 @@ COMPACT_RTF = NO
RTF_HYPERLINKS = NO
RTF_STYLESHEET_FILE =
RTF_EXTENSIONS_FILE =
RTF_SOURCE_CODE = NO
#---------------------------------------------------------------------------
# Configuration options related to the man page output
#---------------------------------------------------------------------------
@@ -273,7 +270,6 @@ XML_NS_MEMB_FILE_SCOPE = NO
#---------------------------------------------------------------------------
GENERATE_DOCBOOK = NO
DOCBOOK_OUTPUT = docbook
DOCBOOK_PROGRAMLISTING = NO
#---------------------------------------------------------------------------
# Configuration options for the AutoGen Definitions output
#---------------------------------------------------------------------------
@@ -309,7 +305,6 @@ EXTERNAL_PAGES = YES
#---------------------------------------------------------------------------
# Configuration options related to the dot tool
#---------------------------------------------------------------------------
CLASS_DIAGRAMS = YES
DIA_PATH =
HIDE_UNDOC_RELATIONS = YES
HAVE_DOT = YES

View File

@@ -27,7 +27,7 @@ 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 |
@@ -42,6 +42,9 @@ CMakeLists.txt file.
| `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`
@@ -49,6 +52,31 @@ 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 (default ON) |
| `WITH_MOD` | Build with Tracker modules (default ON) |
| `WITH_MP4` | Build with MP4 (default ON) |
| `WITH_RIFF` | Build with AIFF, RIFF, WAV (default ON) |
| `WITH_SHORTEN` | Build with Shorten (default ON) |
| `WITH_TRUEAUDIO` | Build with TrueAudio (default ON) |
| `WITH_VORBIS` | Build with Vorbis, FLAC, Ogg, Opus (default ON) |
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
@@ -438,3 +466,54 @@ 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
```

View File

@@ -7,10 +7,10 @@
https://taglib.org/
TagLib is a library for reading and editing the metadata of several
popular audio formats. Currently, it supports both ID3v1 and [ID3v2][]
popular audio formats. Currently, it supports both ID3v1 and ID3v2
for MP3 files, [Ogg Vorbis][] comments and ID3 tags
in [FLAC][], MPC, Speex, WavPack, TrueAudio, WAV, AIFF, MP4, APE,
and ASF files.
in [FLAC][], MPC, Speex, WavPack, TrueAudio, WAV, AIFF, MP4, APE, ASF,
DSF, DFF and AAC files.
TagLib is distributed under the [GNU Lesser General Public License][]
(LGPL) and [Mozilla Public License][] (MPL). Essentially that means that
@@ -18,7 +18,6 @@ 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.
[ID3v2]: https://id3.org/
[Ogg Vorbis]: https://xiph.org/vorbis/
[FLAC]: https://xiph.org/flac/
[GNU Lesser General Public License]: https://www.gnu.org/licenses/lgpl.html

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,31 +1,75 @@
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
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/riff
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/riff/aiff
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/riff/wav
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/ape
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/it
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/mod
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/s3m
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/xm
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/ogg/opus
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/dsf
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/dsdiff
)
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)
@@ -74,6 +118,9 @@ set_target_properties(tag_c PROPERTIES
DEFINE_SYMBOL MAKE_TAGLIB_C_LIB
INSTALL_NAME_DIR ${CMAKE_INSTALL_FULL_LIBDIR}
)
if(NOT BUILD_SHARED_LIBS)
target_compile_definitions(tag_c PUBLIC TAGLIB_STATIC)
endif()
if(TAGLIB_INSTALL_SUFFIX)
if(BUILD_SHARED_LIBS)

View File

@@ -29,35 +29,60 @@
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include "taglib_config.h"
#include "tstringlist.h"
#include "tbytevectorlist.h"
#include "tbytevectorstream.h"
#include "tiostream.h"
#include "tfile.h"
#include "tpropertymap.h"
#include "fileref.h"
#include "asffile.h"
#include "vorbisfile.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"
#include "speexfile.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"
#include "opusfile.h"
#endif
#ifdef TAGLIB_WITH_DSF
#include "dsffile.h"
#include "dsdifffile.h"
#include "tag.h"
#include "id3v2framefactory.h"
#endif
#ifdef TAGLIB_WITH_SHORTEN
#include "shortenfile.h"
#endif
#ifdef TAGLIB_WITH_MATROSKA
#include "matroskafile.h"
#endif
using namespace TagLib;
@@ -127,49 +152,73 @@ TagLib_File *taglib_file_new(const char *filename)
return reinterpret_cast<TagLib_File *>(new FileRef(filename));
}
TagLib_File *taglib_file_new_type(const char *filename, TagLib_File_Type type)
#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_MPC:
file = new MPC::File(filename);
break;
case TagLib_File_OggFlac:
file = new Ogg::FLAC::File(filename);
break;
case TagLib_File_WavPack:
file = new WavPack::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;
case TagLib_File_APE:
file = new APE::File(filename);
break;
#endif
#ifdef TAGLIB_WITH_MOD
case TagLib_File_IT:
file = new IT::File(filename);
break;
@@ -182,21 +231,43 @@ TagLib_File *taglib_file_new_type(const char *filename, TagLib_File_Type type)
case TagLib_File_XM:
file = new XM::File(filename);
break;
case TagLib_File_Opus:
file = new Ogg::Opus::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)
{
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 *>(
@@ -236,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);
@@ -245,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);
@@ -254,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);
@@ -263,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);
@@ -272,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);
@@ -281,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);
}
@@ -406,24 +477,25 @@ void _taglib_property_set(TagLib_File *file, const char* prop, const char* value
return;
auto tfile = reinterpret_cast<FileRef *>(file);
auto propStr = charArrayToString(prop);
PropertyMap map = tfile->tag()->properties();
if(value) {
auto property = map.find(prop);
auto property = map.find(propStr);
if(property == map.end()) {
map.insert(prop, StringList(value));
map.insert(propStr, StringList(charArrayToString(value)));
}
else {
if(append) {
property->second.append(value);
property->second.append(charArrayToString(value));
}
else {
property->second = StringList(value);
property->second = StringList(charArrayToString(value));
}
}
}
else {
map.erase(prop);
map.erase(propStr);
}
tfile->setProperties(map);
@@ -431,17 +503,17 @@ void _taglib_property_set(TagLib_File *file, const char* prop, const char* value
} // namespace
void taglib_property_set(TagLib_File *f, const char *prop, const char *value)
void taglib_property_set(TagLib_File *file, const char *prop, const char *value)
{
_taglib_property_set(f, prop, value, false);
_taglib_property_set(file, prop, value, false);
}
void taglib_property_set_append(TagLib_File *f, const char *prop, const char *value)
void taglib_property_set_append(TagLib_File *file, const char *prop, const char *value)
{
_taglib_property_set(f, prop, value, true);
_taglib_property_set(file, prop, value, true);
}
char** taglib_property_keys(TagLib_File *file)
char** taglib_property_keys(const TagLib_File *file)
{
if(file == NULL)
return NULL;
@@ -461,14 +533,14 @@ char** taglib_property_keys(TagLib_File *file)
return props;
}
char **taglib_property_get(TagLib_File *file, const char *prop)
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(prop);
auto property = map.find(charArrayToString(prop));
if(property == map.end())
return NULL;
@@ -510,18 +582,18 @@ bool _taglib_complex_property_set(
return false;
auto tfile = reinterpret_cast<FileRef *>(file);
auto keyStr = charArrayToString(key);
if(value == NULL) {
return tfile->setComplexProperties(key, {});
return tfile->setComplexProperties(keyStr, {});
}
VariantMap map;
const TagLib_Complex_Property_Attribute** attrPtr = value;
while(*attrPtr) {
const TagLib_Complex_Property_Attribute *attr = *attrPtr;
String attrKey(attr->key);
TagLib_Variant_Type type = attr->value.type;
switch(type) {
auto attrKey = charArrayToString(attr->key);
switch(attr->value.type) {
case TagLib_Variant_Void:
map.insert(attrKey, Variant());
break;
@@ -544,14 +616,14 @@ bool _taglib_complex_property_set(
map.insert(attrKey, attr->value.value.doubleValue);
break;
case TagLib_Variant_String:
map.insert(attrKey, attr->value.value.stringValue);
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(*s++);
strs.append(charArrayToString(*s++));
}
}
map.insert(attrKey, strs);
@@ -565,8 +637,8 @@ bool _taglib_complex_property_set(
++attrPtr;
}
return append ? tfile->setComplexProperties(key, tfile->complexProperties(key).append(map))
: tfile->setComplexProperties(key, {map});
return append ? tfile->setComplexProperties(keyStr, tfile->complexProperties(keyStr).append(map))
: tfile->setComplexProperties(keyStr, {map});
}
} // namespace
@@ -585,7 +657,7 @@ BOOL taglib_complex_property_set_append(
return _taglib_complex_property_set(file, key, value, true);
}
char** taglib_complex_property_keys(TagLib_File *file)
char** taglib_complex_property_keys(const TagLib_File *file)
{
if(file == NULL) {
return NULL;
@@ -608,26 +680,26 @@ char** taglib_complex_property_keys(TagLib_File *file)
}
TagLib_Complex_Property_Attribute*** taglib_complex_property_get(
TagLib_File *file, const char *key)
const TagLib_File *file, const char *key)
{
if(file == NULL || key == NULL) {
return NULL;
}
const auto variantMaps = reinterpret_cast<const FileRef *>(file)->complexProperties(key);
const auto variantMaps = reinterpret_cast<const FileRef *>(file)->complexProperties(charArrayToString(key));
if(variantMaps.isEmpty()) {
return NULL;
}
TagLib_Complex_Property_Attribute ***props = static_cast<TagLib_Complex_Property_Attribute ***>(
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()) {
TagLib_Complex_Property_Attribute **attrs = static_cast<TagLib_Complex_Property_Attribute **>(
auto attrs = static_cast<TagLib_Complex_Property_Attribute **>(
malloc(sizeof(TagLib_Complex_Property_Attribute *) * (variantMap.size() + 1)));
TagLib_Complex_Property_Attribute *attr = static_cast<TagLib_Complex_Property_Attribute *>(
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,
@@ -675,7 +747,7 @@ TagLib_Complex_Property_Attribute*** taglib_complex_property_get(
}
case Variant::StringList: {
attr->value.type = TagLib_Variant_StringList;
StringList strs = v.value<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();
@@ -727,8 +799,7 @@ void taglib_picture_from_complex_property(
TagLib_Complex_Property_Attribute** attrPtr = *propPtr;
while(*attrPtr) {
TagLib_Complex_Property_Attribute *attr = *attrPtr;
TagLib_Variant_Type type = attr->value.type;
switch(type) {
switch(attr->value.type) {
case TagLib_Variant_String:
if(strcmp("mimeType", attr->key) == 0) {
picture->mimeType = attr->value.value.stringValue;
@@ -779,8 +850,7 @@ void taglib_complex_property_free(
TagLib_Complex_Property_Attribute** attrPtr = *propPtr;
while(*attrPtr) {
TagLib_Complex_Property_Attribute *attr = *attrPtr;
TagLib_Variant_Type type = attr->value.type;
switch(type) {
switch(attr->value.type) {
case TagLib_Variant_String:
free(attr->value.value.stringValue);
break;

View File

@@ -43,6 +43,7 @@ extern "C" {
#define TAGLIB_C_EXPORT
#endif
#include <wchar.h>
#ifdef _MSC_VER
/* minwindef.h contains typedef int BOOL */
#include <windows.h>
@@ -130,7 +131,9 @@ typedef enum {
TagLib_File_XM,
TagLib_File_Opus,
TagLib_File_DSF,
TagLib_File_DSDIFF
TagLib_File_DSDIFF,
TagLib_File_SHORTEN,
TagLib_File_MATROSKA
} TagLib_File_Type;
/*!
@@ -141,12 +144,18 @@ 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.
@@ -164,7 +173,7 @@ TAGLIB_C_EXPORT TagLib_File *taglib_file_new_iostream(TagLib_IOStream *stream);
TAGLIB_C_EXPORT void taglib_file_free(TagLib_File *file);
/*!
* Returns true if the file is open and readable 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.
*/
@@ -232,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);
@@ -354,7 +363,7 @@ TAGLIB_C_EXPORT void taglib_property_set_append(TagLib_File *file, const char *p
* \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(TagLib_File *file);
TAGLIB_C_EXPORT char** taglib_property_keys(const TagLib_File *file);
/*!
* Get value(s) of property \a prop.
@@ -362,7 +371,7 @@ TAGLIB_C_EXPORT char** taglib_property_keys(TagLib_File *file);
* \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(TagLib_File *file, const char *prop);
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.
@@ -541,7 +550,7 @@ TAGLIB_C_EXPORT BOOL taglib_complex_property_set_append(
* \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(TagLib_File *file);
TAGLIB_C_EXPORT char** taglib_complex_property_keys(const TagLib_File *file);
/*!
* Get value(s) of complex property \a key.
@@ -551,7 +560,7 @@ TAGLIB_C_EXPORT char** taglib_complex_property_keys(TagLib_File *file);
* It must be freed by the client using taglib_complex_property_free().
*/
TAGLIB_C_EXPORT TagLib_Complex_Property_Attribute*** taglib_complex_property_get(
TagLib_File *file, const char *key);
const TagLib_File *file, const char *key);
/*!
* Extract the complex property values of a picture.

View File

@@ -20,5 +20,6 @@
#cmakedefine TRACE_IN_RELEASE 1
#cmakedefine TESTS_DIR "@TESTS_DIR@"
#cmakedefine TESTS_TMPDIR "@TESTS_TMPDIR@"
#endif

View File

@@ -2,6 +2,7 @@ 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
@@ -38,6 +39,18 @@ 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}

View File

@@ -25,6 +25,7 @@
#include <iostream>
#include <cstdlib>
#include "taglib_config.h"
#include "tbytevector.h"
#include "mpegfile.h"
#include "id3v2tag.h"
@@ -32,9 +33,10 @@
#include "id3v2header.h"
#include "commentsframe.h"
#include "id3v1tag.h"
#ifdef TAGLIB_WITH_APE
#include "apetag.h"
#endif
using namespace std;
using namespace TagLib;
int main(int argc, char *argv[])
@@ -44,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]);
@@ -52,62 +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;
const auto &frames = id3v2tag->frameList();
for(auto it = frames.begin(); it != frames.end(); it++) {
cout << (*it)->frameID();
std::cout << (*it)->frameID();
if(auto comment = dynamic_cast<ID3v2::CommentsFrame *>(*it))
if(!comment->description().isEmpty())
cout << " [" << comment->description() << "]";
std::cout << " [" << comment->description() << "]";
cout << " - \"" << (*it)->toString() << "\"" << endl;
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) {
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;
}
}

124
examples/matroskareader.cpp Normal file
View File

@@ -0,0 +1,124 @@
#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)
);
}
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

@@ -32,13 +32,11 @@
#include "fileref.h"
#include "tag.h"
using namespace std;
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]);
@@ -46,14 +44,14 @@ 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.properties();
if(!tags.isEmpty()) {
@@ -64,10 +62,10 @@ int main(int argc, char *argv[])
}
}
cout << "-- TAG (properties) --" << endl;
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) {
cout << left << std::setfill(' ') << std::setw(longest) << j->first << " - " << '"' << *k << '"' << endl;
std::cout << std::left << std::setfill(' ') << std::setw(longest) << j->first << " - " << '"' << *k << '"' << std::endl;
}
}
}
@@ -76,13 +74,13 @@ int main(int argc, char *argv[])
for(const auto &name : names) {
const auto& properties = f.complexProperties(name);
for(const auto &property : properties) {
cout << name << ":" << endl;
std::cout << name << ":" << std::endl;
for(const auto &[key, value] : property) {
cout << " " << left << std::setfill(' ') << std::setw(11) << key << " - ";
std::cout << " " << std::left << std::setfill(' ') << std::setw(11) << key << " - ";
if(value.type() == TagLib::Variant::ByteVector) {
cout << "(" << value.value<TagLib::ByteVector>().size() << " bytes)" << endl;
std::cout << "(" << value.value<TagLib::ByteVector>().size() << " bytes)" << std::endl;
/* The picture could be extracted using:
ofstream picture;
std::ofstream picture;
TagLib::String fn(argv[i]);
int slashPos = fn.rfind('/');
int dotPos = fn.rfind('.');
@@ -90,13 +88,13 @@ int main(int argc, char *argv[])
fn = fn.substr(slashPos + 1, dotPos - slashPos - 1);
}
fn += ".jpg";
picture.open(fn.toCString(), ios_base::out | ios_base::binary);
picture.open(fn.toCString(), std::ios_base::out | std::ios_base::binary);
picture << value.value<TagLib::ByteVector>();
picture.close();
*/
}
else {
cout << value << endl;
std::cout << value << std::endl;
}
}
}
@@ -110,12 +108,13 @@ int main(int argc, char *argv[])
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) << right << 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,11 +22,11 @@
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "tag_c.h"
#include <stdio.h>
#include <string.h>
#include "tag_c.h"
#ifndef FALSE
#define FALSE 0
#endif

View File

@@ -39,8 +39,6 @@
#include "fileref.h"
#include "tag.h"
using namespace std;
bool isArgument(const char *s)
{
return strlen(s) == 2 && s[0] == '-';
@@ -48,32 +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 << " -p <picturefile> <description> (\"\" \"\" to remove)" << 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);
}
@@ -87,15 +87,111 @@ void checkForRejectedProperties(const TagLib::PropertyMap &tags)
longest = i->first.size();
}
}
cout << "-- rejected TAGs (properties) --" << 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) {
cout << left << std::setw(longest) << i->first << " - " << '"' << *j << '"' << endl;
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;
@@ -171,17 +267,30 @@ int main(int argc, char *argv[])
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())) {
cout << value.toCString() << " not found." << endl;
std::cout << value.toCString() << " not found." << std::endl;
return 1;
}
ifstream picture;
picture.open(value.toCString());
stringstream buffer;
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());

View File

@@ -1,33 +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
${CMAKE_CURRENT_SOURCE_DIR}/dsf
${CMAKE_CURRENT_SOURCE_DIR}/dsdiff
)
if(WITH_ASF)
set(tag_HDR_DIRS ${tag_HDR_DIRS}
${CMAKE_CURRENT_SOURCE_DIR}/asf
)
endif()
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
@@ -83,66 +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
mp4/mp4itemfactory.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
dsf/dsffile.h
dsf/dsfproperties.h
dsdiff/dsdifffile.h
dsdiff/dsdiffproperties.h
dsdiff/dsdiffdiintag.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
@@ -186,128 +306,190 @@ 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
mp4/mp4itemfactory.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()
set(dsf_SRCS
dsf/dsffile.cpp
dsf/dsfproperties.cpp
)
if(WITH_DSF)
set(dsf_SRCS
dsf/dsffile.cpp
dsf/dsfproperties.cpp
)
set(dsdiff_SRCS
dsdiff/dsdifffile.cpp
dsdiff/dsdiffproperties.cpp
dsdiff/dsdiffdiintag.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
@@ -332,7 +514,7 @@ set(tag_LIB_SRCS
${vorbis_SRCS} ${oggflacs_SRCS} ${mpc_SRCS} ${ape_SRCS} ${toolkit_SRCS} ${flacs_SRCS}
${wavpack_SRCS} ${speex_SRCS} ${trueaudio_SRCS} ${riff_SRCS} ${aiff_SRCS} ${wav_SRCS}
${asf_SRCS} ${mp4_SRCS} ${mod_SRCS} ${s3m_SRCS} ${it_SRCS} ${xm_SRCS} ${opus_SRCS}
${dsf_SRCS} ${dsdiff_SRCS}
${dsf_SRCS} ${dsdiff_SRCS} ${shorten_SRCS} ${matroska_SRCS} ${ebml_SRCS}
tag.cpp
tagunion.cpp
fileref.cpp
@@ -340,7 +522,7 @@ 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})
target_include_directories(tag INTERFACE
$<INSTALL_INTERFACE:include>
@@ -348,7 +530,7 @@ target_include_directories(tag INTERFACE
)
target_link_libraries(tag
PRIVATE $<$<TARGET_EXISTS:utf8::cpp>:utf8::cpp>
PRIVATE $<IF:$<TARGET_EXISTS:utf8::cpp>,utf8::cpp,$<$<TARGET_EXISTS:utf8cpp>:utf8cpp>>
$<$<TARGET_EXISTS:ZLIB::ZLIB>:ZLIB::ZLIB>
)
@@ -360,6 +542,9 @@ set_target_properties(tag PROPERTIES
INTERFACE_LINK_LIBRARIES "${ZLIB_INTERFACE_LINK_LIBRARIES}"
PUBLIC_HEADER "${tag_HDRS}"
)
if(NOT BUILD_SHARED_LIBS)
target_compile_definitions(tag PUBLIC TAGLIB_STATIC)
endif()
if(VISIBILITY_HIDDEN)
set_target_properties(tag PROPERTIES C_VISIBILITY_PRESET hidden)
set_target_properties(tag PROPERTIES CXX_VISIBILITY_PRESET hidden)

View File

@@ -75,7 +75,7 @@ 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);
return buffer.find("MAC ") >= 0;
}
////////////////////////////////////////////////////////////////////////////////
@@ -178,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();
}
@@ -224,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;
}
////////////////////////////////////////////////////////////////////////////////
@@ -283,7 +283,7 @@ 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);

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
@@ -155,15 +155,15 @@ namespace TagLib {
/*!
* 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.
*
@@ -174,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.
*

View File

@@ -28,6 +28,8 @@
#include <bitset>
#include "taglib.h"
using namespace TagLib;
using namespace APE;

View File

@@ -59,7 +59,7 @@ namespace TagLib {
/*!
* Destroys the footer.
*/
virtual ~Footer();
~Footer();
Footer(const Footer &) = delete;
Footer &operator=(const Footer &) = delete;
@@ -70,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;
@@ -145,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;

View File

@@ -141,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();
}
@@ -241,7 +241,7 @@ void APE::Item::parse(const ByteVector &data)
ByteVector APE::Item::render() const
{
ByteVector data;
unsigned int flags = ((d->readOnly) ? 1 : 0) | (d->type << 1);
unsigned int flags = (d->readOnly ? 1 : 0) | (d->type << 1);
ByteVector val;
if(isEmpty())

View File

@@ -63,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);
@@ -75,7 +75,7 @@ namespace TagLib {
/*!
* Destroys the item.
*/
virtual ~Item();
~Item();
/*!
* Copies the contents of \a item into this item.
@@ -83,7 +83,7 @@ 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) noexcept;
@@ -172,7 +172,7 @@ 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;
@@ -189,7 +189,7 @@ 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;

View File

@@ -137,9 +137,9 @@ void APE::Properties::read(File *file, offset_t 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);
}
}
@@ -153,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

@@ -97,7 +97,7 @@ namespace TagLib {
unsigned int sampleFrames() const;
/*!
* Returns APE version.
* Returns the APE version.
*/
int version() const;

View File

@@ -47,8 +47,8 @@ using namespace APE;
namespace
{
const unsigned int MinKeyLength = 2;
const unsigned int MaxKeyLength = 255;
constexpr unsigned int MinKeyLength = 2;
constexpr unsigned int MaxKeyLength = 255;
const String FRONT_COVER("COVER ART (FRONT)");
const String BACK_COVER("COVER ART (BACK)");
@@ -203,10 +203,10 @@ PropertyMap APE::Tag::properties() const
{
PropertyMap properties;
for(const auto &[tag, item] : std::as_const(itemListMap())) {
String tagName = tag.upper();
// if the item is Binary or Locator, or if the key is an invalid string,
// add to unsupportedData
if(item.type() != Item::Text || tagName.isEmpty()) {
if(String tagName = tag.upper();
item.type() != Item::Text || tagName.isEmpty()) {
properties.addUnsupportedData(tag);
}
else {
@@ -241,9 +241,9 @@ PropertyMap APE::Tag::setProperties(const PropertyMap &origProps)
// first check if tags need to be removed completely
StringList toRemove;
for(const auto &[k, t] : std::as_const(itemListMap())) {
String key = k.upper();
// only remove if a) key is valid, b) type is text, c) key not contained in new properties
if(!key.isEmpty() && t.type() == APE::Item::Text && !props.contains(key))
if(String key = k.upper();
!key.isEmpty() && t.type() == APE::Item::Text && !props.contains(key))
toRemove.append(k);
}
@@ -255,7 +255,7 @@ PropertyMap APE::Tag::setProperties(const PropertyMap &origProps)
for(const auto &[tagName, val] : std::as_const(props)) {
if(!checkKey(tagName))
invalid.insert(tagName, val);
else if(!(itemListMap().contains(tagName)) || !(itemListMap()[tagName].values() == val)) {
else if(!itemListMap().contains(tagName) || itemListMap()[tagName].values() != val) {
if(val.isEmpty())
removeItem(tagName);
else {
@@ -281,13 +281,12 @@ StringList APE::Tag::complexPropertyKeys() const
List<VariantMap> APE::Tag::complexProperties(const String &key) const
{
List<VariantMap> props;
const String uppercaseKey = key.upper();
if(uppercaseKey == "PICTURE") {
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)) {
Item picture = d->itemListMap.value(itemName);
if(picture.type() == Item::Binary) {
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.
@@ -316,8 +315,7 @@ List<VariantMap> APE::Tag::complexProperties(const String &key) const
bool APE::Tag::setComplexProperties(const String &key, const List<VariantMap> &value)
{
const String uppercaseKey = key.upper();
if(uppercaseKey == "PICTURE") {
if(const String uppercaseKey = key.upper(); uppercaseKey == "PICTURE") {
removeItem(FRONT_COVER);
removeItem(BACK_COVER);

View File

@@ -29,6 +29,7 @@
#include "tbytevector.h"
#include "tmap.h"
#include "tstring.h"
#include "taglib.h"
#include "taglib_export.h"
#include "tag.h"
#include "apeitem.h"
@@ -116,6 +117,7 @@ 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 override;
@@ -165,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);
@@ -184,7 +186,7 @@ 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 override;

View File

@@ -39,7 +39,7 @@ public:
pictureValue(ASF::Picture::fromInvalid())
{
}
AttributeTypes type;
AttributeTypes type { UnicodeType };
String stringValue;
ByteVector byteVectorValue;
ASF::Picture pictureValue;
@@ -154,7 +154,7 @@ unsigned int ASF::Attribute::toUInt() const
unsigned long long ASF::Attribute::toULongLong() const
{
return static_cast<unsigned long long>(d->numericValue);
return d->numericValue;
}
ASF::Picture ASF::Attribute::toPicture() const
@@ -162,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 = static_cast<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 = static_cast<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) {
@@ -194,33 +194,33 @@ String ASF::Attribute::parse(ASF::File &f, int kind)
switch(d->type) {
case WordType:
d->numericValue = readWORD(&f);
d->numericValue = readWORD(&file);
break;
case BoolType:
if(kind == 0) {
d->numericValue = (readDWORD(&f) != 0);
d->numericValue = readDWORD(&file) != 0;
}
else {
d->numericValue = (readWORD(&f) != 0);
d->numericValue = readWORD(&file) != 0;
}
break;
case DWordType:
d->numericValue = readDWORD(&f);
d->numericValue = readDWORD(&file);
break;
case QWordType:
d->numericValue = 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;
}
@@ -240,7 +240,6 @@ int ASF::Attribute::dataSize() const
case WordType:
return 2;
case BoolType:
return 4;
case DWordType:
return 4;
case QWordType:
@@ -303,16 +302,16 @@ ByteVector ASF::Attribute::render(const String &name, int kind) const
if(kind == 0) {
data = renderString(name, true) +
ByteVector::fromShort(static_cast<int>(d->type), false) +
ByteVector::fromShort(data.size(), false) +
ByteVector::fromShort(static_cast<short>(d->type), false) +
ByteVector::fromShort(static_cast<short>(data.size()), false) +
data;
}
else {
ByteVector nameData = renderString(name);
data = ByteVector::fromShort(kind == 2 ? d->language : 0, false) +
ByteVector::fromShort(d->stream, false) +
ByteVector::fromShort(nameData.size(), false) +
ByteVector::fromShort(static_cast<int>(d->type), false) +
data = ByteVector::fromShort(static_cast<short>(kind == 2 ? d->language : 0), false) +
ByteVector::fromShort(static_cast<short>(d->stream), false) +
ByteVector::fromShort(static_cast<short>(nameData.size()), false) +
ByteVector::fromShort(static_cast<short>(d->type), false) +
ByteVector::fromUInt(data.size(), false) +
nameData +
data;

View File

@@ -38,6 +38,8 @@ namespace TagLib
class File;
class Picture;
//! Attribute of ASF (WMA) metadata
class TAGLIB_EXPORT Attribute
{
public:
@@ -114,17 +116,17 @@ namespace TagLib
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) noexcept;
/*!
* Destroys the attribute.
*/
virtual ~Attribute();
~Attribute();
/*!
* Returns type of the value.
* Returns the type of the value.
*/
AttributeTypes type() const;

View File

@@ -221,7 +221,8 @@ void ASF::File::FilePrivate::FilePropertiesObject::parse(ASF::File *file, long l
const long long duration = data.toLongLong(40, false);
const long long preroll = data.toLongLong(56, false);
file->d->properties->setLengthInMilliseconds(static_cast<int>(duration / 10000.0 - preroll + 0.5));
file->d->properties->setLengthInMilliseconds(
static_cast<int>(static_cast<double>(duration) / 10000.0 - static_cast<double>(preroll) + 0.5));
}
ByteVector ASF::File::FilePrivate::StreamPropertiesObject::guid() const
@@ -271,11 +272,11 @@ ByteVector ASF::File::FilePrivate::ContentDescriptionObject::render(ASF::File *f
const ByteVector v4 = renderString(file->d->tag->comment());
const ByteVector v5 = renderString(file->d->tag->rating());
data.clear();
data.append(ByteVector::fromShort(v1.size(), false));
data.append(ByteVector::fromShort(v2.size(), false));
data.append(ByteVector::fromShort(v3.size(), false));
data.append(ByteVector::fromShort(v4.size(), false));
data.append(ByteVector::fromShort(v5.size(), false));
data.append(ByteVector::fromShort(static_cast<short>(v1.size()), false));
data.append(ByteVector::fromShort(static_cast<short>(v2.size()), false));
data.append(ByteVector::fromShort(static_cast<short>(v3.size()), false));
data.append(ByteVector::fromShort(static_cast<short>(v4.size()), false));
data.append(ByteVector::fromShort(static_cast<short>(v5.size()), false));
data.append(v1);
data.append(v2);
data.append(v3);
@@ -302,7 +303,7 @@ void ASF::File::FilePrivate::ExtendedContentDescriptionObject::parse(ASF::File *
ByteVector ASF::File::FilePrivate::ExtendedContentDescriptionObject::render(ASF::File *file)
{
data.clear();
data.append(ByteVector::fromShort(attributeData.size(), false));
data.append(ByteVector::fromShort(static_cast<short>(attributeData.size()), false));
data.append(attributeData.toByteVector(""));
return BaseObject::render(file);
}
@@ -325,7 +326,7 @@ void ASF::File::FilePrivate::MetadataObject::parse(ASF::File *file, long long /*
ByteVector ASF::File::FilePrivate::MetadataObject::render(ASF::File *file)
{
data.clear();
data.append(ByteVector::fromShort(attributeData.size(), false));
data.append(ByteVector::fromShort(static_cast<short>(attributeData.size()), false));
data.append(attributeData.toByteVector(""));
return BaseObject::render(file);
}
@@ -348,7 +349,7 @@ void ASF::File::FilePrivate::MetadataLibraryObject::parse(ASF::File *file, long
ByteVector ASF::File::FilePrivate::MetadataLibraryObject::render(ASF::File *file)
{
data.clear();
data.append(ByteVector::fromShort(attributeData.size(), false));
data.append(ByteVector::fromShort(static_cast<short>(attributeData.size()), false));
data.append(attributeData.toByteVector(""));
return BaseObject::render(file);
}
@@ -472,7 +473,7 @@ 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);
return id == headerGuid;
}
////////////////////////////////////////////////////////////////////////////////
@@ -564,8 +565,8 @@ bool ASF::File::save()
bool inMetadataObject = false;
for(const auto &attribute : attributes) {
const bool largeValue = (attribute.dataSize() > 65535);
const bool guid = (attribute.type() == Attribute::GuidType);
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));
@@ -681,6 +682,5 @@ void ASF::File::read()
if(!filePropertiesObject || !streamPropertiesObject) {
debug("ASF::File::read(): Missing mandatory header objects.");
setValid(false);
return;
}
}

View File

@@ -35,6 +35,8 @@
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
* TagLib::Tag and TagLib::AudioProperties interfaces by way of implementing
@@ -112,7 +114,7 @@ namespace TagLib {
/*!
* Save the file.
*
* This returns true if the save was successful.
* This returns \c true if the save was successful.
*/
bool save() override;

View File

@@ -30,7 +30,6 @@
#include "tbytevector.h"
#include "tpicturetype.h"
#include "taglib_export.h"
#include "attachedpictureframe.h"
namespace TagLib
{
@@ -40,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)
@@ -50,7 +49,7 @@ namespace TagLib
class TAGLIB_EXPORT Picture {
public:
/*!
/*
* This describes the function or content of the picture.
*/
DECLARE_PICTURE_TYPE_ENUM(Type)
@@ -61,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.
@@ -76,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) noexcept;
/*!
* Returns true if Picture stores valid picture
* Returns \c true if Picture stores valid picture
*/
bool isValid() const;
@@ -136,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()

View File

@@ -38,7 +38,7 @@ namespace TagLib {
public:
/*!
* Audio codec types can be used in ASF file.
* Audio codec types which can be used in ASF files.
*/
enum Codec
{

View File

@@ -313,8 +313,7 @@ PropertyMap ASF::Tag::properties() const
}
for(const auto &[k, attributes] : std::as_const(d->attributeListMap)) {
const String key = translateKey(k);
if(!key.isEmpty()) {
if(const String key = translateKey(k); !key.isEmpty()) {
for(const auto &attr : attributes) {
if(key == "TRACKNUMBER") {
if(attr.type() == ASF::Attribute::DWordType)
@@ -411,8 +410,7 @@ StringList ASF::Tag::complexPropertyKeys() const
List<VariantMap> ASF::Tag::complexProperties(const String &key) const
{
List<VariantMap> props;
const String uppercaseKey = key.upper();
if(uppercaseKey == "PICTURE") {
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();
@@ -430,8 +428,7 @@ List<VariantMap> ASF::Tag::complexProperties(const String &key) const
bool ASF::Tag::setComplexProperties(const String &key, const List<VariantMap> &value)
{
const String uppercaseKey = key.upper();
if(uppercaseKey == "PICTURE") {
if(const String uppercaseKey = key.upper(); uppercaseKey == "PICTURE") {
removeItem("WM/Picture");
for(const auto &property : value) {

View File

@@ -39,6 +39,8 @@ namespace TagLib {
using AttributeList = List<Attribute>;
using AttributeListMap = Map<String, AttributeList>;
//! An implementation of ASF (WMA) tags
class TAGLIB_EXPORT Tag : public TagLib::Tag {
friend class File;
@@ -64,7 +66,7 @@ namespace TagLib {
/*!
* Returns the album name; if no album name is present in the tag
* String::null will be returned.
* an empty string will be returned.
*/
String album() const override;
@@ -74,7 +76,7 @@ namespace TagLib {
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.
*/
String genre() const override;
@@ -85,8 +87,8 @@ 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;
@@ -112,7 +114,7 @@ namespace TagLib {
void setArtist(const String &value) override;
/*!
* Sets the album to \a value. If \a value 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.
*/
void setAlbum(const String &value) override;
@@ -148,7 +150,7 @@ namespace TagLib {
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.
*/
@@ -168,7 +170,7 @@ namespace TagLib {
const AttributeListMap &attributeListMap() const;
/*!
* \return True if a value for \a key is currently set.
* \return \c true if a value for \a key is currently set.
*/
bool contains(const String &key) const;

View File

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

View File

@@ -37,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.
*/

View File

@@ -48,7 +48,7 @@ namespace
char padding;
};
typedef std::vector<Chunk64> ChunkList;
using ChunkList = std::vector<Chunk64>;
int chunkIndex(const ChunkList &chunks, const ByteVector &id)
{
@@ -65,12 +65,8 @@ namespace
if(name.size() != 4)
return false;
for(int i = 0; i < 4; i++) {
if(name[i] < 32 || name[i] > 126)
return false;
}
return true;
return std::none_of(name.cbegin(), name.cend(),
[](unsigned char c) { return c < 32 || c > 126; });
}
enum {
@@ -86,7 +82,7 @@ namespace
class DSDIFF::File::FilePrivate
{
public:
FilePrivate(ID3v2::FrameFactory *frameFactory)
FilePrivate(const ID3v2::FrameFactory *frameFactory)
: ID3v2FrameFactory(frameFactory ? frameFactory
: ID3v2::FrameFactory::instance())
{
@@ -130,7 +126,7 @@ 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));
return id.startsWith("FRM8") && id.containsAt("DSD ", 12);
}
////////////////////////////////////////////////////////////////////////////////
@@ -189,9 +185,9 @@ PropertyMap DSDIFF::File::properties() const
return d->tag.properties();
}
void DSDIFF::File::removeUnsupportedProperties(const StringList &unsupported)
void DSDIFF::File::removeUnsupportedProperties(const StringList &properties)
{
d->tag.removeUnsupportedProperties(unsupported);
d->tag.removeUnsupportedProperties(properties);
}
PropertyMap DSDIFF::File::setProperties(const PropertyMap &properties)
@@ -226,9 +222,7 @@ bool DSDIFF::File::save(int tags, StripTags strip, ID3v2::Version version)
// First: save ID3V2 chunk
ID3v2::Tag *id3v2Tag = ID3v2Tag();
if((tags & ID3v2) && id3v2Tag) {
if(const ID3v2::Tag *id3v2Tag = ID3v2Tag(); (tags & ID3v2) && id3v2Tag) {
if(d->isID3InPropChunk) {
if(!id3v2Tag->isEmpty()) {
setChildChunkData(d->id3v2TagChunkID, id3v2Tag->render(version), PROPChunk);
@@ -255,9 +249,7 @@ bool DSDIFF::File::save(int tags, StripTags strip, ID3v2::Version version)
// Second: save the DIIN chunk
DSDIFF::DIIN::Tag *diinTag = DIINTag();
if((tags & DIIN) && diinTag) {
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));
@@ -433,7 +425,7 @@ void DSDIFF::File::removeChildChunk(unsigned int i, unsigned int childChunkNum)
// Update the internal offsets
// For child chunks
if((i + 1) < childChunks.size()) {
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
@@ -527,8 +519,8 @@ void DSDIFF::File::setChildChunkData(const ByteVector &name,
int i = d->childChunkIndex[DIINChunk];
if(i < 0) {
setRootChunkData("DIIN", ByteVector());
const int lastChunkIndex = static_cast<int>(d->chunks.size()) - 1;
if(lastChunkIndex >= 0 && d->chunks[lastChunkIndex].name == "DIIN") {
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;
@@ -559,13 +551,13 @@ void DSDIFF::File::setChildChunkData(const ByteVector &name,
// Now add the chunk to the file
unsigned long long nextRootChunkIdx = length();
if((d->childChunkIndex[childChunkNum] + 1) < static_cast<int>(d->chunks.size()))
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);
offset & 1 ? 1 : 0);
// For root chunks
@@ -575,7 +567,7 @@ void DSDIFF::File::setChildChunkData(const ByteVector &name,
chunk.name = name;
chunk.size = data.size();
chunk.offset = offset + 12;
chunk.padding = (data.size() & 0x01) ? 1 : 0;
chunk.padding = data.size() & 0x01 ? 1 : 0;
childChunks.push_back(chunk);
}
@@ -589,8 +581,8 @@ void DSDIFF::File::updateRootChunksStructure(unsigned int startingChunk)
// Update child chunks structure as well
if(d->childChunkIndex[PROPChunk] >= static_cast<int>(startingChunk)) {
ChunkList &childChunksToUpdate = d->childChunks[PROPChunk];
if(!childChunksToUpdate.empty()) {
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
@@ -599,8 +591,8 @@ void DSDIFF::File::updateRootChunksStructure(unsigned int startingChunk)
}
if(d->childChunkIndex[DIINChunk] >= static_cast<int>(startingChunk)) {
ChunkList &childChunksToUpdate = d->childChunks[DIINChunk];
if(!childChunksToUpdate.empty()) {
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
@@ -647,10 +639,9 @@ void DSDIFF::File::read(bool readProperties, Properties::ReadStyle propertiesSty
// Check padding
chunk.padding = 0;
offset_t uPosNotPadded = tell();
if((uPosNotPadded & 0x01) != 0) {
ByteVector iByte = readBlock(1);
if((iByte.size() != 1) || (iByte[0] != 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
@@ -708,10 +699,9 @@ void DSDIFF::File::read(bool readProperties, Properties::ReadStyle propertiesSty
seek(dstChunkSize, Current);
// Check padding
offset_t uPosNotPadded = tell();
if((uPosNotPadded & 0x01) != 0) {
ByteVector iByte = readBlock(1);
if((iByte.size() != 1) || (iByte[0] != 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);
}
@@ -749,10 +739,9 @@ void DSDIFF::File::read(bool readProperties, Properties::ReadStyle propertiesSty
// Check padding
chunk.padding = 0;
offset_t uPosNotPadded = tell();
if((uPosNotPadded & 0x01) != 0) {
ByteVector iByte = readBlock(1);
if((iByte.size() != 1) || (iByte[0] != 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
@@ -797,11 +786,10 @@ void DSDIFF::File::read(bool readProperties, Properties::ReadStyle propertiesSty
// Check padding
chunk.padding = 0;
offset_t uPosNotPadded = tell();
if((uPosNotPadded & 0x01) != 0) {
ByteVector iByte = readBlock(1);
if((iByte.size() != 1) || (iByte[0] != 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
@@ -867,16 +855,16 @@ void DSDIFF::File::read(bool readProperties, Properties::ReadStyle propertiesSty
for(unsigned int i = 0; i < d->childChunks[DIINChunk].size(); i++) {
if(d->childChunks[DIINChunk][i].name == "DITI") {
seek(d->childChunks[DIINChunk][i].offset);
unsigned int titleStrLength = readBlock(4).toUInt(0, 4, bigEndian);
if(titleStrLength <= d->childChunks[DIINChunk][i].size) {
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);
unsigned int artistStrLength = readBlock(4).toUInt(0, 4, bigEndian);
if(artistStrLength <= d->childChunks[DIINChunk][i].size) {
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);
}
@@ -902,7 +890,7 @@ void DSDIFF::File::read(bool readProperties, Properties::ReadStyle propertiesSty
int bitrate = 0;
if(lengthDSDSamplesTimeChannels > 0)
bitrate = static_cast<int>(
(audioDataSizeinBytes * 8 * sampleRate) / lengthDSDSamplesTimeChannels / 1000);
audioDataSizeinBytes * 8 * sampleRate / lengthDSDSamplesTimeChannels / 1000);
d->properties = std::make_unique<Properties>(sampleRate, channels,
lengthDSDSamplesTimeChannels, bitrate, propertiesStyle);

View File

@@ -26,7 +26,7 @@
#ifndef TAGLIB_DSDIFFFILE_H
#define TAGLIB_DSDIFFFILE_H
#include "rifffile.h"
#include "tfile.h"
#include "id3v2tag.h"
#include "dsdiffproperties.h"
#include "dsdiffdiintag.h"
@@ -36,20 +36,21 @@ namespace TagLib {
//! An implementation of DSDIFF metadata
/*!
* This is 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 http://dsd-guide.com/sites/default/files/white-papers/DSDIFF_1.5_Spec.pdf
* DSDIFF standard does not explicitly specify the ID3V2 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
* 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
//! An implementation of TagLib::File with DSDIFF specific methods.
/*!
* This implements and provides an interface for DSDIFF files to the
@@ -78,7 +79,7 @@ namespace TagLib {
};
/*!
* Constructs a DSDIFF file from \a file. If \a readProperties is true
* 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.
@@ -91,7 +92,7 @@ namespace TagLib {
ID3v2::FrameFactory *frameFactory = nullptr);
/*!
* Constructs an DSDIFF file from \a stream. If \a readProperties is true
* 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
@@ -168,7 +169,7 @@ namespace TagLib {
/*!
* Save the file. If at least one tag -- ID3v1 or DIIN -- exists this
* will duplicate its content into the other tag. This returns true
* 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
@@ -177,7 +178,7 @@ namespace TagLib {
* 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 paramaterized save call below.
* with the concession of generality, use parameterized save call below.
*
* \see save(int tags)
*/
@@ -193,7 +194,7 @@ namespace TagLib {
/*!
* This will strip the tags that match the OR-ed together TagTypes from the
* file. By default it strips all tags. It returns true if the tags are
* file. By default it strips all tags. It returns \c true if the tags are
* successfully stripped.
*
* \note This will update the file immediately.
@@ -227,9 +228,6 @@ namespace TagLib {
protected:
enum Endianness { BigEndian, LittleEndian };
File(FileName file, Endianness endianness);
File(IOStream *stream, Endianness endianness);
private:
void removeRootChunk(const ByteVector &id);
void removeRootChunk(unsigned int i);
@@ -237,7 +235,7 @@ namespace TagLib {
void removeChildChunk(unsigned int i, unsigned int childChunkNum);
/*!
* Sets the data for the the specified chunk at root level to \a data.
* Sets the data for the specified chunk at root level to \a data.
*
* \warning This will update the file immediately.
*/
@@ -254,7 +252,7 @@ namespace TagLib {
void setRootChunkData(const ByteVector &name, const ByteVector &data);
/*!
* Sets the data for the the specified child chunk to \a data.
* Sets the data for the specified child chunk to \a data.
*
* If data is null, then remove the chunk
*
@@ -266,7 +264,7 @@ namespace TagLib {
/*!
* 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 child chunk.
* be created after the existing chunks inside the child chunk.
*
* If data is null, then remove the chunks with \a name name
*

View File

@@ -26,7 +26,6 @@
#include "dsdiffproperties.h"
#include "tstring.h"
#include "tdebug.h"
using namespace TagLib;
@@ -45,10 +44,10 @@ public:
// public members
////////////////////////////////////////////////////////////////////////////////
DSDIFF::Properties::Properties(const unsigned int sampleRate,
const unsigned short channels,
const unsigned long long samplesCount,
const int bitrate,
DSDIFF::Properties::Properties(unsigned int sampleRate,
unsigned short channels,
unsigned long long samplesCount,
int bitrate,
ReadStyle style) :
AudioProperties(style),
d(std::make_unique<PropertiesPrivate>())
@@ -59,7 +58,7 @@ DSDIFF::Properties::Properties(const unsigned int sampleRate,
d->sampleRate = sampleRate;
d->bitrate = bitrate;
d->length = d->sampleRate > 0
? static_cast<int>((d->sampleCount * 1000.0) / d->sampleRate + 0.5)
? static_cast<int>(static_cast<double>(d->sampleCount) * 1000.0 / d->sampleRate + 0.5)
: 0;
}

View File

@@ -37,7 +37,7 @@ namespace TagLib {
//! An implementation of audio property reading for DSDIFF
/*!
* This reads the data from an DSDIFF stream found in the AudioProperties
* This reads the data from a DSDIFF stream found in the AudioProperties
* API.
*/
@@ -48,8 +48,8 @@ namespace TagLib {
* Create an instance of DSDIFF::Properties with the data read from the
* ByteVector \a data.
*/
Properties(const unsigned int sampleRate, const unsigned short channels,
const unsigned long long samplesCount, const int bitrate,
Properties(unsigned int sampleRate, unsigned short channels,
unsigned long long samplesCount, int bitrate,
ReadStyle style);
/*!

View File

@@ -31,12 +31,10 @@
using namespace TagLib;
// The DSF specification is located at http://dsd-guide.com/sites/default/files/white-papers/DSFFileFormatSpec_E.pdf
class DSF::File::FilePrivate
{
public:
FilePrivate(ID3v2::FrameFactory *frameFactory)
FilePrivate(const ID3v2::FrameFactory *frameFactory)
: ID3v2FrameFactory(frameFactory ? frameFactory
: ID3v2::FrameFactory::instance())
{
@@ -61,7 +59,7 @@ bool DSF::File::isSupported(IOStream *stream)
return id.startsWith("DSD ");
}
DSF::File::File(FileName file, bool readProperties,
DSF::File::File(FileName file, bool,
AudioProperties::ReadStyle propertiesStyle,
ID3v2::FrameFactory *frameFactory) :
TagLib::File(file),
@@ -71,7 +69,7 @@ DSF::File::File(FileName file, bool readProperties,
read(propertiesStyle);
}
DSF::File::File(IOStream *stream, bool readProperties,
DSF::File::File(IOStream *stream, bool,
AudioProperties::ReadStyle propertiesStyle,
ID3v2::FrameFactory *frameFactory) :
TagLib::File(stream),

View File

@@ -36,7 +36,27 @@
#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:
/*!
@@ -103,7 +123,7 @@ namespace TagLib {
/*!
* Save the file.
*
* This returns true if the save was successful.
* This returns \c true if the save was successful.
*/
bool save() override;

View File

@@ -127,8 +127,8 @@ void DSF::Properties::read(const ByteVector &data)
d->blockSizePerChannel = data.toUInt(32U,false);
d->bitrate = static_cast<unsigned int>(
(d->samplingFrequency * d->bitsPerSample * d->channelNum) / 1000.0 + 0.5);
d->samplingFrequency * d->bitsPerSample * d->channelNum / 1000.0 + 0.5);
d->length = d->samplingFrequency > 0
? static_cast<unsigned int>(d->sampleCount * 1000.0 / d->samplingFrequency + 0.5)
? static_cast<unsigned int>(static_cast<double>(d->sampleCount) * 1000.0 / d->samplingFrequency + 0.5)
: 0;
}

View File

@@ -34,6 +34,7 @@
namespace TagLib {
namespace DSF {
//! An implementation of audio properties for DSF
class TAGLIB_EXPORT Properties : public AudioProperties {
public:
Properties(const ByteVector &data, ReadStyle style);

View File

@@ -32,31 +32,54 @@
#include <cstring>
#include <utility>
#include "taglib_config.h"
#include "tfilestream.h"
#include "tpropertymap.h"
#include "tstringlist.h"
#include "tvariant.h"
#include "tdebug.h"
#include "aifffile.h"
#include "apefile.h"
#include "asffile.h"
#include "flacfile.h"
#include "itfile.h"
#include "modfile.h"
#include "mp4file.h"
#include "mpcfile.h"
#include "mpegfile.h"
#ifdef TAGLIB_WITH_RIFF
#include "aifffile.h"
#include "wavfile.h"
#endif
#ifdef TAGLIB_WITH_APE
#include "apefile.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"
#include "s3mfile.h"
#include "speexfile.h"
#include "trueaudiofile.h"
#include "vorbisfile.h"
#include "wavfile.h"
#include "wavpackfile.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;
@@ -119,8 +142,7 @@ namespace
#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
@@ -136,6 +158,7 @@ namespace
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") {
@@ -148,26 +171,38 @@ namespace
}
else if(ext == "FLAC")
file = new FLAC::File(stream, readAudioProperties, audioPropertiesStyle);
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 == "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);
else if(ext == "APE")
file = new APE::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);
@@ -177,10 +212,21 @@ namespace
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.
@@ -202,36 +248,58 @@ namespace
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(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(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);
else if(APE::File::isSupported(stream))
file = new APE::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.
@@ -264,7 +332,7 @@ public:
return !file || !file->isValid();
}
bool isNullWithDebugMessage(const String &methodName) const
bool isNullWithDebugMessage([[maybe_unused]] const String &methodName) const
{
if(isNull()) {
debug("FileRef::" + methodName + "() - Called without a valid file.");
@@ -401,17 +469,25 @@ 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("opus");
l.append("mp3");
l.append("mp2");
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");
l.append("aac");
#endif
#ifdef TAGLIB_WITH_MP4
l.append("m4a");
l.append("m4r");
l.append("m4b");
@@ -419,14 +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"
@@ -434,9 +515,20 @@ 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;
}
@@ -457,12 +549,12 @@ void FileRef::swap(FileRef &ref) noexcept
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;
}
////////////////////////////////////////////////////////////////////////////////

View File

@@ -60,34 +60,12 @@ namespace TagLib {
{
public:
//! A class for pluggable file type resolution.
//! A class for pluggable file 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 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
{
@@ -120,6 +98,44 @@ namespace TagLib {
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:
@@ -127,11 +143,22 @@ namespace TagLib {
/*!
* Destroys this StreamTypeResolver instance.
*/
~StreamTypeResolver() override = 0;
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
@@ -148,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
@@ -163,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
@@ -195,7 +222,7 @@ namespace TagLib {
~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.
@@ -287,7 +314,7 @@ namespace TagLib {
/*!
* 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;
@@ -309,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();
@@ -351,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;
@@ -361,17 +388,17 @@ 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) 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;

View File

@@ -45,10 +45,10 @@ namespace
{
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
@@ -86,7 +86,7 @@ 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);
return buffer.find("fLaC") >= 0;
}
////////////////////////////////////////////////////////////////////////////////
@@ -171,8 +171,7 @@ StringList FLAC::File::complexPropertyKeys() const
List<VariantMap> FLAC::File::complexProperties(const String &key) const
{
const String uppercaseKey = key.upper();
if(uppercaseKey == "PICTURE") {
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)) {
@@ -196,8 +195,7 @@ List<VariantMap> FLAC::File::complexProperties(const String &key) const
bool FLAC::File::setComplexProperties(const String &key, const List<VariantMap> &value)
{
const String uppercaseKey = key.upper();
if(uppercaseKey == "PICTURE") {
if(const String uppercaseKey = key.upper(); uppercaseKey == "PICTURE") {
removePictures();
for(const auto &property : value) {
@@ -267,12 +265,19 @@ bool FLAC::File::save()
// Render data for the metadata blocks
ByteVector data;
for(const auto &block : std::as_const(d->blocks)) {
ByteVector blockData = block->render();
for(auto it = d->blocks.begin(); it != d->blocks.end();) {
ByteVector blockData = (*it)->render();
ByteVector blockHeader = ByteVector::fromUInt(blockData.size());
blockHeader[0] = block->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.
@@ -303,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
@@ -320,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();
}
@@ -449,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;
}
////////////////////////////////////////////////////////////////////////////////

View File

@@ -43,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.
*
@@ -83,7 +83,7 @@ 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.
@@ -96,13 +96,15 @@ namespace TagLib {
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 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(FileName file, ID3v2::FrameFactory *frameFactory,
@@ -110,7 +112,7 @@ namespace TagLib {
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
@@ -126,7 +128,7 @@ namespace TagLib {
ID3v2::FrameFactory *frameFactory = nullptr);
/*!
* 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
@@ -136,6 +138,8 @@ namespace TagLib {
* \a frameFactory.
*
* \note In the current implementation, \a propertiesStyle is ignored.
*
* \deprecated Use the constructor above.
*/
TAGLIB_DEPRECATED
File(IOStream *stream, ID3v2::FrameFactory *frameFactory,
@@ -207,22 +211,22 @@ namespace TagLib {
* 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.
*/
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.
*
@@ -233,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.
*
@@ -252,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
@@ -274,8 +278,8 @@ namespace TagLib {
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);

View File

@@ -26,12 +26,12 @@
#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:

View File

@@ -35,11 +35,12 @@
namespace TagLib {
namespace FLAC {
//! FLAC picture
class TAGLIB_EXPORT Picture : public MetadataBlock
{
public:
/*!
/*
* This describes the function or content of the picture.
*/
DECLARE_PICTURE_TYPE_ENUM(Type)
@@ -71,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.
@@ -80,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.
@@ -93,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.
@@ -103,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.
@@ -113,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..
@@ -148,7 +149,7 @@ namespace TagLib {
/*!
* Parse the picture data in the FLAC picture block format.
*/
bool parse(const ByteVector &rawData);
bool parse(const ByteVector &data);
private:
class PicturePrivate;

View File

@@ -28,8 +28,6 @@
#include "tstring.h"
#include "tdebug.h"
#include "flacfile.h"
using namespace TagLib;
class FLAC::Properties::PropertiesPrivate
@@ -134,9 +132,9 @@ void FLAC::Properties::read(const ByteVector &data, offset_t 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

@@ -37,7 +37,7 @@ namespace TagLib {
//! 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.
*/

View File

@@ -33,6 +33,7 @@
namespace TagLib {
namespace FLAC {
//! Unknown FLAC metadata block
class TAGLIB_EXPORT UnknownMetadataBlock : public MetadataBlock
{
public:

View File

@@ -27,7 +27,6 @@
#include "tstringlist.h"
#include "tdebug.h"
#include "tpropertymap.h"
#include "modfileprivate.h"
using namespace TagLib;
@@ -168,7 +167,7 @@ bool IT::File::save()
if(messageOffset + messageLength >= fileSize) {
// append new message
seek(54);
writeU16L(message.size());
writeU16L(static_cast<unsigned short>(message.size()));
writeU32L(messageOffset);
seek(messageOffset);
writeBlock(message);

View File

@@ -31,12 +31,27 @@
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

View File

@@ -26,11 +26,11 @@
#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 {
public:
/*! Flag bits. */

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,213 @@
/***************************************************************************
* 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::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,249 @@
/***************************************************************************
* 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,
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::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

View File

@@ -0,0 +1,81 @@
/***************************************************************************
* 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 "ebmlmkattachments.h"
#include "ebmlstringelement.h"
#include "ebmluintelement.h"
#include "ebmlbinaryelement.h"
#include "matroskaattachments.h"
#include "matroskaattachedfile.h"
using namespace TagLib;
EBML::MkAttachments::MkAttachments(int sizeLength, offset_t dataSize, offset_t offset):
MasterElement(Id::MkAttachments, sizeLength, dataSize, offset)
{
}
EBML::MkAttachments::MkAttachments(Id, int sizeLength, offset_t dataSize, offset_t offset):
MasterElement(Id::MkAttachments, sizeLength, dataSize, offset)
{
}
EBML::MkAttachments::MkAttachments():
MasterElement(Id::MkAttachments, 0, 0, 0)
{
}
std::unique_ptr<Matroska::Attachments> EBML::MkAttachments::parse() const
{
auto attachments = std::make_unique<Matroska::Attachments>();
attachments->setOffset(offset);
attachments->setSize(getSize());
for(const auto &element : elements) {
if(element->getId() != Id::MkAttachedFile)
continue;
const String *filename = nullptr;
const String *description = nullptr;
const String *mediaType = nullptr;
const ByteVector *data = nullptr;
Matroska::AttachedFile::UID uid = 0;
const auto attachedFile = element_cast<Id::MkAttachedFile>(element);
for(const auto &attachedFileChild : *attachedFile) {
if(const Id id = attachedFileChild->getId(); id == Id::MkAttachedFileName)
filename = &element_cast<Id::MkAttachedFileName>(attachedFileChild)->getValue();
else if(id == Id::MkAttachedFileData)
data = &element_cast<Id::MkAttachedFileData>(attachedFileChild)->getValue();
else if(id == Id::MkAttachedFileDescription)
description = &element_cast<Id::MkAttachedFileDescription>(attachedFileChild)->getValue();
else if(id == Id::MkAttachedFileMediaType)
mediaType = &element_cast<Id::MkAttachedFileMediaType>(attachedFileChild)->getValue();
else if(id == Id::MkAttachedFileUID)
uid = element_cast<Id::MkAttachedFileUID>(attachedFileChild)->getValue();
}
if(!(filename && data))
continue;
attachments->addAttachedFile(Matroska::AttachedFile(
*data, *filename, mediaType ? *mediaType : String(),
uid, description ? *description : String()));
}
return attachments;
}

View File

@@ -0,0 +1,47 @@
/***************************************************************************
* 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_EBMLMKATTACHMENTS_H
#define TAGLIB_EBMLMKATTACHMENTS_H
#ifndef DO_NOT_DOCUMENT
#include "ebmlmasterelement.h"
#include "taglib.h"
namespace TagLib {
namespace Matroska {
class Attachments;
}
namespace EBML {
class MkAttachments : public MasterElement
{
public:
MkAttachments(int sizeLength, offset_t dataSize, offset_t offset);
MkAttachments(Id, int sizeLength, offset_t dataSize, offset_t offset);
MkAttachments();
std::unique_ptr<Matroska::Attachments> parse() const;
};
}
}
#endif
#endif

View File

@@ -0,0 +1,114 @@
/***************************************************************************
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 "ebmlmkchapters.h"
#include "ebmlstringelement.h"
#include "ebmluintelement.h"
#include "matroskachapters.h"
#include "matroskachapteredition.h"
using namespace TagLib;
EBML::MkChapters::MkChapters(int sizeLength, offset_t dataSize, offset_t offset):
MasterElement(Id::MkChapters, sizeLength, dataSize, offset)
{
}
EBML::MkChapters::MkChapters(Id, int sizeLength, offset_t dataSize, offset_t offset):
MasterElement(Id::MkChapters, sizeLength, dataSize, offset)
{
}
EBML::MkChapters::MkChapters():
MasterElement(Id::MkChapters, 0, 0, 0)
{
}
std::unique_ptr<Matroska::Chapters> EBML::MkChapters::parse() const
{
auto chapters = std::make_unique<Matroska::Chapters>();
chapters->setOffset(offset);
chapters->setSize(getSize());
for(const auto &element : elements) {
if(element->getId() != Id::MkEditionEntry)
continue;
List<Matroska::Chapter> editionChapters;
Matroska::ChapterEdition::UID editionUid = 0;
bool editionIsDefault = false;
bool editionIsOrdered = false;
const auto edition = element_cast<Id::MkEditionEntry>(element);
for(const auto &editionChild : *edition) {
if(const Id id = editionChild->getId(); id == Id::MkEditionUID)
editionUid = element_cast<Id::MkEditionUID>(editionChild)->getValue();
else if(id == Id::MkEditionFlagDefault)
editionIsDefault = element_cast<Id::MkEditionFlagDefault>(editionChild)->getValue() != 0;
else if(id == Id::MkEditionFlagOrdered)
editionIsOrdered = element_cast<Id::MkEditionFlagOrdered>(editionChild)->getValue() != 0;
else if(id == Id::MkChapterAtom) {
Matroska::Chapter::UID chapterUid = 0;
Matroska::Chapter::Time chapterTimeStart = 0;
Matroska::Chapter::Time chapterTimeEnd = 0;
List<Matroska::Chapter::Display> chapterDisplays;
bool chapterHidden = false;
const auto chapterAtom = element_cast<Id::MkChapterAtom>(editionChild);
for(const auto &chapterChild : *chapterAtom) {
if(const Id cid = chapterChild->getId(); cid == Id::MkChapterUID)
chapterUid = element_cast<Id::MkChapterUID>(chapterChild)->getValue();
else if(cid == Id::MkChapterTimeStart)
chapterTimeStart = element_cast<Id::MkChapterTimeStart>(chapterChild)->getValue();
else if(cid == Id::MkChapterTimeEnd)
chapterTimeEnd = element_cast<Id::MkChapterTimeEnd>(chapterChild)->getValue();
else if(cid == Id::MkChapterFlagHidden)
chapterHidden = element_cast<Id::MkChapterFlagHidden>(chapterChild)->getValue() != 0;
else if(cid == Id::MkChapterDisplay) {
const auto display = element_cast<Id::MkChapterDisplay>(chapterChild);
String displayString;
String displayLanguage;
for(const auto &displayChild : *display) {
if(const Id did = displayChild->getId(); did == Id::MkChapString)
displayString = element_cast<Id::MkChapString>(displayChild)->getValue();
else if(did == Id::MkChapLanguage)
displayLanguage = element_cast<Id::MkChapLanguage>(displayChild)->getValue();
}
if(!displayString.isEmpty()) {
chapterDisplays.append(Matroska::Chapter::Display(displayString, displayLanguage));
}
}
}
if(chapterUid) {
editionChapters.append(Matroska::Chapter(
chapterTimeStart, chapterTimeEnd, chapterDisplays, chapterUid, chapterHidden));
}
}
}
if(!editionChapters.isEmpty()) {
chapters->addChapterEdition(Matroska::ChapterEdition(
editionChapters, editionIsDefault, editionIsOrdered, editionUid));
}
}
return chapters;
}

View File

@@ -0,0 +1,52 @@
/***************************************************************************
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_EBMLMKCHAPTERS_H
#define TAGLIB_EBMLMKCHAPTERS_H
#ifndef DO_NOT_DOCUMENT
#include "ebmlmasterelement.h"
#include "taglib.h"
namespace TagLib {
namespace Matroska {
class Chapters;
}
namespace EBML {
class MkChapters : public MasterElement
{
public:
MkChapters(int sizeLength, offset_t dataSize, offset_t offset);
MkChapters(Id, int sizeLength, offset_t dataSize, offset_t offset);
MkChapters();
std::unique_ptr<Matroska::Chapters> parse() const;
};
}
}
#endif
#endif

View File

@@ -0,0 +1,89 @@
/***************************************************************************
* 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 "ebmlmkcues.h"
#include "ebmluintelement.h"
#include "matroskacues.h"
using namespace TagLib;
EBML::MkCues::MkCues(int sizeLength, offset_t dataSize, offset_t offset):
MasterElement(Id::MkCues, sizeLength, dataSize, offset)
{
}
EBML::MkCues::MkCues(Id, int sizeLength, offset_t dataSize, offset_t offset):
MasterElement(Id::MkCues, sizeLength, dataSize, offset)
{
}
EBML::MkCues::MkCues():
MasterElement(Id::MkCues, 0, 0, 0)
{
}
std::unique_ptr<Matroska::Cues> EBML::MkCues::parse(offset_t segmentDataOffset) const
{
auto cues = std::make_unique<Matroska::Cues>(segmentDataOffset);
cues->setOffset(offset);
cues->setSize(getSize());
cues->setID(static_cast<Matroska::Element::ID>(id));
for(const auto &cuesChild : elements) {
if(cuesChild->getId() != Id::MkCuePoint)
continue;
const auto cuePointElement = element_cast<Id::MkCuePoint>(cuesChild);
auto cuePoint = std::make_unique<Matroska::CuePoint>();
for(const auto &cuePointChild : *cuePointElement) {
if(const Id id = cuePointChild->getId(); id == Id::MkCueTime)
cuePoint->setTime(element_cast<Id::MkCueTime>(cuePointChild)->getValue());
else if(id == Id::MkCueTrackPositions) {
auto cueTrack = std::make_unique<Matroska::CueTrack>();
const auto cueTrackElement = element_cast<Id::MkCueTrackPositions>(cuePointChild);
for(const auto &cueTrackChild : *cueTrackElement) {
if(const Id trackId = cueTrackChild->getId(); trackId == Id::MkCueTrack)
cueTrack->setTrackNumber(element_cast<Id::MkCueTrack>(cueTrackChild)->getValue());
else if(trackId == Id::MkCueClusterPosition)
cueTrack->setClusterPosition(element_cast<Id::MkCueClusterPosition>(cueTrackChild)->getValue());
else if(trackId == Id::MkCueRelativePosition)
cueTrack->setRelativePosition(element_cast<Id::MkCueRelativePosition>(cueTrackChild)->getValue());
else if(trackId == Id::MkCueDuration)
cueTrack->setDuration(element_cast<Id::MkCueDuration>(cueTrackChild)->getValue());
else if(trackId == Id::MkCueBlockNumber)
cueTrack->setBlockNumber(element_cast<Id::MkCueBlockNumber>(cueTrackChild)->getValue());
else if(trackId == Id::MkCueCodecState)
cueTrack->setCodecState(element_cast<Id::MkCueCodecState>(cueTrackChild)->getValue());
else if(trackId == Id::MkCueReference) {
const auto cueReference = element_cast<Id::MkCueReference>(cueTrackChild);
for(const auto &cueReferenceChild : *cueReference) {
if(cueReferenceChild->getId() != Id::MkCueRefTime)
continue;
cueTrack->addReferenceTime(element_cast<Id::MkCueRefTime>(cueReferenceChild)->getValue());
}
}
}
cuePoint->addCueTrack(std::move(cueTrack));
}
}
cues->addCuePoint(std::move(cuePoint));
}
return cues;
}

View File

@@ -0,0 +1,47 @@
/***************************************************************************
* 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_EBMLMKCUES_H
#define TAGLIB_EBMLMKCUES_H
#ifndef DO_NOT_DOCUMENT
#include "ebmlmasterelement.h"
#include "taglib.h"
namespace TagLib {
namespace Matroska {
class Cues;
}
namespace EBML {
class MkCues : public MasterElement
{
public:
MkCues(int sizeLength, offset_t dataSize, offset_t offset);
MkCues(Id, int sizeLength, offset_t dataSize, offset_t offset);
MkCues();
std::unique_ptr<Matroska::Cues> parse(offset_t segmentDataOffset) const;
};
}
}
#endif
#endif

View File

@@ -0,0 +1,72 @@
/***************************************************************************
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 "ebmlmkinfo.h"
#include "ebmlstringelement.h"
#include "ebmluintelement.h"
#include "ebmlfloatelement.h"
#include "matroskaproperties.h"
using namespace TagLib;
EBML::MkInfo::MkInfo(int sizeLength, offset_t dataSize, offset_t offset):
MasterElement(Id::MkInfo, sizeLength, dataSize, offset)
{
}
EBML::MkInfo::MkInfo(Id, int sizeLength, offset_t dataSize, offset_t offset):
MasterElement(Id::MkInfo, sizeLength, dataSize, offset)
{
}
EBML::MkInfo::MkInfo():
MasterElement(Id::MkInfo, 0, 0, 0)
{
}
void EBML::MkInfo::parse(Matroska::Properties *properties) const
{
if(!properties)
return;
unsigned long long timestampScale = 1000000;
double duration = 0.0;
String title;
for(const auto &element : elements) {
if(const Id id = element->getId(); id == Id::MkTimestampScale) {
timestampScale = element_cast<Id::MkTimestampScale>(element)->getValue();
}
else if(id == Id::MkDuration) {
duration = element_cast<Id::MkDuration>(element)->getValueAsDouble();
}
else if(id == Id::MkTitle) {
title = element_cast<Id::MkTitle>(element)->getValue();
}
}
properties->setLengthInMilliseconds(
static_cast<int>(duration * static_cast<double>(timestampScale) / 1000000.0));
properties->setTitle(title);
}

View File

@@ -0,0 +1,52 @@
/***************************************************************************
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_EBMLMKINFO_H
#define TAGLIB_EBMLMKINFO_H
#ifndef DO_NOT_DOCUMENT
#include "ebmlmasterelement.h"
#include "taglib.h"
namespace TagLib {
namespace Matroska {
class Properties;
}
namespace EBML {
class MkInfo : public MasterElement
{
public:
MkInfo(int sizeLength, offset_t dataSize, offset_t offset);
MkInfo(Id, int sizeLength, offset_t dataSize, offset_t offset);
MkInfo();
void parse(Matroska::Properties * properties) const;
};
}
}
#endif
#endif

View File

@@ -0,0 +1,73 @@
/***************************************************************************
* 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 "ebmlmkseekhead.h"
#include "matroskaseekhead.h"
#include "ebmluintelement.h"
#include "ebmlbinaryelement.h"
using namespace TagLib;
EBML::MkSeekHead::MkSeekHead(int sizeLength, offset_t dataSize, offset_t offset):
MasterElement(Id::MkSeekHead, sizeLength, dataSize, offset)
{
}
EBML::MkSeekHead::MkSeekHead(Id, int sizeLength, offset_t dataSize, offset_t offset):
MasterElement(Id::MkSeekHead, sizeLength, dataSize, offset)
{
}
EBML::MkSeekHead::MkSeekHead():
MasterElement(Id::MkSeekHead, 0, 0, 0)
{
}
std::unique_ptr<Matroska::SeekHead> EBML::MkSeekHead::parse(offset_t segmentDataOffset) const
{
auto seekHead = std::make_unique<Matroska::SeekHead>(segmentDataOffset);
seekHead->setOffset(offset);
seekHead->setSize(getSize() + padding);
for(const auto &element : elements) {
if(element->getId() != Id::MkSeek)
continue;
const auto seekElement = element_cast<Id::MkSeek>(element);
Matroska::Element::ID entryId = 0;
offset_t offset = 0;
for(const auto &seekElementChild : *seekElement) {
if(const Id id = seekElementChild->getId(); id == Id::MkSeekID && !entryId) {
if(auto data = element_cast<Id::MkSeekID>(seekElementChild)->getValue();
data.size() == 4)
entryId = data.toUInt(true);
}
else if(id == Id::MkSeekPosition && !offset)
offset = element_cast<Id::MkSeekPosition>(seekElementChild)->getValue();
}
if(entryId && offset)
seekHead->addEntry(entryId, offset);
else {
seekHead.reset();
return nullptr;
}
}
return seekHead;
}

View File

@@ -0,0 +1,47 @@
/***************************************************************************
* 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_EBMLMKSEEKHEAD_H
#define TAGLIB_EBMLMKSEEKHEAD_H
#ifndef DO_NOT_DOCUMENT
#include "ebmlmasterelement.h"
#include "taglib.h"
namespace TagLib {
namespace Matroska {
class SeekHead;
}
namespace EBML {
class MkSeekHead : public MasterElement
{
public:
MkSeekHead(int sizeLength, offset_t dataSize, offset_t offset);
MkSeekHead(Id, int sizeLength, offset_t dataSize, offset_t offset);
MkSeekHead();
std::unique_ptr<Matroska::SeekHead> parse(offset_t segmentDataOffset) const;
};
}
}
#endif
#endif

View File

@@ -0,0 +1,148 @@
/***************************************************************************
* 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 "ebmlmksegment.h"
#include "ebmlutils.h"
#include "matroskafile.h"
#include "matroskatag.h"
#include "matroskaattachments.h"
#include "matroskachapters.h"
#include "matroskacues.h"
#include "matroskaseekhead.h"
#include "matroskasegment.h"
using namespace TagLib;
EBML::MkSegment::MkSegment(int sizeLength, offset_t dataSize, offset_t offset):
MasterElement(Id::MkSegment, sizeLength, dataSize, offset)
{
}
EBML::MkSegment::MkSegment(Id, int sizeLength, offset_t dataSize, offset_t offset):
MasterElement(Id::MkSegment, sizeLength, dataSize, offset)
{
}
EBML::MkSegment::~MkSegment() = default;
offset_t EBML::MkSegment::segmentDataOffset() const
{
return offset + idSize(id) + sizeLength;
}
bool EBML::MkSegment::read(File &file)
{
const offset_t maxOffset = file.tell() + dataSize;
std::unique_ptr<Element> element;
int i = 0;
int seekHeadIndex = -1;
while((element = findNextElement(file, maxOffset))) {
if(const Id id = element->getId(); id == Id::MkSeekHead) {
seekHeadIndex = i;
seekHead = element_cast<Id::MkSeekHead>(std::move(element));
if(!seekHead->read(file))
return false;
}
else if(id == Id::MkCues) {
cues = element_cast<Id::MkCues>(std::move(element));
if(!cues->read(file))
return false;
}
else if(id == Id::MkInfo) {
info = element_cast<Id::MkInfo>(std::move(element));
if(!info->read(file))
return false;
}
else if(id == Id::MkTracks) {
tracks = element_cast<Id::MkTracks>(std::move(element));
if(!tracks->read(file))
return false;
}
else if(id == Id::MkTags) {
tags = element_cast<Id::MkTags>(std::move(element));
if(!tags->read(file))
return false;
}
else if(id == Id::MkAttachments) {
attachments = element_cast<Id::MkAttachments>(std::move(element));
if(!attachments->read(file))
return false;
}
else if(id == Id::MkChapters) {
chapters = element_cast<Id::MkChapters>(std::move(element));
if(!chapters->read(file))
return false;
}
else {
if(id == Id::VoidElement
&& seekHead
&& seekHeadIndex == i - 1)
seekHead->setPadding(element->getSize());
element->skipData(file);
}
i++;
}
return true;
}
std::unique_ptr<Matroska::Tag> EBML::MkSegment::parseTag() const
{
return tags ? tags->parse() : nullptr;
}
std::unique_ptr<Matroska::Attachments> EBML::MkSegment::parseAttachments() const
{
return attachments ? attachments->parse() : nullptr;
}
std::unique_ptr<Matroska::Chapters> EBML::MkSegment::parseChapters() const
{
return chapters ? chapters->parse() : nullptr;
}
std::unique_ptr<Matroska::SeekHead> EBML::MkSegment::parseSeekHead() const
{
return seekHead ? seekHead->parse(segmentDataOffset()) : nullptr;
}
std::unique_ptr<Matroska::Cues> EBML::MkSegment::parseCues() const
{
return cues ? cues->parse(segmentDataOffset()) : nullptr;
}
std::unique_ptr<Matroska::Segment> EBML::MkSegment::parseSegment() const
{
return std::make_unique<Matroska::Segment>(sizeLength, dataSize, offset + idSize(id));
}
void EBML::MkSegment::parseInfo(Matroska::Properties *properties) const
{
if(info) {
info->parse(properties);
}
}
void EBML::MkSegment::parseTracks(Matroska::Properties *properties) const
{
if(tracks) {
tracks->parse(properties);
}
}

View File

@@ -0,0 +1,76 @@
/***************************************************************************
* 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_EBMLMKSEGMENT_H
#define TAGLIB_EBMLMKSEGMENT_H
#ifndef DO_NOT_DOCUMENT
#include "ebmlmasterelement.h"
#include "ebmlmktags.h"
#include "ebmlmkattachments.h"
#include "ebmlmkchapters.h"
#include "ebmlmkseekhead.h"
#include "ebmlmkcues.h"
#include "ebmlmkinfo.h"
#include "ebmlmktracks.h"
#include "taglib.h"
namespace TagLib {
namespace Matroska {
class Tag;
class Attachments;
class Chapters;
class SeekHead;
class Segment;
}
namespace EBML {
class MkSegment : public MasterElement
{
public:
MkSegment(int sizeLength, offset_t dataSize, offset_t offset);
MkSegment(Id, int sizeLength, offset_t dataSize, offset_t offset);
~MkSegment() override;
offset_t segmentDataOffset() const;
bool read(File &file) override;
std::unique_ptr<Matroska::Tag> parseTag() const;
std::unique_ptr<Matroska::Attachments> parseAttachments() const;
std::unique_ptr<Matroska::Chapters> parseChapters() const;
std::unique_ptr<Matroska::SeekHead> parseSeekHead() const;
std::unique_ptr<Matroska::Cues> parseCues() const;
std::unique_ptr<Matroska::Segment> parseSegment() const;
void parseInfo(Matroska::Properties *properties) const;
void parseTracks(Matroska::Properties *properties) const;
private:
std::unique_ptr<MkTags> tags;
std::unique_ptr<MkAttachments> attachments;
std::unique_ptr<MkChapters> chapters;
std::unique_ptr<MkSeekHead> seekHead;
std::unique_ptr<MkCues> cues;
std::unique_ptr<MkInfo> info;
std::unique_ptr<MkTracks> tracks;
};
}
}
#endif
#endif

View File

@@ -0,0 +1,119 @@
/***************************************************************************
* 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 "ebmlmktags.h"
#include "ebmlbinaryelement.h"
#include "ebmluintelement.h"
#include "ebmlstringelement.h"
#include "matroskatag.h"
#include "matroskasimpletag.h"
#include "tlist.h"
using namespace TagLib;
EBML::MkTags::MkTags(int sizeLength, offset_t dataSize, offset_t offset):
MasterElement(Id::MkTags, sizeLength, dataSize, offset)
{
}
EBML::MkTags::MkTags(Id, int sizeLength, offset_t dataSize, offset_t offset):
MasterElement(Id::MkTags, sizeLength, dataSize, offset)
{
}
EBML::MkTags::MkTags():
MasterElement(Id::MkTags, 0, 0, 0)
{
}
std::unique_ptr<Matroska::Tag> EBML::MkTags::parse() const
{
auto mTag = std::make_unique<Matroska::Tag>();
mTag->setOffset(offset);
mTag->setSize(getSize());
mTag->setID(static_cast<Matroska::Element::ID>(id));
// Loop through each <Tag> element
for(const auto &tagsChild : elements) {
if(tagsChild->getId() != Id::MkTag)
continue;
const auto tag = element_cast<Id::MkTag>(tagsChild);
List<const MasterElement *> simpleTags;
const MasterElement *targets = nullptr;
// Identify the <Targets> element and the <SimpleTag> elements
for(const auto &tagChild : *tag) {
if(const Id tagChildId = tagChild->getId(); !targets && tagChildId == Id::MkTagTargets)
targets = element_cast<Id::MkTagTargets>(tagChild);
else if(tagChildId == Id::MkSimpleTag)
simpleTags.append(element_cast<Id::MkSimpleTag>(tagChild));
}
// Parse the <Targets> element
Matroska::SimpleTag::TargetTypeValue targetTypeValue = Matroska::SimpleTag::TargetTypeValue::None;
unsigned long long trackUid = 0;
if(targets) {
for(const auto &targetsChild : *targets) {
if(const Id id = targetsChild->getId(); id == Id::MkTagTargetTypeValue
&& targetTypeValue == Matroska::SimpleTag::TargetTypeValue::None) {
targetTypeValue = static_cast<Matroska::SimpleTag::TargetTypeValue>(
element_cast<Id::MkTagTargetTypeValue>(targetsChild)->getValue()
);
}
else if(id == Id::MkTagTrackUID) {
trackUid = element_cast<Id::MkTagTrackUID>(targetsChild)->getValue();
}
}
}
// Parse each <SimpleTag>
for(const auto simpleTag : simpleTags) {
const String *tagValueString = nullptr;
const ByteVector *tagValueBinary = nullptr;
String tagName;
String language;
bool defaultLanguageFlag = true;
for(const auto &simpleTagChild : *simpleTag) {
if(const Id id = simpleTagChild->getId(); id == Id::MkTagName && tagName.isEmpty())
tagName = element_cast<Id::MkTagName>(simpleTagChild)->getValue();
else if(id == Id::MkTagString && !tagValueString)
tagValueString = &element_cast<Id::MkTagString>(simpleTagChild)->getValue();
else if(id == Id::MkTagBinary && !tagValueBinary)
tagValueBinary = &element_cast<Id::MkTagBinary>(simpleTagChild)->getValue();
else if(id == Id::MkTagsTagLanguage && language.isEmpty())
language = element_cast<Id::MkTagsTagLanguage>(simpleTagChild)->getValue();
else if(id == Id::MkTagsLanguageDefault)
defaultLanguageFlag = element_cast<Id::MkTagsLanguageDefault>(simpleTagChild)->getValue() ? true : false;
}
if(tagName.isEmpty() || (tagValueString && tagValueBinary) || (!tagValueString && !tagValueBinary))
continue;
mTag->addSimpleTag(tagValueString
? Matroska::SimpleTag(tagName, *tagValueString,
targetTypeValue, language, defaultLanguageFlag,
trackUid)
: Matroska::SimpleTag(tagName, *tagValueBinary,
targetTypeValue, language, defaultLanguageFlag,
trackUid));
}
}
return mTag;
}

View File

@@ -0,0 +1,47 @@
/***************************************************************************
* 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_EBMLMKTAGS_H
#define TAGLIB_EBMLMKTAGS_H
#ifndef DO_NOT_DOCUMENT
#include "ebmlmasterelement.h"
#include "taglib.h"
namespace TagLib {
namespace Matroska {
class Tag;
}
namespace EBML {
class MkTags : public MasterElement
{
public:
MkTags(int sizeLength, offset_t dataSize, offset_t offset);
MkTags(Id, int sizeLength, offset_t dataSize, offset_t offset);
MkTags();
std::unique_ptr<Matroska::Tag> parse() const;
};
}
}
#endif
#endif

View File

@@ -0,0 +1,86 @@
/***************************************************************************
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 "ebmlmktracks.h"
#include "ebmlstringelement.h"
#include "ebmluintelement.h"
#include "ebmlfloatelement.h"
#include "matroskaproperties.h"
using namespace TagLib;
EBML::MkTracks::MkTracks(int sizeLength, offset_t dataSize, offset_t offset):
MasterElement(Id::MkTracks, sizeLength, dataSize, offset)
{
}
EBML::MkTracks::MkTracks(Id, int sizeLength, offset_t dataSize, offset_t offset):
MasterElement(Id::MkTracks, sizeLength, dataSize, offset)
{
}
EBML::MkTracks::MkTracks():
MasterElement(Id::MkTracks, 0, 0, 0)
{
}
void EBML::MkTracks::parse(Matroska::Properties *properties) const
{
if(!properties)
return;
for(const auto &element : elements) {
if(element->getId() != Id::MkTrackEntry)
continue;
String codecId;
double samplingFrequency = 0.0;
unsigned long long bitDepth = 0;
unsigned long long channels = 0;
const auto trackEntry = element_cast<Id::MkTrackEntry>(element);
for(const auto &trackEntryChild : *trackEntry) {
if(const Id trackEntryChildId = trackEntryChild->getId(); trackEntryChildId == Id::MkCodecID)
codecId = element_cast<Id::MkCodecID>(trackEntryChild)->getValue();
else if(trackEntryChildId == Id::MkAudio) {
const auto audio = element_cast<Id::MkAudio>(trackEntryChild);
for(const auto &audioChild : *audio) {
if(const Id audioChildId = audioChild->getId(); audioChildId == Id::MkSamplingFrequency)
samplingFrequency = element_cast<Id::MkSamplingFrequency>(audioChild)->getValueAsDouble();
else if(audioChildId == Id::MkBitDepth)
bitDepth = element_cast<Id::MkBitDepth>(audioChild)->getValue();
else if(audioChildId == Id::MkChannels)
channels = element_cast<Id::MkChannels>(audioChild)->getValue();
}
}
}
if(bitDepth || channels) {
properties->setSampleRate(static_cast<int>(samplingFrequency));
properties->setBitsPerSample(static_cast<int>(bitDepth));
properties->setChannels(static_cast<int>(channels));
properties->setCodecName(codecId);
return;
}
}
}

View File

@@ -0,0 +1,52 @@
/***************************************************************************
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_EBMLMKTRACKS_H
#define TAGLIB_EBMLMKTRACKS_H
#ifndef DO_NOT_DOCUMENT
#include "ebmlmasterelement.h"
#include "taglib.h"
namespace TagLib {
namespace Matroska {
class Properties;
}
namespace EBML {
class MkTracks : public MasterElement
{
public:
MkTracks(int sizeLength, offset_t dataSize, offset_t offset);
MkTracks(Id, int sizeLength, offset_t dataSize, offset_t offset);
MkTracks();
void parse(Matroska::Properties *properties) const;
};
}
}
#endif
#endif

View File

@@ -0,0 +1,100 @@
/***************************************************************************
* 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 "ebmlstringelement.h"
#include <string>
#include "tfile.h"
#include "tbytevector.h"
#include "tdebug.h"
#include "ebmlutils.h"
using namespace TagLib;
EBML::StringElement::StringElement(
String::Type stringEncoding, Id id, int sizeLength, offset_t dataSize):
Element(id, sizeLength, dataSize), encoding(stringEncoding)
{
}
const String& EBML::StringElement::getValue() const
{
return value;
}
void EBML::StringElement::setValue(const String& val)
{
value = val;
}
bool EBML::StringElement::read(File &file)
{
ByteVector buffer = file.readBlock(dataSize);
if(buffer.size() != dataSize) {
debug("Failed to read string");
return false;
}
// The EBML strings aren't supposed to be null-terminated,
// but we'll check for it and strip the null terminator if found
if(const int nullByte = buffer.find('\0'); nullByte >= 0)
buffer = ByteVector(buffer.data(), nullByte);
value = String(buffer, encoding);
return true;
}
ByteVector EBML::StringElement::render()
{
ByteVector buffer = renderId();
const std::string string = value.to8Bit(encoding == String::UTF8);
dataSize = string.size();
buffer.append(renderVINT(dataSize, 0));
buffer.append(ByteVector(string.data(), static_cast<unsigned int>(dataSize)));
return buffer;
}
EBML::UTF8StringElement::UTF8StringElement(Id id, int sizeLength, offset_t dataSize):
StringElement(String::UTF8, id, sizeLength, dataSize)
{
}
EBML::UTF8StringElement::UTF8StringElement(Id id, int sizeLength, offset_t dataSize, offset_t):
UTF8StringElement(id, sizeLength, dataSize)
{
}
EBML::UTF8StringElement::UTF8StringElement(Id id):
UTF8StringElement(id, 0, 0)
{
}
EBML::Latin1StringElement::Latin1StringElement(Id id, int sizeLength, offset_t dataSize):
StringElement(String::Latin1, id, sizeLength, dataSize)
{
}
EBML::Latin1StringElement::Latin1StringElement(Id id, int sizeLength, offset_t dataSize, offset_t):
Latin1StringElement(id, sizeLength, dataSize)
{
}
EBML::Latin1StringElement::Latin1StringElement(Id id):
Latin1StringElement(id, 0, 0)
{
}

View File

@@ -0,0 +1,65 @@
/***************************************************************************
* 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_EBMLSTRINGELEMENT_H
#define TAGLIB_EBMLSTRINGELEMENT_H
#ifndef DO_NOT_DOCUMENT
#include "ebmlelement.h"
#include "tstring.h"
namespace TagLib {
class File;
class ByteVector;
namespace EBML {
class StringElement : public Element
{
public:
StringElement(String::Type stringEncoding, Id id, int sizeLength, offset_t dataSize);
const String &getValue() const;
void setValue(const String &val);
bool read(File &file) override;
ByteVector render() override;
private:
String value;
String::Type encoding;
};
class UTF8StringElement : public StringElement {
public:
UTF8StringElement(Id id, int sizeLength, offset_t dataSize);
UTF8StringElement(Id id, int sizeLength, offset_t dataSize, offset_t);
explicit UTF8StringElement(Id id);
};
class Latin1StringElement : public StringElement {
public:
Latin1StringElement(Id id, int sizeLength, offset_t dataSize);
Latin1StringElement(Id id, int sizeLength, offset_t dataSize, offset_t);
explicit Latin1StringElement(Id id);
};
}
}
#endif
#endif

View File

@@ -0,0 +1,95 @@
/***************************************************************************
* 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 "ebmluintelement.h"
#include "ebmlutils.h"
#include "tbytevector.h"
#include "tfile.h"
#include "tutils.h"
#include "tdebug.h"
using namespace TagLib;
EBML::UIntElement::UIntElement(Id id, int sizeLength, offset_t dataSize):
Element(id, sizeLength, dataSize)
{
}
EBML::UIntElement::UIntElement(Id id, int sizeLength, offset_t dataSize, offset_t):
Element(id, sizeLength, dataSize)
{
}
EBML::UIntElement::UIntElement(Id id):
UIntElement(id, 0, 0)
{
}
unsigned long long EBML::UIntElement::getValue() const
{
return value;
}
void EBML::UIntElement::setValue(unsigned long long val)
{
value = val;
}
bool EBML::UIntElement::read(File &file)
{
const ByteVector buffer = file.readBlock(dataSize);
if(buffer.size() != dataSize) {
debug("Failed to read EBML Uint element");
return false;
}
value = buffer.toULongLong(true);
return true;
}
ByteVector EBML::UIntElement::render()
{
int dataSize = 0;
if(value <= 0xFFull)
dataSize = 1;
else if(value <= 0xFFFFull)
dataSize = 2;
else if(value <= 0xFFFFFFull)
dataSize = 3;
else if(value <= 0xFFFFFFFFull)
dataSize = 4;
else if(value <= 0xFFFFFFFFFFull)
dataSize = 5;
else if(value <= 0xFFFFFFFFFFFFull)
dataSize = 6;
else if(value <= 0xFFFFFFFFFFFFFFull)
dataSize = 7;
else if(value <= 0xFFFFFFFFFFFFFFFFull)
dataSize = 8;
ByteVector buffer = renderId();
buffer.append(renderVINT(dataSize, 0));
uint64_t val = value;
static const auto byteOrder = Utils::systemByteOrder();
if(byteOrder == Utils::LittleEndian)
val = Utils::byteSwap(val);
buffer.append(ByteVector(reinterpret_cast<char *>(&val) + (sizeof(val) - dataSize), dataSize));
return buffer;
}

View File

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

View File

@@ -0,0 +1,122 @@
/***************************************************************************
* 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 "ebmlutils.h"
#include <random>
#include "tbytevector.h"
#include "matroskafile.h"
#include "tutils.h"
#include "tdebug.h"
using namespace TagLib;
std::unique_ptr<EBML::Element> EBML::findElement(File &file, Element::Id id, offset_t maxOffset)
{
std::unique_ptr<Element> element;
while(file.tell() < maxOffset) {
element = Element::factory(file);
if(!element || element->getId() == id)
return element;
element->skipData(file);
element.reset();
}
return element;
}
std::unique_ptr<EBML::Element> EBML::findNextElement(File &file, offset_t maxOffset)
{
return file.tell() < maxOffset ? Element::factory(file) : nullptr;
}
template <int maxSizeLength>
unsigned int EBML::VINTSizeLength(uint8_t firstByte)
{
static_assert(maxSizeLength >= 1 && maxSizeLength <= 8);
if(!firstByte) {
debug("VINT with greater than 8 bytes not allowed");
return 0;
}
uint8_t mask = 0b10000000;
unsigned int numBytes = 1;
while(!(mask & firstByte)) {
numBytes++;
mask >>= 1;
}
if(numBytes > maxSizeLength) {
debug(Utils::formatString("VINT size length exceeds %i bytes", maxSizeLength));
return 0;
}
return numBytes;
}
namespace TagLib::EBML {
template unsigned int VINTSizeLength<4>(uint8_t firstByte);
template unsigned int VINTSizeLength<8>(uint8_t firstByte);
}
std::pair<unsigned int, uint64_t> EBML::readVINT(File &file)
{
auto buffer = file.readBlock(1);
if(buffer.size() != 1) {
debug("Failed to read VINT size");
return {0, 0};
}
unsigned int numBytes = VINTSizeLength<8>(*buffer.begin());
if(!numBytes)
return {0, 0};
if(numBytes > 1)
buffer.append(file.readBlock(numBytes - 1));
const int bitsToShift = static_cast<int>(sizeof(uint64_t) * 8) - 7 * numBytes;
const uint64_t mask = 0xFFFFFFFFFFFFFFFF >> bitsToShift;
return { numBytes, buffer.toULongLong(true) & mask };
}
std::pair<unsigned int, uint64_t> EBML::parseVINT(const ByteVector &buffer)
{
if(buffer.isEmpty())
return {0, 0};
unsigned int numBytes = VINTSizeLength<8>(*buffer.begin());
if(!numBytes)
return {0, 0};
const int bitsToShift = static_cast<int>(sizeof(uint64_t) * 8) - 7 * numBytes;
const uint64_t mask = 0xFFFFFFFFFFFFFFFF >> bitsToShift;
return { numBytes, buffer.toULongLong(true) & mask };
}
ByteVector EBML::renderVINT(uint64_t number, int minSizeLength)
{
const int numBytes = std::max(minSizeLength, minSize(number));
number |= 1ULL << (numBytes * 7);
static const auto byteOrder = Utils::systemByteOrder();
if(byteOrder == Utils::LittleEndian)
number = Utils::byteSwap(number);
return ByteVector(reinterpret_cast<char *>(&number) + (sizeof(number) - numBytes), numBytes);
}
unsigned long long EBML::randomUID()
{
static std::random_device device;
static std::mt19937 generator(device());
static std::uniform_int_distribution<unsigned long long> distribution;
return distribution(generator);
}

View File

@@ -0,0 +1,78 @@
/***************************************************************************
* 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_EBMLUTILS_H
#define TAGLIB_EBMLUTILS_H
#ifndef DO_NOT_DOCUMENT
#include <utility>
#include "taglib.h"
#include "ebmlelement.h"
namespace TagLib {
class File;
class ByteVector;
namespace EBML {
std::unique_ptr<Element> findElement(File &file, Element::Id id, offset_t maxOffset);
std::unique_ptr<Element> findNextElement(File &file, offset_t maxOffset);
template <int maxSizeLength>
unsigned int VINTSizeLength(uint8_t firstByte);
std::pair<unsigned int, uint64_t> readVINT(File &file);
std::pair<unsigned int, uint64_t> parseVINT(const ByteVector &buffer);
ByteVector renderVINT(uint64_t number, int minSizeLength);
unsigned long long randomUID();
constexpr int minSize(uint64_t data)
{
if(data <= 0x7Fu)
return 1;
if(data <= 0x3FFFu)
return 2;
if(data <= 0x1FFFFFu)
return 3;
if(data <= 0xFFFFFFFu)
return 4;
if(data <= 0x7FFFFFFFFu)
return 5;
return 0;
}
constexpr int idSize(Element::Id id)
{
const auto uintId = static_cast<unsigned int>(id);
if(uintId <= 0xFF)
return 1;
if(uintId <= 0xFFFF)
return 2;
if(uintId <= 0xFFFFFF)
return 3;
return 4;
}
}
}
#endif
#endif

View File

@@ -0,0 +1,73 @@
/***************************************************************************
* 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 "ebmlvoidelement.h"
#include <algorithm>
#include "ebmlutils.h"
#include "tbytevector.h"
using namespace TagLib;
EBML::VoidElement::VoidElement(int sizeLength, offset_t dataSize):
Element(Id::VoidElement, sizeLength, dataSize)
{
}
EBML::VoidElement::VoidElement(Id, int sizeLength, offset_t dataSize, offset_t):
Element(Id::VoidElement, sizeLength, dataSize)
{
}
EBML::VoidElement::VoidElement():
Element(Id::VoidElement, 0, 0)
{
}
ByteVector EBML::VoidElement::render()
{
offset_t bytesNeeded = targetSize;
ByteVector buffer = renderId();
bytesNeeded -= buffer.size();
sizeLength = static_cast<int>(std::min(bytesNeeded, static_cast<offset_t>(8)));
bytesNeeded -= sizeLength;
dataSize = bytesNeeded;
buffer.append(renderVINT(dataSize, sizeLength));
if(dataSize)
buffer.append(ByteVector(static_cast<unsigned int>(dataSize), 0));
return buffer;
}
offset_t EBML::VoidElement::getTargetSize() const
{
return targetSize;
}
void EBML::VoidElement::setTargetSize(offset_t size)
{
this->targetSize = std::max(size, MIN_VOID_ELEMENT_SIZE);
}
ByteVector EBML::VoidElement::renderSize(offset_t targetSize)
{
VoidElement element;
element.setTargetSize(targetSize);
return element.render();
}

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_EBMLVOIDELEMENT_H
#define TAGLIB_EBMLVOIDELEMENT_H
#ifndef DO_NOT_DOCUMENT
#include "ebmlelement.h"
namespace TagLib {
class File;
namespace EBML {
inline constexpr offset_t MIN_VOID_ELEMENT_SIZE = 2;
class VoidElement : public Element
{
public:
VoidElement(int sizeLength, offset_t dataSize);
VoidElement(Id, int sizeLength, offset_t dataSize, offset_t);
VoidElement();
ByteVector render() override;
offset_t getTargetSize() const;
void setTargetSize(offset_t size);
static ByteVector renderSize(offset_t targetSize);
private:
offset_t targetSize = MIN_VOID_ELEMENT_SIZE;
};
}
}
#endif
#endif

View File

@@ -0,0 +1,99 @@
/***************************************************************************
* 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 "matroskaattachedfile.h"
#include "tbytevector.h"
using namespace TagLib;
class Matroska::AttachedFile::AttachedFilePrivate
{
public:
AttachedFilePrivate(const ByteVector &data, const String &fileName,
const String &mediaType, UID uid, const String &description) :
fileName(fileName), description(description), mediaType(mediaType),
data(data), uid(uid) {}
~AttachedFilePrivate() = default;
String fileName;
String description;
String mediaType;
ByteVector data;
UID uid = 0;
};
////////////////////////////////////////////////////////////////////////////////
// public members
////////////////////////////////////////////////////////////////////////////////
Matroska::AttachedFile::AttachedFile(const ByteVector &data,
const String &fileName, const String &mediaType, UID uid,
const String &description) :
d(std::make_unique<AttachedFilePrivate>(data, fileName, mediaType, uid, description))
{
}
Matroska::AttachedFile::AttachedFile(const AttachedFile &other) :
d(std::make_unique<AttachedFilePrivate>(*other.d))
{
}
Matroska::AttachedFile::AttachedFile(AttachedFile &&other) noexcept = default;
Matroska::AttachedFile::~AttachedFile() = default;
Matroska::AttachedFile &Matroska::AttachedFile::operator=(AttachedFile &&other) noexcept = default;
Matroska::AttachedFile &Matroska::AttachedFile::operator=(const AttachedFile &other)
{
AttachedFile(other).swap(*this);
return *this;
}
void Matroska::AttachedFile::swap(AttachedFile &other) noexcept
{
using std::swap;
swap(d, other.d);
}
const String &Matroska::AttachedFile::fileName() const
{
return d->fileName;
}
const String &Matroska::AttachedFile::description() const
{
return d->description;
}
const String &Matroska::AttachedFile::mediaType() const
{
return d->mediaType;
}
const ByteVector &Matroska::AttachedFile::data() const
{
return d->data;
}
Matroska::AttachedFile::UID Matroska::AttachedFile::uid() const
{
return d->uid;
}

View File

@@ -0,0 +1,110 @@
/***************************************************************************
* 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_MATROSKAATTACHEDFILE_H
#define TAGLIB_MATROSKAATTACHEDFILE_H
#include <memory>
#include "tstring.h"
#include "taglib_export.h"
namespace TagLib {
class String;
class ByteVector;
namespace Matroska {
//! Attached file embedded into a Matroska file.
class TAGLIB_EXPORT AttachedFile
{
public:
//! Unique identifier.
using UID = unsigned long long;
/*!
* Construct an attached file.
*/
AttachedFile(const ByteVector &data, const String &fileName,
const String &mediaType, UID uid = 0,
const String &description = String());
/*!
* Construct an attached file as a copy of \a other.
*/
AttachedFile(const AttachedFile &other);
/*!
* Construct an attached file moving from \a other.
*/
AttachedFile(AttachedFile &&other) noexcept;
/*!
* Destroys this attached file.
*/
~AttachedFile();
/*!
* Copies the contents of \a other into this object.
*/
AttachedFile &operator=(const AttachedFile &other);
/*!
* Moves the contents of \a other into this object.
*/
AttachedFile &operator=(AttachedFile &&other) noexcept;
/*!
* Exchanges the content of the object with the content of \a other.
*/
void swap(AttachedFile &other) noexcept;
/*!
* Returns the filename of the attached file.
*/
const String &fileName() const;
/*!
* Returns the human-friendly description for the attached file.
*/
const String &description() const;
/*!
* Returns the media type of the attached file.
*/
const String &mediaType() const;
/*!
* Returns the data of the attached file.
*/
const ByteVector &data() const;
/*!
* Returns the UID of the attached file.
*/
UID uid() const;
private:
class AttachedFilePrivate;
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
std::unique_ptr<AttachedFilePrivate> d;
};
}
}
#endif

View File

@@ -0,0 +1,141 @@
/***************************************************************************
* 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 "matroskaattachments.h"
#include "matroskaattachedfile.h"
#include "ebmlmkattachments.h"
#include "ebmlmasterelement.h"
#include "ebmlstringelement.h"
#include "ebmlbinaryelement.h"
#include "ebmluintelement.h"
#include "ebmlutils.h"
#include "tlist.h"
#include "tbytevector.h"
using namespace TagLib;
class Matroska::Attachments::AttachmentsPrivate
{
public:
AttachmentsPrivate() = default;
~AttachmentsPrivate() = default;
AttachmentsPrivate(const AttachmentsPrivate &) = delete;
AttachmentsPrivate &operator=(const AttachmentsPrivate &) = delete;
AttachedFileList files;
};
////////////////////////////////////////////////////////////////////////////////
// public members
////////////////////////////////////////////////////////////////////////////////
Matroska::Attachments::Attachments() :
Element(static_cast<ID>(EBML::Element::Id::MkAttachments)),
d(std::make_unique<AttachmentsPrivate>())
{
}
Matroska::Attachments::~Attachments() = default;
void Matroska::Attachments::addAttachedFile(const AttachedFile &file)
{
d->files.append(file);
setNeedsRender(true);
}
void Matroska::Attachments::removeAttachedFile(unsigned long long uid)
{
const auto it = std::find_if(d->files.begin(), d->files.end(),
[uid](const AttachedFile &file) {
return file.uid() == uid;
});
if(it != d->files.end()) {
d->files.erase(it);
setNeedsRender(true);
}
}
void Matroska::Attachments::clear()
{
d->files.clear();
setNeedsRender(true);
}
const Matroska::Attachments::AttachedFileList &Matroska::Attachments::attachedFileList() const
{
return d->files;
}
////////////////////////////////////////////////////////////////////////////////
// private members
////////////////////////////////////////////////////////////////////////////////
Matroska::Attachments::AttachedFileList &Matroska::Attachments::attachedFiles()
{
setNeedsRender(true);
return d->files;
}
ByteVector Matroska::Attachments::renderInternal()
{
if(d->files.isEmpty()) {
// Avoid writing an Attachments element without AttachedFile element.
return {};
}
EBML::MkAttachments attachments;
for(const auto &attachedFile : std::as_const(d->files)) {
auto attachedFileElement = EBML::make_unique_element<EBML::Element::Id::MkAttachedFile>();
// Filename
auto fileNameElement = EBML::make_unique_element<EBML::Element::Id::MkAttachedFileName>();
fileNameElement->setValue(attachedFile.fileName());
attachedFileElement->appendElement(std::move(fileNameElement));
// Media/MIME type
auto mediaTypeElement =
EBML::make_unique_element<EBML::Element::Id::MkAttachedFileMediaType>();
mediaTypeElement->setValue(attachedFile.mediaType());
attachedFileElement->appendElement(std::move(mediaTypeElement));
// Description
if(const String &description = attachedFile.description(); !description.isEmpty()) {
auto descriptionElement =
EBML::make_unique_element<EBML::Element::Id::MkAttachedFileDescription>();
descriptionElement->setValue(description);
attachedFileElement->appendElement(std::move(descriptionElement));
}
// Data
auto dataElement = EBML::make_unique_element<EBML::Element::Id::MkAttachedFileData>();
dataElement->setValue(attachedFile.data());
attachedFileElement->appendElement(std::move(dataElement));
// UID
auto uidElement = EBML::make_unique_element<EBML::Element::Id::MkAttachedFileUID>();
AttachedFile::UID uid = attachedFile.uid();
if(!uid)
uid = EBML::randomUID();
uidElement->setValue(uid);
attachedFileElement->appendElement(std::move(uidElement));
attachments.appendElement(std::move(attachedFileElement));
}
return attachments.render();
}

View File

@@ -0,0 +1,83 @@
/***************************************************************************
* 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_MATROSKAATTACHMENTS_H
#define TAGLIB_MATROSKAATTACHMENTS_H
#include <memory>
#include "taglib_export.h"
#include "tlist.h"
#include "matroskaelement.h"
namespace TagLib {
class File;
namespace EBML {
class MkAttachments;
}
namespace Matroska {
class AttachedFile;
class File;
//! Collection of attached files.
class TAGLIB_EXPORT Attachments
#ifndef DO_NOT_DOCUMENT
: private Element
#endif
{
public:
//! List of attached files.
using AttachedFileList = List<AttachedFile>;
//! Construct attachments.
Attachments();
//! Destroy attachments.
virtual ~Attachments();
//! Add an attached file.
void addAttachedFile(const AttachedFile &file);
//! Remove an attached file.
void removeAttachedFile(unsigned long long uid);
//! Remove all attached files.
void clear();
//! Get list of all attached files.
const AttachedFileList &attachedFileList() const;
private:
friend class EBML::MkAttachments;
friend class File;
class AttachmentsPrivate;
// private Element implementation
ByteVector renderInternal() override;
AttachedFileList &attachedFiles();
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
std::unique_ptr<AttachmentsPrivate> d;
};
}
}
#endif

View File

@@ -0,0 +1,157 @@
/***************************************************************************
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 "matroskachapter.h"
#include "tstring.h"
#include "tbytevector.h"
using namespace TagLib;
class Matroska::Chapter::Display::DisplayPrivate
{
public:
DisplayPrivate() = default;
~DisplayPrivate() = default;
String string;
String language;
};
class Matroska::Chapter::ChapterPrivate
{
public:
ChapterPrivate() = default;
~ChapterPrivate() = default;
UID uid = 0;
Time timeStart = 0;
Time timeEnd = 0;
List<Display> displayList;
bool hidden = false;
};
////////////////////////////////////////////////////////////////////////////////
// public members
////////////////////////////////////////////////////////////////////////////////
Matroska::Chapter::Chapter(Time timeStart, Time timeEnd,
const List<Display> &displayList, UID uid, bool hidden) :
d(std::make_unique<ChapterPrivate>())
{
d->uid = uid;
d->timeStart = timeStart;
d->timeEnd = timeEnd;
d->displayList = displayList;
d->hidden = hidden;
}
Matroska::Chapter::Chapter(const Chapter &other) :
d(std::make_unique<ChapterPrivate>(*other.d))
{
}
Matroska::Chapter::Chapter(Chapter &&other) noexcept = default;
Matroska::Chapter::~Chapter() = default;
Matroska::Chapter &Matroska::Chapter::operator=(Chapter &&other) noexcept = default;
Matroska::Chapter &Matroska::Chapter::operator=(const Chapter &other)
{
Chapter(other).swap(*this);
return *this;
}
void Matroska::Chapter::swap(Chapter &other) noexcept
{
using std::swap;
swap(d, other.d);
}
Matroska::Chapter::UID Matroska::Chapter::uid() const
{
return d->uid;
}
Matroska::Chapter::Time Matroska::Chapter::timeStart() const
{
return d->timeStart;
}
Matroska::Chapter::Time Matroska::Chapter::timeEnd() const
{
return d->timeEnd;
}
bool Matroska::Chapter::isHidden() const
{
return d->hidden;
}
const List<Matroska::Chapter::Display> &Matroska::Chapter::displayList() const
{
return d->displayList;
}
Matroska::Chapter::Display::Display(const String &string, const String &language) :
d(std::make_unique<DisplayPrivate>())
{
d->string = string;
d->language = language;
}
Matroska::Chapter::Display::Display(const Display &other) :
d(std::make_unique<DisplayPrivate>(*other.d))
{
}
Matroska::Chapter::Display::Display(Display &&other) noexcept = default;
Matroska::Chapter::Display::~Display() = default;
Matroska::Chapter::Display &Matroska::Chapter::Display::operator=(const Display &other)
{
Display(other).swap(*this);
return *this;
}
Matroska::Chapter::Display &Matroska::Chapter::Display::operator=(
Display &&other) noexcept = default;
void Matroska::Chapter::Display::swap(Display &other) noexcept
{
using std::swap;
swap(d, other.d);
}
const String &Matroska::Chapter::Display::string() const
{
return d->string;
}
const String &Matroska::Chapter::Display::language() const
{
return d->language;
}

View File

@@ -0,0 +1,178 @@
/***************************************************************************
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_MATROSKACHAPTER_H
#define TAGLIB_MATROSKACHAPTER_H
#include <memory>
#include "taglib_export.h"
#include "tlist.h"
namespace TagLib {
class String;
class ByteVector;
namespace EBML {
class MkChapters;
}
namespace Matroska {
//! Matroska chapter.
class TAGLIB_EXPORT Chapter
{
public:
//! Unique identifier.
using UID = unsigned long long;
//! Timestamp in nanoseconds.
using Time = unsigned long long;
/*!
* Contains all possible strings to use for the chapter display.
*/
class TAGLIB_EXPORT Display
{
public:
/*!
* Construct a chapter display.
*/
Display(const String &string, const String &language);
/*!
* Construct a chapter display as a copy of \a other.
*/
Display(const Display &other);
/*!
* Construct a chapter display moving from \a other.
*/
Display(Display &&other) noexcept;
/*!
* Destroys this chapter display.
*/
~Display();
/*!
* Copies the contents of \a other into this object.
*/
Display &operator=(const Display &other);
/*!
* Moves the contents of \a other into this object.
*/
Display &operator=(Display &&other) noexcept;
/*!
* Exchanges the content of the object with the content of \a other.
*/
void swap(Display &other) noexcept;
/*!
* Returns string representing the chapter.
*/
const String &string() const;
/*!
* Returns language corresponding to the string.
*/
const String &language() const;
private:
class DisplayPrivate;
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
std::unique_ptr<DisplayPrivate> d;
};
/*!
* Construct a chapter.
*/
Chapter(Time timeStart, Time timeEnd, const List<Display> &displayList,
UID uid, bool hidden = false);
/*!
* Construct a chapter as a copy of \a other.
*/
Chapter(const Chapter &other);
/*!
* Construct a chapter moving from \a other.
*/
Chapter(Chapter &&other) noexcept;
/*!
* Destroys this chapter.
*/
~Chapter();
/*!
* Copies the contents of \a other into this object.
*/
Chapter &operator=(const Chapter &other);
/*!
* Moves the contents of \a other into this object.
*/
Chapter &operator=(Chapter &&other) noexcept;
/*!
* Exchanges the content of the object with the content of \a other.
*/
void swap(Chapter &other) noexcept;
/*!
* Returns the UID of the chapter.
*/
UID uid() const;
/*!
* Returns the timestamp of the start of the chapter in nanoseconds.
*/
Time timeStart() const;
/*!
* Returns the timestamp of the start of the chapter in nanoseconds.
*/
Time timeEnd() const;
/*!
* Check if chapter is hidden.
*/
bool isHidden() const;
/*!
* Returns strings with language.
*/
const List<Display> &displayList() const;
private:
class ChapterPrivate;
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
std::unique_ptr<ChapterPrivate> d;
};
}
}
#endif

View File

@@ -0,0 +1,102 @@
/***************************************************************************
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 "matroskachapter.h"
#include "matroskachapteredition.h"
#include "tstring.h"
#include "tbytevector.h"
#include "tlist.h"
using namespace TagLib;
class Matroska::ChapterEdition::ChapterEditionPrivate
{
public:
ChapterEditionPrivate() = default;
~ChapterEditionPrivate() = default;
List<Chapter> chapters;
UID uid = 0;
bool flagDefault = false;
bool flagOrdered = false;
};
////////////////////////////////////////////////////////////////////////////////
// public members
////////////////////////////////////////////////////////////////////////////////
Matroska::ChapterEdition::ChapterEdition(const List<Chapter> &chapterList,
bool isDefault, bool isOrdered, UID uid) :
d(std::make_unique<ChapterEditionPrivate>())
{
d->chapters = chapterList;
d->uid = uid;
d->flagDefault = isDefault;
d->flagOrdered = isOrdered;
}
Matroska::ChapterEdition::ChapterEdition(const ChapterEdition &other) :
d(std::make_unique<ChapterEditionPrivate>(*other.d))
{
}
Matroska::ChapterEdition::ChapterEdition(ChapterEdition &&other) noexcept = default;
Matroska::ChapterEdition::~ChapterEdition() = default;
Matroska::ChapterEdition &Matroska::ChapterEdition::operator=(
ChapterEdition &&other) noexcept = default;
Matroska::ChapterEdition &Matroska::ChapterEdition::operator=(const ChapterEdition &other)
{
ChapterEdition(other).swap(*this);
return *this;
}
void Matroska::ChapterEdition::swap(ChapterEdition &other) noexcept
{
using std::swap;
swap(d, other.d);
}
Matroska::ChapterEdition::UID Matroska::ChapterEdition::uid() const
{
return d->uid;
}
bool Matroska::ChapterEdition::isDefault() const
{
return d->flagDefault;
}
bool Matroska::ChapterEdition::isOrdered() const
{
return d->flagOrdered;
}
const List<Matroska::Chapter> &Matroska::ChapterEdition::chapterList() const
{
return d->chapters;
}

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