380 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
Urs Fleisch
bd4c9cbf97 Support installation alongside TagLib 1 with -DTAGLIB_INSTALL_SUFFIX=-2 2023-12-24 14:16:15 +01:00
Urs Fleisch
7dc8bfc806 Fix property mappings
For MP4 map ENCODEDBY to ©enc instead of ©too, which is now mapped to
ENCODING.
For ASF, add new properties ENCODINGTIME (WM/EncodingTime) and FILEWEBPAGE
(WM/AudioFileURL).
2023-12-24 08:42:15 +01:00
Rosen Penev
1a1ee8b54f remove pointless static in namespace
Use constexpr too

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

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

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

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

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

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

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

Type IDs

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

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

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

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

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

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

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

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

* Apply suggestions from code review

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

---------

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

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

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

---------

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

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

and

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

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

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

* Use swap for assignment

---------

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

* Use swap assignment

---------

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

  #include <tversionnumber.h>

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

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

---------

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

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

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

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

More standard. Allows using the system provided utf8cpp.

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

* cmake: use standard find_package for zlib

Simpler.

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

---------

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

It gets implicitly generated.

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

* default some more

swap is not needed here as shared_ptr is copyable.

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

---------

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

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

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

* Use consistent order of includes

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

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

---------

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

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

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

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

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

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

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

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

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

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

* Restore const containers where non const temporaries are iterated

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

---------

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

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

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

* clang: fix documentation

Found with Wdocumentation

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

* add deprecated to length() and fix usages

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

---------

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

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

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

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

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

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

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

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

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

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

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

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

* clang-tidy: add missing deleted functions

Found with cppcoreguidelines-special-member-functions

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

* unique_ptr conversions

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

Also added missing = default.

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

---------

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

The latter is C++11.

Found with clang-analyzer-webkit.NoUncountedMemberChecker

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

* remove trefcounter

It is now unused.

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

---------

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

All this stuff was replaced with std::atomic

Fixes: 9bcba812af

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

* cmake: remove vsnprintf checks

Available since C++11

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

* clang-tidy: const ref conversions

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

* clang-tidy: handle self assignment

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

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

* clang-tidy: remove wrong forward declarations

Found with bugprone-forward-declaration-namespace

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

---------

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

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

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

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

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

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

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

---------

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

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

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

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

Require a more modern cppunit to fix tagib build breakage.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Signed-off-by: Andreas Sturmlechner <asturm@gentoo.org>
2021-03-06 14:52:25 +01:00
Scott Wheeler
340ec9932a SVG for API doc diagrams makes a lot more sense in 2021
This also makes things look non-crappy on high-DPI monitors.
2021-02-16 12:00:52 +00:00
410 changed files with 30959 additions and 12641 deletions

View File

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

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

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

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

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

9
.gitignore vendored
View File

@@ -3,6 +3,7 @@ cmake_uninstall.cmake
Makefile
CTestTestfile.cmake
CMakeFiles/
CMakeLists.txt.user*
*.so
*.so.*
*.dylib
@@ -12,6 +13,7 @@ CMakeFiles/
*.suo
*.user
.*
!.github/workflows/
*~
/CMakeCache.txt
/Doxyfile
@@ -20,7 +22,6 @@ CMakeFiles/
/tests/test_runner
/tests/Testing
/taglib/libtag.a
/taglib_config.h
/taglib-config
/bindings/c/libtag_c.a
/bindings/c/taglib_c.pc
@@ -47,3 +48,9 @@ CMakeFiles/
taglib.h.stamp
taglib.xcodeproj
CMakeScripts
/.clang-format
/compile_commands.json
/build/
.clangd
.cache
.idea

3
.gitmodules vendored Normal file
View File

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

View File

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

View File

@@ -1,327 +0,0 @@
// Copyright 2006-2016 Nemanja Trifunovic
/*
Permission is hereby granted, free of charge, to any person or organization
obtaining a copy of the software and accompanying documentation covered by
this license (the "Software") to use, reproduce, display, distribute,
execute, and transmit the Software, and to prepare derivative works of the
Software, and to permit third-parties to whom the Software is furnished to
do so, all subject to the following:
The copyright notices in the Software and this entire statement, including
the above license grant, this restriction and the following disclaimer,
must be included in all copies of the Software, in whole or in part, and
all derivative works of the Software, unless such copies or derivative
works are solely in the form of machine-executable object code generated by
a source language processor.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
*/
#ifndef UTF8_FOR_CPP_CHECKED_H_2675DCD0_9480_4c0c_B92A_CC14C027B731
#define UTF8_FOR_CPP_CHECKED_H_2675DCD0_9480_4c0c_B92A_CC14C027B731
#include "core.h"
#include <stdexcept>
namespace utf8
{
// Base for the exceptions that may be thrown from the library
class exception : public ::std::exception {
};
// Exceptions that may be thrown from the library functions.
class invalid_code_point : public exception {
uint32_t cp;
public:
invalid_code_point(uint32_t codepoint) : cp(codepoint) {}
virtual const char* what() const throw() { return "Invalid code point"; }
uint32_t code_point() const {return cp;}
};
class invalid_utf8 : public exception {
uint8_t u8;
public:
invalid_utf8 (uint8_t u) : u8(u) {}
virtual const char* what() const throw() { return "Invalid UTF-8"; }
uint8_t utf8_octet() const {return u8;}
};
class invalid_utf16 : public exception {
uint16_t u16;
public:
invalid_utf16 (uint16_t u) : u16(u) {}
virtual const char* what() const throw() { return "Invalid UTF-16"; }
uint16_t utf16_word() const {return u16;}
};
class not_enough_room : public exception {
public:
virtual const char* what() const throw() { return "Not enough space"; }
};
/// The library API - functions intended to be called by the users
template <typename octet_iterator>
octet_iterator append(uint32_t cp, octet_iterator result)
{
if (!utf8::internal::is_code_point_valid(cp))
throw invalid_code_point(cp);
if (cp < 0x80) // one octet
*(result++) = static_cast<uint8_t>(cp);
else if (cp < 0x800) { // two octets
*(result++) = static_cast<uint8_t>((cp >> 6) | 0xc0);
*(result++) = static_cast<uint8_t>((cp & 0x3f) | 0x80);
}
else if (cp < 0x10000) { // three octets
*(result++) = static_cast<uint8_t>((cp >> 12) | 0xe0);
*(result++) = static_cast<uint8_t>(((cp >> 6) & 0x3f) | 0x80);
*(result++) = static_cast<uint8_t>((cp & 0x3f) | 0x80);
}
else { // four octets
*(result++) = static_cast<uint8_t>((cp >> 18) | 0xf0);
*(result++) = static_cast<uint8_t>(((cp >> 12) & 0x3f) | 0x80);
*(result++) = static_cast<uint8_t>(((cp >> 6) & 0x3f) | 0x80);
*(result++) = static_cast<uint8_t>((cp & 0x3f) | 0x80);
}
return result;
}
template <typename octet_iterator, typename output_iterator>
output_iterator replace_invalid(octet_iterator start, octet_iterator end, output_iterator out, uint32_t replacement)
{
while (start != end) {
octet_iterator sequence_start = start;
internal::utf_error err_code = utf8::internal::validate_next(start, end);
switch (err_code) {
case internal::UTF8_OK :
for (octet_iterator it = sequence_start; it != start; ++it)
*out++ = *it;
break;
case internal::NOT_ENOUGH_ROOM:
throw not_enough_room();
case internal::INVALID_LEAD:
out = utf8::append (replacement, out);
++start;
break;
case internal::INCOMPLETE_SEQUENCE:
case internal::OVERLONG_SEQUENCE:
case internal::INVALID_CODE_POINT:
out = utf8::append (replacement, out);
++start;
// just one replacement mark for the sequence
while (start != end && utf8::internal::is_trail(*start))
++start;
break;
}
}
return out;
}
template <typename octet_iterator, typename output_iterator>
inline output_iterator replace_invalid(octet_iterator start, octet_iterator end, output_iterator out)
{
static const uint32_t replacement_marker = utf8::internal::mask16(0xfffd);
return utf8::replace_invalid(start, end, out, replacement_marker);
}
template <typename octet_iterator>
uint32_t next(octet_iterator& it, octet_iterator end)
{
uint32_t cp = 0;
internal::utf_error err_code = utf8::internal::validate_next(it, end, cp);
switch (err_code) {
case internal::UTF8_OK :
break;
case internal::NOT_ENOUGH_ROOM :
throw not_enough_room();
case internal::INVALID_LEAD :
case internal::INCOMPLETE_SEQUENCE :
case internal::OVERLONG_SEQUENCE :
throw invalid_utf8(*it);
case internal::INVALID_CODE_POINT :
throw invalid_code_point(cp);
}
return cp;
}
template <typename octet_iterator>
uint32_t peek_next(octet_iterator it, octet_iterator end)
{
return utf8::next(it, end);
}
template <typename octet_iterator>
uint32_t prior(octet_iterator& it, octet_iterator start)
{
// can't do much if it == start
if (it == start)
throw not_enough_room();
octet_iterator end = it;
// Go back until we hit either a lead octet or start
while (utf8::internal::is_trail(*(--it)))
if (it == start)
throw invalid_utf8(*it); // error - no lead byte in the sequence
return utf8::peek_next(it, end);
}
/// Deprecated in versions that include "prior"
template <typename octet_iterator>
uint32_t previous(octet_iterator& it, octet_iterator pass_start)
{
octet_iterator end = it;
while (utf8::internal::is_trail(*(--it)))
if (it == pass_start)
throw invalid_utf8(*it); // error - no lead byte in the sequence
octet_iterator temp = it;
return utf8::next(temp, end);
}
template <typename octet_iterator, typename distance_type>
void advance (octet_iterator& it, distance_type n, octet_iterator end)
{
for (distance_type i = 0; i < n; ++i)
utf8::next(it, end);
}
template <typename octet_iterator>
typename std::iterator_traits<octet_iterator>::difference_type
distance (octet_iterator first, octet_iterator last)
{
typename std::iterator_traits<octet_iterator>::difference_type dist;
for (dist = 0; first < last; ++dist)
utf8::next(first, last);
return dist;
}
template <typename u16bit_iterator, typename octet_iterator>
octet_iterator utf16to8 (u16bit_iterator start, u16bit_iterator end, octet_iterator result)
{
while (start != end) {
uint32_t cp = utf8::internal::mask16(*start++);
// Take care of surrogate pairs first
if (utf8::internal::is_lead_surrogate(cp)) {
if (start != end) {
uint32_t trail_surrogate = utf8::internal::mask16(*start++);
if (utf8::internal::is_trail_surrogate(trail_surrogate))
cp = (cp << 10) + trail_surrogate + internal::SURROGATE_OFFSET;
else
throw invalid_utf16(static_cast<uint16_t>(trail_surrogate));
}
else
throw invalid_utf16(static_cast<uint16_t>(cp));
}
// Lone trail surrogate
else if (utf8::internal::is_trail_surrogate(cp))
throw invalid_utf16(static_cast<uint16_t>(cp));
result = utf8::append(cp, result);
}
return result;
}
template <typename u16bit_iterator, typename octet_iterator>
u16bit_iterator utf8to16 (octet_iterator start, octet_iterator end, u16bit_iterator result)
{
while (start < end) {
uint32_t cp = utf8::next(start, end);
if (cp > 0xffff) { //make a surrogate pair
*result++ = static_cast<uint16_t>((cp >> 10) + internal::LEAD_OFFSET);
*result++ = static_cast<uint16_t>((cp & 0x3ff) + internal::TRAIL_SURROGATE_MIN);
}
else
*result++ = static_cast<uint16_t>(cp);
}
return result;
}
template <typename octet_iterator, typename u32bit_iterator>
octet_iterator utf32to8 (u32bit_iterator start, u32bit_iterator end, octet_iterator result)
{
while (start != end)
result = utf8::append(*(start++), result);
return result;
}
template <typename octet_iterator, typename u32bit_iterator>
u32bit_iterator utf8to32 (octet_iterator start, octet_iterator end, u32bit_iterator result)
{
while (start < end)
(*result++) = utf8::next(start, end);
return result;
}
// The iterator class
template <typename octet_iterator>
class iterator : public std::iterator <std::bidirectional_iterator_tag, uint32_t> {
octet_iterator it;
octet_iterator range_start;
octet_iterator range_end;
public:
iterator () {}
explicit iterator (const octet_iterator& octet_it,
const octet_iterator& rangestart,
const octet_iterator& rangeend) :
it(octet_it), range_start(rangestart), range_end(rangeend)
{
if (it < range_start || it > range_end)
throw std::out_of_range("Invalid utf-8 iterator position");
}
// the default "big three" are OK
octet_iterator base () const { return it; }
uint32_t operator * () const
{
octet_iterator temp = it;
return utf8::next(temp, range_end);
}
bool operator == (const iterator& rhs) const
{
if (range_start != rhs.range_start || range_end != rhs.range_end)
throw std::logic_error("Comparing utf-8 iterators defined with different ranges");
return (it == rhs.it);
}
bool operator != (const iterator& rhs) const
{
return !(operator == (rhs));
}
iterator& operator ++ ()
{
utf8::next(it, range_end);
return *this;
}
iterator operator ++ (int)
{
iterator temp = *this;
utf8::next(it, range_end);
return temp;
}
iterator& operator -- ()
{
utf8::prior(it, range_start);
return *this;
}
iterator operator -- (int)
{
iterator temp = *this;
utf8::prior(it, range_start);
return temp;
}
}; // class iterator
} // namespace utf8
#endif //header guard

View File

@@ -1,332 +0,0 @@
// Copyright 2006 Nemanja Trifunovic
/*
Permission is hereby granted, free of charge, to any person or organization
obtaining a copy of the software and accompanying documentation covered by
this license (the "Software") to use, reproduce, display, distribute,
execute, and transmit the Software, and to prepare derivative works of the
Software, and to permit third-parties to whom the Software is furnished to
do so, all subject to the following:
The copyright notices in the Software and this entire statement, including
the above license grant, this restriction and the following disclaimer,
must be included in all copies of the Software, in whole or in part, and
all derivative works of the Software, unless such copies or derivative
works are solely in the form of machine-executable object code generated by
a source language processor.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
*/
#ifndef UTF8_FOR_CPP_CORE_H_2675DCD0_9480_4c0c_B92A_CC14C027B731
#define UTF8_FOR_CPP_CORE_H_2675DCD0_9480_4c0c_B92A_CC14C027B731
#include <iterator>
namespace utf8
{
// The typedefs for 8-bit, 16-bit and 32-bit unsigned integers
// You may need to change them to match your system.
// These typedefs have the same names as ones from cstdint, or boost/cstdint
typedef unsigned char uint8_t;
typedef unsigned short uint16_t;
typedef unsigned int uint32_t;
// Helper code - not intended to be directly called by the library users. May be changed at any time
namespace internal
{
// Unicode constants
// Leading (high) surrogates: 0xd800 - 0xdbff
// Trailing (low) surrogates: 0xdc00 - 0xdfff
const uint16_t LEAD_SURROGATE_MIN = 0xd800u;
const uint16_t LEAD_SURROGATE_MAX = 0xdbffu;
const uint16_t TRAIL_SURROGATE_MIN = 0xdc00u;
const uint16_t TRAIL_SURROGATE_MAX = 0xdfffu;
const uint16_t LEAD_OFFSET = LEAD_SURROGATE_MIN - (0x10000 >> 10);
const uint32_t SURROGATE_OFFSET = 0x10000u - (LEAD_SURROGATE_MIN << 10) - TRAIL_SURROGATE_MIN;
// Maximum valid value for a Unicode code point
const uint32_t CODE_POINT_MAX = 0x0010ffffu;
template<typename octet_type>
inline uint8_t mask8(octet_type oc)
{
return static_cast<uint8_t>(0xff & oc);
}
template<typename u16_type>
inline uint16_t mask16(u16_type oc)
{
return static_cast<uint16_t>(0xffff & oc);
}
template<typename octet_type>
inline bool is_trail(octet_type oc)
{
return ((utf8::internal::mask8(oc) >> 6) == 0x2);
}
template <typename u16>
inline bool is_lead_surrogate(u16 cp)
{
return (cp >= LEAD_SURROGATE_MIN && cp <= LEAD_SURROGATE_MAX);
}
template <typename u16>
inline bool is_trail_surrogate(u16 cp)
{
return (cp >= TRAIL_SURROGATE_MIN && cp <= TRAIL_SURROGATE_MAX);
}
template <typename u16>
inline bool is_surrogate(u16 cp)
{
return (cp >= LEAD_SURROGATE_MIN && cp <= TRAIL_SURROGATE_MAX);
}
template <typename u32>
inline bool is_code_point_valid(u32 cp)
{
return (cp <= CODE_POINT_MAX && !utf8::internal::is_surrogate(cp));
}
template <typename octet_iterator>
inline typename std::iterator_traits<octet_iterator>::difference_type
sequence_length(octet_iterator lead_it)
{
uint8_t lead = utf8::internal::mask8(*lead_it);
if (lead < 0x80)
return 1;
else if ((lead >> 5) == 0x6)
return 2;
else if ((lead >> 4) == 0xe)
return 3;
else if ((lead >> 3) == 0x1e)
return 4;
else
return 0;
}
template <typename octet_difference_type>
inline bool is_overlong_sequence(uint32_t cp, octet_difference_type length)
{
if (cp < 0x80) {
if (length != 1)
return true;
}
else if (cp < 0x800) {
if (length != 2)
return true;
}
else if (cp < 0x10000) {
if (length != 3)
return true;
}
return false;
}
enum utf_error {UTF8_OK, NOT_ENOUGH_ROOM, INVALID_LEAD, INCOMPLETE_SEQUENCE, OVERLONG_SEQUENCE, INVALID_CODE_POINT};
/// Helper for get_sequence_x
template <typename octet_iterator>
utf_error increase_safely(octet_iterator& it, octet_iterator end)
{
if (++it == end)
return NOT_ENOUGH_ROOM;
if (!utf8::internal::is_trail(*it))
return INCOMPLETE_SEQUENCE;
return UTF8_OK;
}
#define UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(IT, END) {utf_error ret = increase_safely(IT, END); if (ret != UTF8_OK) return ret;}
/// get_sequence_x functions decode utf-8 sequences of the length x
template <typename octet_iterator>
utf_error get_sequence_1(octet_iterator& it, octet_iterator end, uint32_t& code_point)
{
if (it == end)
return NOT_ENOUGH_ROOM;
code_point = utf8::internal::mask8(*it);
return UTF8_OK;
}
template <typename octet_iterator>
utf_error get_sequence_2(octet_iterator& it, octet_iterator end, uint32_t& code_point)
{
if (it == end)
return NOT_ENOUGH_ROOM;
code_point = utf8::internal::mask8(*it);
UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(it, end)
code_point = ((code_point << 6) & 0x7ff) + ((*it) & 0x3f);
return UTF8_OK;
}
template <typename octet_iterator>
utf_error get_sequence_3(octet_iterator& it, octet_iterator end, uint32_t& code_point)
{
if (it == end)
return NOT_ENOUGH_ROOM;
code_point = utf8::internal::mask8(*it);
UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(it, end)
code_point = ((code_point << 12) & 0xffff) + ((utf8::internal::mask8(*it) << 6) & 0xfff);
UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(it, end)
code_point += (*it) & 0x3f;
return UTF8_OK;
}
template <typename octet_iterator>
utf_error get_sequence_4(octet_iterator& it, octet_iterator end, uint32_t& code_point)
{
if (it == end)
return NOT_ENOUGH_ROOM;
code_point = utf8::internal::mask8(*it);
UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(it, end)
code_point = ((code_point << 18) & 0x1fffff) + ((utf8::internal::mask8(*it) << 12) & 0x3ffff);
UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(it, end)
code_point += (utf8::internal::mask8(*it) << 6) & 0xfff;
UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(it, end)
code_point += (*it) & 0x3f;
return UTF8_OK;
}
#undef UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR
template <typename octet_iterator>
utf_error validate_next(octet_iterator& it, octet_iterator end, uint32_t& code_point)
{
if (it == end)
return NOT_ENOUGH_ROOM;
// Save the original value of it so we can go back in case of failure
// Of course, it does not make much sense with i.e. stream iterators
octet_iterator original_it = it;
uint32_t cp = 0;
// Determine the sequence length based on the lead octet
typedef typename std::iterator_traits<octet_iterator>::difference_type octet_difference_type;
const octet_difference_type length = utf8::internal::sequence_length(it);
// Get trail octets and calculate the code point
utf_error err = UTF8_OK;
switch (length) {
case 0:
return INVALID_LEAD;
case 1:
err = utf8::internal::get_sequence_1(it, end, cp);
break;
case 2:
err = utf8::internal::get_sequence_2(it, end, cp);
break;
case 3:
err = utf8::internal::get_sequence_3(it, end, cp);
break;
case 4:
err = utf8::internal::get_sequence_4(it, end, cp);
break;
}
if (err == UTF8_OK) {
// Decoding succeeded. Now, security checks...
if (utf8::internal::is_code_point_valid(cp)) {
if (!utf8::internal::is_overlong_sequence(cp, length)){
// Passed! Return here.
code_point = cp;
++it;
return UTF8_OK;
}
else
err = OVERLONG_SEQUENCE;
}
else
err = INVALID_CODE_POINT;
}
// Failure branch - restore the original value of the iterator
it = original_it;
return err;
}
template <typename octet_iterator>
inline utf_error validate_next(octet_iterator& it, octet_iterator end) {
uint32_t ignored;
return utf8::internal::validate_next(it, end, ignored);
}
} // namespace internal
/// The library API - functions intended to be called by the users
// Byte order mark
const uint8_t bom[] = {0xef, 0xbb, 0xbf};
template <typename octet_iterator>
octet_iterator find_invalid(octet_iterator start, octet_iterator end)
{
octet_iterator result = start;
while (result != end) {
utf8::internal::utf_error err_code = utf8::internal::validate_next(result, end);
if (err_code != internal::UTF8_OK)
return result;
}
return result;
}
template <typename octet_iterator>
inline bool is_valid(octet_iterator start, octet_iterator end)
{
return (utf8::find_invalid(start, end) == end);
}
template <typename octet_iterator>
inline bool starts_with_bom (octet_iterator it, octet_iterator end)
{
return (
((it != end) && (utf8::internal::mask8(*it++)) == bom[0]) &&
((it != end) && (utf8::internal::mask8(*it++)) == bom[1]) &&
((it != end) && (utf8::internal::mask8(*it)) == bom[2])
);
}
//Deprecated in release 2.3
template <typename octet_iterator>
inline bool is_bom (octet_iterator it)
{
return (
(utf8::internal::mask8(*it++)) == bom[0] &&
(utf8::internal::mask8(*it++)) == bom[1] &&
(utf8::internal::mask8(*it)) == bom[2]
);
}
} // namespace utf8
#endif // header guard

1
3rdparty/utfcpp vendored Submodule

Submodule 3rdparty/utfcpp added at df857efc5b

View File

@@ -1,3 +1,223 @@
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 mostly source-compatible
with the latest 1.x release if no deprecated features are used.
Simple applications should build without changes, more complex
applications (e.g. extending classes of TagLib) will have to be adapted.
* Requires a C++17 compiler and uses features of C++17.
* Major code cleanup, fixed warnings issued by compilers and static analyzers.
* Made methods virtual which should have been virtual but could not be
changed to keep binary compatibility, remove related workarounds.
* Removed deprecated functions:
- APE::Item::Item(const String &, const String &)
- APE::Item::toStringList(): Use values()
- APE::Item::value(): Use binaryData()
- ASF::Properties::setLength()
- ByteVector::checksum()
- ByteVector::isNull(): Use isEmpty()
- ByteVector::null
- FLAC::File::setID3v2FrameFactory()
- FLAC::File::streamInfoData()
- FLAC::File::streamLength()
- FLAC::Properties::Properties(File *, ReadStyle)
- FLAC::Properties::sampleWidth(): Use bitsPerSample()
- File::isReadable(): Use system functions
- File::isWritable(): Use system functions
- FileName::str()
- FileRef::create(): Use constructor
- MP4::Tag::itemListMap(): Use itemMap()
- MPC::File::remove(): Use strip()
- MPC::Properties::Properties(const ByteVector &, long, ReadStyle)
- MPEG::File::save(int, ...): Use overload
- MPEG::File::setID3v2FrameFactory(): Use constructor
- MPEG::ID3v2::Frame::Header::Header(const ByteVector &, bool)
- MPEG::ID3v2::Frame::Header::frameAlterPreservation(): Use
fileAlterPreservation()
- MPEG::ID3v2::Frame::Header::setData(const ByteVector &, bool)
- MPEG::ID3v2::Frame::Header::size(unsigned int): Use size()
- MPEG::ID3v2::Frame::Header::unsycronisation(): use unsynchronisation()
- MPEG::ID3v2::Frame::checkEncoding(const StringList &, String::Type): Use
checkTextEncoding(const StringList &, String::Type)
- MPEG::ID3v2::Frame::headerSize(): Use Header::size()
- MPEG::ID3v2::Frame::headerSize(unsigned int): Use
Header::size(unsigned int)
- MPEG::ID3v2::FrameFactory::createFrame(const ByteVector &, bool)
- MPEG::ID3v2::FrameFactory::createFrame(const ByteVector &, unsigned int):
Use createFrame(const ByteVector &, const Header *)
- MPEG::ID3v2::RelativeVolumeFrame::channelType()
- MPEG::ID3v2::RelativeVolumeFrame::peakVolume(): Use peakVolume(ChannelType)
- MPEG::ID3v2::RelativeVolumeFrame::setChannelType()
- MPEG::ID3v2::RelativeVolumeFrame::setPeakVolume(const PeakVolume &): Use
setPeakVolume(const PeakVolume &, ChannelType)
- MPEG::ID3v2::RelativeVolumeFrame::setVolumeAdjustment(float): Use
setVolumeAdjustment(float, ChannelType)
- MPEG::ID3v2::RelativeVolumeFrame::setVolumeAdjustmentIndex(short): Use
setVolumeAdjustmentIndex(short, ChannelType)
- MPEG::ID3v2::RelativeVolumeFrame::volumeAdjustment(): Use
volumeAdjustment(ChannelType)
- MPEG::ID3v2::RelativeVolumeFrame::volumeAdjustmentIndex(): Use
volumeAdjustmentIndex(ChannelType)
- MPEG::ID3v2::Tag::footer()
- MPEG::ID3v2::Tag::render(int): Use render(Version)
- MPEG::XingHeader::xingHeaderOffset()
- Ogg::Page::getCopyWithNewPageSequenceNumber()
- Ogg::XiphComment::removeField(): Use removeFields()
- PropertyMap::unsupportedData(): Returns now const reference, use
addUnsupportedData() to add keys
- RIFF::AIFF::Properties::Properties(const ByteVector &, ReadStyle)
- RIFF::AIFF::Properties::Properties(const ByteVector &, int, ReadStyle)
- RIFF::AIFF::Properties::sampleWidth(): Use bitsPerSample()
- RIFF::WAV::File::save(TagTypes, bool, int): Use
save(TagTypes, StripTags, Version)
- RIFF::WAV::File::tag(): Returns now a TagUnion, use ID3v2Tag() to get an
ID3v2::Tag
- String::isNull(): Use isEmpty()
- String::null
- TrueAudio::File::setID3v2FrameFactory(): Use constructor
- WavPack::Properties::Properties(const ByteVector &, long, ReadStyle)
* Made methods const: Frame::Header::size(), Frame::headerSize(),
MP4::Atom::findall(), MP4::Atoms::find(), MP4::Atoms::path().
* Made classes non-virtual: APE::Footer, APE::Item, ASF::Attribute,
ASF::Picture, MP4::CoverArt, MP4::Item, ID3v2::ExtendedHeader, ID3v2::Footer,
ID3v2::Header, MPEG::Header, MPEG::XingHeader, Ogg::Page, Ogg::PageHeader.
* Removed type definitions in TagLib namespace: wchar, uchar, ushort, uint,
ulong, ulonglong, wstring: Use the standard types.
* Removed include file taglib_config.h and its defines TAGLIB_WITH_ASF,
TAGLIB_WITH_MP4: They were always 1 since version 1.8.
* Behavioral changes:
- The basic tag methods (e.g. genre()) separate multiple values with " / "
instead of " ".
- The stream operator for String uses UTF-8 instead of ISO-8859-1 encoding.
- MP4 property ORIGINALDATE is mapped to "----:com.apple.iTunes:ORIGINALDATE"
instead of "----:com.apple.iTunes:originaldate".
- MP4 property ENCODEDBY is mapped to "©enc" instead of "©too", which is now
mapped to ENCODING.
* Unified interface for complex properties like pictures.
* Simplified the unified properties interface by providing its methods on
FileRef.
* C bindings: Support for properties (taglib_property_...) and complex
properties like cover art (taglib_complex_property_...), memory I/O streams.
* Support for Direct Stream Digital (DSD) stream files (DSF) and interchange
file format (DSDIFF, DFF), ADTS (AAC) files.
* The runtime version can be queried.
* Additional utility functions ByteVector::fromUShort(),
ByteVector::fromULongLong(), ByteVector::toULongLong(),
ByteVector::toULongLong(), List::sort().
* Fixed List::setAutoDelete() affecting implicitly shared copies.
* Build system: Direct support for CMake, find_package(TagLib) exports target
TagLib::tag.
* Build system: Fixed PackageConfig to support both relative and absolute paths.
* Build system: utf8cpp is no longer included, it can be provided via a system
package or a Git submodule.
* ASF: Support additional properties ARTISTWEBPAGE, ENCODING, ENCODINGTIME,
FILEWEBPAGE, INITIALKEY, ORIGINALALBUM, ORIGINALARTIST, ORIGINALFILENAME,
ORIGINALLYRICIST.
* ID3v2: Fixed extensibility of FrameFactory, use it also for WAV and AIFF
files.
* MP4: Support additional properties OWNER, RELEASEDATE.
* MP4: Introduced ItemFactory allowing clients to support new atom types.
* MP4: Detect duration from mvhd atom if not present in mdhd atom.
* MP4: Fixed type of hdvd atom to be integer instead of boolean.
* MP4: Tolerate trailing garbage in M4A files.
* MPC: Fixed content check in presence of an ID3v2 tag.
* MPEG: Do not scan full file for ID3v2 tag when ReadStyle Fast is used.
* RIFF: Support properties ALBUM, ARRANGER, ARTIST, ARTISTWEBPAGE, BPM,
COMMENT, COMPOSER, COPYRIGHT, DATE, DISCSUBTITLE, ENCODEDBY, ENCODING,
ENCODINGTIME, GENRE, ISRC, LABEL, LANGUAGE, LYRICIST, MEDIA, PERFORMER,
RELEASECOUNTRY, REMIXER, TITLE, TRACKNUMBER.
* WAV: Fixed crash with files having the "id3 " chunk as the only valid chunk.
* Windows: Fixed support for files larger than 2GB.
TagLib 1.13.1 (Jul 1, 2023)
===========================
* Fixed parsing of TXXX frames without description.
* Detect MP4 atoms with invalid length or type.
* Do not miss ID3v2 frames when an extended header is present.
* Use property "DISCSUBTITLE" for ID3v2 "TSST" frame.
* Build system improvements: Use absolute path for macOS dylib install name,
support --define-prefix when using pkg-config, fixed minimum required
CppUnit version.
* Code clean up using clang-tidy.
TagLib 1.13 (Oct 27, 2022)
==========================
* Added interface StreamTypeResolver to support streams which cannot be
fopen()'ed, e.g. network files.
* Added MP4::File::strip() to remove meta atom from MP4 file.
* Added Map::value() to look up without creating entry.
* Use property "WORK" instead of "CONTENTGROUP" for ID3v2 "TIT1" frame,
use property "WORK" for ASF "WM/ContentGroupDescription",
use property "COMPILATION" for ID3v2 "TCMP" frame.
* Build system improvements: option WITH_ZLIB, BUILD_TESTING instead of
BUILD_TESTS, GNUInstallDirs, FeatureSummary, tests with BUILD_SHARED_LIBS,
cross compilation with Buildroot, systems without HAVE_GCC_ATOMIC, Clang.
* Fixed heap-buffer-overflows when handling ASF, APE, FLAC, ID3v2, MP4, MPC
tags.
* Fixed detection of invalid file by extension when correct type can be
detected by contents.
* Fixed unnecessary creation of map entries in APE and FLAC tags if looked up
tag does not exist.
* Fixed parsing of MP4 non-full meta atoms.
* Fixed potential ID3v1 false positive in the presence of an APE tag.
* Fixed ID3v2 version handling for frames embedded in CHAP or CTOC frames.
* Fixed parsing of multiple strings with a single BOM in ID3v2.4.0.
* Fixed several smaller issues reported by clang-tidy.
TagLib 1.12 (Feb 16, 2021)
==========================
@@ -183,7 +403,7 @@ TagLib 1.8 (Sep 6, 2012)
* Added support for OWNE ID3 frames.
* Changed key validation in the new PropertyMap API.
* ID3v1::Tag::setStringHandler will no londer delete the previous handler,
* ID3v1::Tag::setStringHandler will no longer delete the previous handler,
the caller is responsible for this.
* File objects will also no longer delete the passed IOStream objects. It
should be done in the caller code after the File object is no longer
@@ -326,7 +546,7 @@ TagLib 1.6 (Sep 13, 2009)
and in String::operator+=. (BUG:169389)
* Added support for PRIV ID3v2 frames.
* Empty ID3v2 genres are no longer treated as numeric ID3v1 genres.
* Added support for the POPM (rating/playcount) ID3v2 frame.
* Added support for the POPM (rating/play count) ID3v2 frame.
* Generic RIFF file format support:
* Support for AIFF files with ID3v2 tags.
* Support for WAV files with ID3v2 tags.

View File

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

View File

@@ -20,7 +20,7 @@ if(NOT ${SIZEOF_LONGLONG} EQUAL 8)
endif()
check_type_size("wchar_t" SIZEOF_WCHAR_T)
if(${SIZEOF_WCHAR_T} LESS 2)
if(NOT ${SIZEOF_WCHAR_T} GREATER 1)
message(FATAL_ERROR "TagLib requires that wchar_t is sufficient to store a UTF-16 char.")
endif()
@@ -34,53 +34,6 @@ if(NOT ${SIZEOF_DOUBLE} EQUAL 8)
message(FATAL_ERROR "TagLib requires that double is 64-bit wide.")
endif()
# Determine which kind of atomic operations your compiler supports.
check_cxx_source_compiles("
int main() {
volatile int x;
__sync_add_and_fetch(&x, 1);
int y = __sync_sub_and_fetch(&x, 1);
return 0;
}
" HAVE_GCC_ATOMIC)
if(NOT HAVE_GCC_ATOMIC)
check_cxx_source_compiles("
#include <libkern/OSAtomic.h>
int main() {
volatile int32_t x;
OSAtomicIncrement32Barrier(&x);
int32_t y = OSAtomicDecrement32Barrier(&x);
return 0;
}
" HAVE_MAC_ATOMIC)
if(NOT HAVE_MAC_ATOMIC)
check_cxx_source_compiles("
#include <windows.h>
int main() {
volatile LONG x;
InterlockedIncrement(&x);
LONG y = InterlockedDecrement(&x);
return 0;
}
" HAVE_WIN_ATOMIC)
if(NOT HAVE_WIN_ATOMIC)
check_cxx_source_compiles("
#include <ia64intrin.h>
int main() {
volatile int x;
__sync_add_and_fetch(&x, 1);
int y = __sync_sub_and_fetch(&x, 1);
return 0;
}
" HAVE_IA64_ATOMIC)
endif()
endif()
endif()
# Determine which kind of byte swap functions your compiler supports.
check_cxx_source_compiles("
@@ -105,7 +58,7 @@ if(NOT HAVE_GCC_BYTESWAP)
if(NOT HAVE_GLIBC_BYTESWAP)
check_cxx_source_compiles("
#include <stdlib.h>
#include <cstdlib>
int main() {
_byteswap_ushort(0);
_byteswap_ulong(0);
@@ -140,32 +93,6 @@ if(NOT HAVE_GCC_BYTESWAP)
endif()
endif()
# Determine whether your compiler supports some safer version of vsprintf.
check_cxx_source_compiles("
#include <cstdio>
#include <cstdarg>
int main() {
char buf[20];
va_list args;
vsnprintf(buf, 20, \"%d\", args);
return 0;
}
" HAVE_VSNPRINTF)
if(NOT HAVE_VSNPRINTF)
check_cxx_source_compiles("
#include <cstdio>
#include <cstdarg>
int main() {
char buf[20];
va_list args;
vsprintf_s(buf, \"%d\", args);
return 0;
}
" HAVE_VSPRINTF_S)
endif()
# Determine whether your compiler supports ISO _strdup.
check_cxx_source_compiles("
@@ -176,28 +103,7 @@ check_cxx_source_compiles("
}
" HAVE_ISO_STRDUP)
# Determine whether zlib is installed.
if(NOT ZLIB_SOURCE)
find_package(ZLIB)
if(ZLIB_FOUND)
set(HAVE_ZLIB 1)
else()
set(HAVE_ZLIB 0)
endif()
endif()
# Determine whether CppUnit is installed.
if(BUILD_TESTS AND NOT BUILD_SHARED_LIBS)
find_package(CppUnit)
if(NOT CppUnit_FOUND)
message(STATUS "CppUnit not found, disabling tests.")
set(BUILD_TESTS OFF)
endif()
endif()
# Detect WinRT mode
if(CMAKE_SYSTEM_NAME STREQUAL "WindowsStore")
set(PLATFORM WINRT 1)
set(PLATFORM_WINRT 1)
endif()

View File

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

View File

@@ -1,5 +1,4 @@
TagLib Installation
===================
# TagLib Installation
TagLib uses the CMake build system. As a user, you will most likely want to
build TagLib in release mode and install it into a system-wide location.
@@ -13,207 +12,508 @@ In order to build the included examples, use the `BUILD_EXAMPLES` option:
cmake -DBUILD_EXAMPLES=ON [...]
See http://www.cmake.org/cmake/help/runningcmake.html for generic help on
running CMake.
If you want to build TagLib without ZLib, you can use
Mac OS X
--------
cmake -DCMAKE_INSTALL_PREFIX=/usr/local -DCMAKE_BUILD_TYPE=Release -DWITH_ZLIB=OFF .
make
sudo make install
On Mac OS X, you might want to build a framework that can be easily integrated
See [cmake(1)](https://cmake.org/cmake/help/latest/manual/cmake.1.html) for
generic help on running CMake.
## Build Options
These are the most important build options. For details, have a look into the
CMakeLists.txt file.
| Option | Description |
|-------------------------|----------------------------------------------------|
| `BUILD_SHARED_LIBS` | Build shared libraries |
| `CMAKE_BUILD_TYPE` | Debug, Release, RelWithDebInfo, MinSizeRel |
| `BUILD_EXAMPLES` | Build examples |
| `BUILD_BINDINGS` | Build C bindings |
| `BUILD_TESTING` | Build unit tests |
| `TRACE_IN_RELEASE` | Enable debug output in release builds |
| `WITH_ZLIB` | Whether to build with ZLib (default ON) |
| `ZLIB_ROOT` | Where to find ZLib's root directory |
| `ZLIB_INCLUDE_DIR` | Where to find ZLib's include directory |
| `ZLIB_LIBRARY` | Where to find ZLib's library |
| `CMAKE_INSTALL_PREFIX` | Where to install Taglib |
| `TAGLIB_INSTALL_SUFFIX` | Suffix added to installed libraries, includes, ... |
| `ENABLE_STATIC_RUNTIME` | Link with MSVC runtime statically |
| `BUILD_FRAMEWORK` | Build a macOS framework |
| `TESTS_DIR` | Where to find unit test data (with data appended) |
| `TESTS_TMPDIR` | Where to create temporary files in unit tests |
If you want to install TagLib 2 alongside TagLib 1, you can use
`-DTAGLIB_INSTALL_SUFFIX=-2` and make sure that `BUILD_EXAMPLES` is not `ON`
for both versions. The installed files will then include bin/taglib-2-config,
include/taglib-2, cmake/taglib-2, pkgconfig/taglib-2.pc,
pkgconfig/taglib_c-2.pc and the libraries have a suffix "-2".
### Compile Time Configuration of Supported Formats
To reduce the size of the library, it is possible to switch off supported file
formats. By default, all formats are enabled. Support for MPEG files (MP3, AAC)
and ID3 tags cannot be disabled. The following CMake options are available:
| Option | Description |
|-------------------------|----------------------------------------------------|
| `WITH_APE` | Build with APE, MPC, WavPack (default ON) |
| `WITH_ASF` | Build with ASF (default ON) |
| `WITH_DSF` | Build with DSF (default ON) |
| `WITH_MATROSKA` | Build with Matroska (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
A required dependency is [utf8cpp](https://github.com/nemtrif/utfcpp). You can
install the corresponding package (libutfcpp-dev on Ubuntu, utf8cpp in Homebrew,
utfcpp in vcpkg) or fetch the Git submodule with `git submodule update --init`.
Optional dependencies are
- [zlib](https://www.zlib.net/): You can disable it with `-DWITH_ZLIB=OFF`,
build and install it from the sources or use a package (zlib1g-dev on Ubuntu,
zlib in vcpkg). It is needed for compressed ID3v2 frames.
- [CppUnit](https://wiki.documentfoundation.org/Cppunit): Is required for unit
tests, which are disabled by default. If you enable them with
`-DBUILD_TESTING=ON`, you can build and install it from the sources or use a
package (libcppunit-dev on Ubuntu, cppunit in Homebrew, cppunit in vcpkg).
If the unit tests are enabled, you can run them with your build tool
(`make check`, `ninja check`) or via CMake
`cmake --build /path/to/build-dir --target check`.
## UNIX (Including Linux, BSD and macOS)
#### Building TagLib
On Linux, you can install the dependencies using the package manager of your
distribution. On macOS with Homebrew, you can use `brew install cppunit utf8cpp`
to install the dependencies.
```
# Adapt these environment variables to your directories
TAGLIB_SRC_DIR=$HOME/projects/taglib/src/taglib
TAGLIB_DST_DIR=$HOME/projects/taglib/src/build
cd $TAGLIB_SRC_DIR
cmake -B $TAGLIB_DST_DIR -DBUILD_SHARED_LIBS=ON -DVISIBILITY_HIDDEN=ON \
-DBUILD_TESTING=ON -DBUILD_EXAMPLES=ON -DBUILD_BINDINGS=ON \
-DCMAKE_BUILD_TYPE=Release
cmake --build $TAGLIB_DST_DIR --config Release
cmake --build $TAGLIB_DST_DIR --config Release --target check
# Install to ~/pkg folder
cmake --install $TAGLIB_DST_DIR --config Release --prefix $HOME/pkg --strip
# Run example from installed package
LD_LIBRARY_PATH=$HOME/pkg/lib $HOME/pkg/bin/tagreader /path/to/audio-file
```
#### Building a Project Using TagLib
As an example for an external application using TagLib, we create a folder
taglib-client and copy the file tagreader.cpp from the examples folder
of the TagLib sources into it. We then add this simple CMakeLists.txt.
```
cmake_minimum_required(VERSION 3.5.0)
project(taglib-client)
set(CMAKE_CXX_STANDARD 17)
find_package(ZLIB)
find_package(TagLib 2.0.0 REQUIRED)
add_executable(tagreader tagreader.cpp)
target_link_libraries(tagreader TagLib::tag)
```
To build into a folder `build` inside this directory, just run
```
# Set this to the path where TagLib is installed
TAGLIB_PREFIX=$HOME/pkg
cmake -B build -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=$TAGLIB_PREFIX
cmake --build build --config Release
build/tagreader /path/to/audio-file
```
If TagLib is installed in a standard location (e.g. /usr, /usr/local), its CMake
configuration is found without setting CMAKE_INSTALL_PREFIX (or alternatives
like CMAKE_PREFIX_PATH, CMAKE_SYSTEM_PREFIX_PATH).
To use the C-bindings with tagreader_c.c, you can change the last two lines in
CMakeLists.txt to
```
add_executable(tagreader_c tagreader_c.c)
target_link_libraries(tagreader_c TagLib::tag_c)
```
#### Building a Project Using pkg-config
Before version 2.0, TagLib did not export CMake configuration, therefore
`pkg-config` could be used. This is still possible.
Note, however, that `pkg-config` makes it more difficult to use packages which
are not installed in a standard location. You can point `pkg-config` to search
in non-standard locations for .pc-files, but they still contain the install
locations defined when building TagLib. Since we did not give a
`CMAKE_INSTALL_PREFIX` in the example above, the default `/usr/local/` is used.
```
PKG_CONFIG_PATH=$HOME/pkg/lib/pkgconfig pkg-config --libs --cflags taglib
-I/usr/local/include -I/usr/local/include/taglib -L/usr/local/lib -ltag -lz
```
The following examples use the same build example with additional CMake
parameters affecting the installation location.
- Using the default prefix `-DCMAKE_INSTALL_PREFIX=/usr/local`:
```
-I/usr/local/include -I/usr/local/include/taglib -L/usr/local/lib -ltag -lz
```
- Using an absolute prefix `-DCMAKE_INSTALL_PREFIX=/usr`:
```
-I/usr/include/taglib -ltag -lz
```
- Using absolute lib and include directories
`-DCMAKE_INSTALL_LIBDIR=/abs-lib -DCMAKE_INSTALL_INCLUDEDIR=/abs-include -DCMAKE_INSTALL_PREFIX=/usr`:
```
-I/abs-include -I/abs-include/taglib -L/abs-lib -ltag -lz
```
- Using relative lib and include directories
`-DCMAKE_INSTALL_LIBDIR=rel-lib -DCMAKE_INSTALL_INCLUDEDIR=rel-include -DCMAKE_INSTALL_PREFIX=/usr`:
```
-I/usr/rel-include -I/usr/rel-include/taglib -L/usr/rel-lib -ltag -lz
```
This is the output of
```
PKG_CONFIG_PATH=$HOME/pkg/rel-lib/pkgconfig pkg-config --libs --cflags taglib
```
You could add `--define-prefix` to the `pkg-config` arguments to have the
effective location `$HOME/pkg/` instead of `/usr/` in the output.
Therefore, the correct paths for our example package would be given when using
the `--define-prefix` feature.
```
PKG_CONFIG_PATH=$HOME/pkg/lib/pkgconfig pkg-config --define-prefix --libs --cflags taglib
```
You can use pkg-config from CMake, however, relocation with `--define-prefix`
is not supported.
```
cmake_minimum_required(VERSION 3.6.0)
project(taglib-client)
find_package(PkgConfig)
pkg_check_modules(TAGLIB REQUIRED IMPORTED_TARGET taglib)
add_executable(tagreader tagreader.cpp)
target_link_libraries(tagreader PkgConfig::TAGLIB)
```
#### Framework on macOS
On macOS, you might want to build a framework that can be easily integrated
into your application. If you set the BUILD_FRAMEWORK option on, it will compile
TagLib as a framework. For example, the following command can be used to build
an Universal Binary framework with Mac OS X 10.4 as the deployment target:
a framework with macOS 10.10 as the deployment target:
cmake -DCMAKE_BUILD_TYPE=Release \
mkdir build; cd build
cmake .. -DCMAKE_BUILD_TYPE=Release \
-DBUILD_TESTING=OFF \
-DBUILD_FRAMEWORK=ON \
-DCMAKE_C_COMPILER=/usr/bin/gcc-4.0 \
-DCMAKE_CXX_COMPILER=/usr/bin/c++-4.0 \
-DCMAKE_OSX_SYSROOT=/Developer/SDKs/MacOSX10.4u.sdk/ \
-DCMAKE_OSX_DEPLOYMENT_TARGET=10.4 \
-DCMAKE_OSX_ARCHITECTURES="ppc;i386;x86_64"
-DCMAKE_OSX_DEPLOYMENT_TARGET=10.10 \
-DCMAKE_OSX_ARCHITECTURES="arm64;x86_64"
make
For a 10.6 Snow Leopard static library with both 32-bit and 64-bit code, use:
For a 10.10 static library, use:
cmake -DCMAKE_BUILD_TYPE=Release \
-DCMAKE_OSX_DEPLOYMENT_TARGET=10.6 \
-DCMAKE_OSX_ARCHITECTURES="i386;x86_64" \
mkdir build; cd build
cmake .. -DCMAKE_BUILD_TYPE=Release \
-DBUILD_TESTING=OFF \
-DBUILD_SHARED_LIBS=OFF \
-DCMAKE_INSTALL_PREFIX="<folder you want to install to>"
-DCMAKE_OSX_DEPLOYMENT_TARGET=10.10 \
-DCMAKE_OSX_ARCHITECTURES="arm64;x86_64"
make
After `make`, and `make install`, add `libtag.` to your XCode project, and add
the include folder to the project's User Header Search Paths.
Windows
-------
## Windows
It's Windows ... Systems vary!
This means you need to adjust things to suit your system, especially paths.
### Using Visual Studio Build Tools
Tested with:
* Microsoft Visual Studio 2010, 2015, 2017
* Microsoft C++ Build Tools 2015, 2017 (standalone packages not requiring Visual Studio)
* GCC by mingw-w64.sf.net v4.6.3 (Strawberry Perl 32b)
* MinGW32-4.8.0
For this example, we assume that you have the Visual Studio Build Tools 2022
installed. Additionally, you need [cmake](https://cmake.org/), which can be
installed for example using [scoop](https://scoop.sh/) with
`scoop install cmake`.
Requirements:
* Tool chain, build environment, whatever ya want to call it ...
Installed and working.
* CMake program. (Available at: www.cmake.org)
Installed and working.
#### Building TagLib (MSVC)
Optional:
* Zlib library.
Available in some tool chains, not all.
Search the web, take your choice.
You can build and install the dependencies
[utf8cpp](https://github.com/nemtrif/utfcpp) and optionally
[zlib](https://www.zlib.net/) and
[CppUnit](https://wiki.documentfoundation.org/Cppunit) yourself, but it is
probably easier using a package manager such as [vcpkg](https://vcpkg.io/),
which can be installed using `scoop install vcpkg` and then install the
dependencies using `vcpkg install utfcpp zlib cppunit`.
Useful configuration options used with CMake (GUI and/or command line):
Any of the `ZLIB_` variables may be used at the command line, `ZLIB_ROOT` is only
available on the command line.
Now you can build TagLib from PowerShell.
| option | description |
| ----------------------- | ----------- |
| `ZLIB_ROOT=` | Where to find ZLib's root directory. Assumes parent of: `\include` and `\lib.`|
| `ZLIB_INCLUDE_DIR=` | Where to find ZLib's Include directory.|
| `ZLIB_LIBRARY=` | Where to find ZLib's Library.
| `ZLIB_SOURCE=` | Where to find ZLib's Source Code. Alternative to `ZLIB_INCLUDE_DIR` and `ZLIB_LIBRARY`.
| `CMAKE_INSTALL_PREFIX=` | Where to install Taglib. |
| `CMAKE_BUILD_TYPE=` | Release, Debug, etc ... (Not available in MSVC) |
```
# Adapt these environment variables to your directories
$env:TAGLIB_SRC_DIR = "${env:HOMEDRIVE}${env:HOMEPATH}/projects/taglib/src/taglib"
$env:TAGLIB_DST_DIR = "${env:HOMEDRIVE}${env:HOMEPATH}/projects/taglib/src/msvs_vcpkg_build"
cd $env:TAGLIB_SRC_DIR
cmake -B $env:TAGLIB_DST_DIR -DBUILD_SHARED_LIBS=ON -DVISIBILITY_HIDDEN=ON `
-DBUILD_TESTING=ON -DBUILD_EXAMPLES=ON -DBUILD_BINDINGS=ON `
-DCMAKE_BUILD_TYPE=Release `
-DCMAKE_TOOLCHAIN_FILE="$env:VCPKG_ROOT/scripts/buildsystems/vcpkg.cmake" `
-G "Visual Studio 17 2022"
cmake --build $env:TAGLIB_DST_DIR --config Release
The easiest way is at the command prompt (Visual C++ command prompt for MSVS users batch file and/or shortcuts are your friends).
# Add directories containing DLL dependencies to path
$env:Path += -join(";$env:TAGLIB_DST_DIR\taglib\Release;",
"$env:TAGLIB_DST_DIR\bindings\c\Release;",
"$env:VCPKG_ROOT\packages\cppunit_x64-windows\bin;",
"$env:VCPKG_ROOT\packages\utfcpp_x64-windows\bin;",
"$env:VCPKG_ROOT\packages\zlib_x64-windows\bin")
cmake --build $env:TAGLIB_DST_DIR --config Release --target check
1. **Build the Makefiles:**
# Install to \pkg folder on current drive
cmake --install $env:TAGLIB_DST_DIR --config Release --prefix /pkg --strip
Replace "GENERATOR" with your needs.
* For MSVS: `Visual Studio XX YYYY`, e.g. `Visual Studio 14 2015`.
# Static library
$env:TAGLIB_DST_DIR = "C:/Users/fle/projects/taglib/src/msvs_vcpkg_static_build"
cmake -B $env:TAGLIB_DST_DIR -DBUILD_SHARED_LIBS=OFF -DVISIBILITY_HIDDEN=ON `
-DBUILD_TESTING=ON -DBUILD_EXAMPLES=ON -DBUILD_BINDINGS=ON `
-DCMAKE_BUILD_TYPE=Release `
-DCMAKE_TOOLCHAIN_FILE="$env:VCPKG_ROOT/scripts/buildsystems/vcpkg.cmake" `
-G "Visual Studio 17 2022"
cmake --build $env:TAGLIB_DST_DIR --config Release
**Note**: As Visual Studio 2017 supports CMake, you can skip this step and open the taglib
folder in VS instead.
* For MinGW: `MinGW Makefiles`
cmake --build $env:TAGLIB_DST_DIR --config Release --target check
C:\GitRoot\taglib> cmake -G "GENERATOR" -DCMAKE_INSTALL_PREFIX=C:\Libraries\taglib
Or use the CMake GUI:
1. Open CMake GUI.
2. Set paths: *Where is the source code* and *Where to build the binaries*.
In the example, both would be: `C:\GitRoot\taglib`
3. Tick: Advanced
4. Select: Configure
5. Select: Generator
6. Tick: Use default native compilers
7. Select: Finish
Wait until done.
8. If using ZLib, Scroll down.
(to the bottom of the list of options ... should go over them all)
1. Edit: `ZLIB_INCLUDE_DIR`
2. Edit: `ZLIB_LIBRARY`
9. Select: Generate
2. **Build the project**
* MSVS:
C:\GitRoot\taglib> msbuild all_build.vcxproj /p:Configuration=Release
OR (Depending on MSVS version or personal choice)
C:\GitRoot\taglib> devenv all_build.vcxproj /build Release
OR in the MSVS GUI:
1. Open MSVS.
2. Open taglib solution.
3. Set build type to: Release (look in the tool bars)
2. Hit F7 to build the solution. (project)
* MinGW:
C:\GitRoot\taglib> gmake
OR (Depending on MinGW install)
C:\GitRoot\taglib> mingw32-make
3. **Install the project**
(Change `install` to `uninstall` to uninstall the project)
* MSVS:
C:\GitRoot\taglib> msbuild install.vcxproj
OR (Depending on MSVC version or personal choice)
C:\GitRoot\taglib> devenv install.vcxproj
Or in the MSVS GUI:
1. Open project.
2. Open Solution Explorer.
3. Right Click: INSTALL
4. Select: Project Only
5. Select: Build Only INSTALL
* MinGW:
C:\GitRoot\taglib> gmake install
OR (Depending on MinGW install)
C:\GitRoot\taglib> mingw32-make install
To build a static library, set the following two options with CMake:
-DBUILD_SHARED_LIBS=OFF -DENABLE_STATIC_RUNTIME=ON
# Install to \pkg_static folder on current drive
cmake --install $env:TAGLIB_DST_DIR --config Release --prefix /pkg_static --strip
```
Including `ENABLE_STATIC_RUNTIME=ON` indicates you want TagLib built using the
static runtime library, rather than the DLL form of the runtime.
Unit Tests
----------
#### Building a Project Using TagLib (MSVC)
If you want to run the test suite to make sure TagLib works properly on your
system, you need to have cppunit installed. To build the tests, include
the option `-DBUILD_TESTS=ON -DBUILD_SHARED_LIBS=OFF` when running cmake.
As an example for an external application using TagLib, we create a folder
taglib-client and copy the file tagreader.cpp from the examples folder
of the TagLib sources into it. We then add this simple CMakeLists.txt.
The test suite has a custom target in the build system, so you can run
the tests using make:
```
cmake_minimum_required(VERSION 3.5.0)
project(taglib-client)
set(CMAKE_CXX_STANDARD 17)
find_package(ZLIB)
find_package(TagLib 2.0.0 REQUIRED)
add_executable(tagreader tagreader.cpp)
target_link_libraries(tagreader TagLib::tag)
```
make check
To build into a folder build inside this directory, just run
Windows MinGW:
```
# Set this to the path where TagLib is installed
$env:TAGLIB_PREFIX = "/pkg"
cmake -B build -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX="$env:TAGLIB_PREFIX" `
-DCMAKE_TOOLCHAIN_FILE="$env:VCPKG_ROOT/scripts/buildsystems/vcpkg.cmake" `
-G "Visual Studio 17 2022"
cmake --build build --config Release
* Get cppunit from https://www.freedesktop.org/wiki/Software/cppunit/
* Build it for MinGW:
- `./autogen.sh`
- `./configure --disable-shared`
- `mingw32-make; mingw32-make install DESTDIR=/path/to/install/dir`
* Build TagLib with testing enabled:
- ```
cmake -G "MinGW Makefiles" -DBUILD_TESTS=ON -DBUILD_EXAMPLES=ON -DBUILD_SHARED_LIBS=OFF \
-DCPPUNIT_INCLUDE_DIR=/path/to/cppunit/include \
-DCPPUNIT_LIBRARIES=/path/to/cppunit/lib/libcppunit.a \
-DCPPUNIT_INSTALLED_VERSION=1.15.1
```
- `mingw32-make`
- `mingw32-make check`
# Add directories containing DLL dependencies to path
$env:Path += ";$env:TAGLIB_PREFIX\bin;$env:VCPKG_ROOT\packages\zlib_x64-windows\bin"
build\Release\tagreader /path/to/audio-file
```
Windows MSVS:
To use the C-bindings with tagreader_c.c, you can change the last two lines in
CMakeLists.txt to
* Get cppunit from https://www.freedesktop.org/wiki/Software/cppunit/
* Build it in Visual Studio:
- Open examples/examples2008.sln, convert all, do not overwrite.
- Set architecture to x64, build configuration, e.g. Debug DLL.
- It may fail, but the needed libraries should be available in src\cppunit\DebugDll.
* Build TagLib with testing enabled:
- ```
cmake -G "Visual Studio 16 2019" -DCMAKE_BUILD_TYPE=Debug -DBUILD_TESTS=ON -DBUILD_EXAMPLES=ON
-DBUILD_SHARED_LIBS=OFF -DENABLE_STATIC_RUNTIME=ON
-DCPPUNIT_INCLUDE_DIR=\path\to\cppunit\include
-DCPPUNIT_LIBRARIES=\path\to\cppunit\DebugDll\cppunitd_dll.lib
-DCPPUNIT_INSTALLED_VERSION=1.15.1
```
- `msbuild all_build.vcxproj /p:Configuration=Debug`
- `path %path%;\path\to\cppunit\DebugDll`
- `tests\Debug\test_runner.exe`
```
add_executable(tagreader_c tagreader_c.c)
target_link_libraries(tagreader_c TagLib::tag_c)
```
macOS:
If you link against a static TagLib, you have to adapt the installation path
(e.g. "/pkg_static") and add the following line to CMakeLists.txt
* Install cppunit using a package manager, e.g. `brew install cppunit`
* Build TagLib with testing enabled:
- `cmake -DBUILD_TESTS=ON -DBUILD_EXAMPLES=ON -DBUILD_SHARED_LIBS=OFF`
- `make`
- `make check`
```
target_compile_definitions(tagreader_c PRIVATE TAGLIB_STATIC)
```
### Using MSYS2
#### Building TagLib (MSYS2)
To build TagLib using Clang from MSYS, install [msys2](https://www.msys2.org/),
then start the MSYS CLANG64 shell and install the dependencies as they are
specified in the [MSYS recipe for TagLib](
https://packages.msys2.org/package/mingw-w64-clang-x86_64-taglib?repo=clang64).
```
pacman -Suy
pacman -S mingw-w64-clang-x86_64-gcc-libs mingw-w64-clang-x86_64-zlib \
mingw-w64-clang-x86_64-cc mingw-w64-clang-x86_64-cmake \
mingw-w64-clang-x86_64-cppunit mingw-w64-clang-x86_64-ninja
```
Then you can build TagLib from the MSYS CLANG64 Bash.
```
# Adapt these environment variables to your directories
TAGLIB_SRC_DIR=$HOME/projects/taglib/src/taglib
TAGLIB_DST_DIR=$HOME/projects/taglib/src/msys_build
cd $TAGLIB_SRC_DIR
cmake -B $TAGLIB_DST_DIR -DBUILD_SHARED_LIBS=ON -DVISIBILITY_HIDDEN=ON \
-DBUILD_TESTING=ON -DBUILD_EXAMPLES=ON -DBUILD_BINDINGS=ON \
-DCMAKE_BUILD_TYPE=Release -G Ninja
cmake --build $TAGLIB_DST_DIR --config Release
PATH=$PATH:/clang64/bin:$TAGLIB_DST_DIR/taglib:$TAGLIB_DST_DIR/bindings/c \
cmake --build $TAGLIB_DST_DIR --config Release --target check
# Install to /pkg_msys folder inside MSYS root (e.g. C:/msys64/pkg_msys/)
cmake --install $TAGLIB_DST_DIR --config Release --prefix /pkg_msys --strip
```
#### Building a Project Using TagLib (MSYS2)
As an example for an external application using TagLib, we create a folder
taglib-client and copy the file tagreader.cpp from the examples folder
of the TagLib sources into it. We then add this simple CMakeLists.txt.
```
cmake_minimum_required(VERSION 3.5.0)
project(taglib-client)
set(CMAKE_CXX_STANDARD 17)
find_package(ZLIB)
find_package(TagLib 2.0.0 REQUIRED)
add_executable(tagreader tagreader.cpp)
target_link_libraries(tagreader TagLib::tag)
```
To build into a folder build_msys inside this directory, just run
```
# Set this to the path where TagLib is installed
TAGLIB_PREFIX=/pkg_msys
cmake -B build_msys -DCMAKE_BUILD_TYPE=Release \
-DCMAKE_INSTALL_PREFIX=$TAGLIB_PREFIX -G Ninja
cmake --build build_msys --config Release
PATH=$PATH:$TAGLIB_PREFIX/bin
build_msys/tagreader /path/to/audio-file
```
To use the C-bindings with tagreader_c.c, you can change the last two lines in
CMakeLists.txt to
```
add_executable(tagreader_c tagreader_c.c)
target_link_libraries(tagreader_c TagLib::tag_c)
```
### Using MinGW
The instructions for MSYS2 can also be used to build with MinGW. You could use
Git Bash together with the MinGW provided by Qt.
```
# Adapt these environment variables to your directories
PATH=$PATH:/c/Qt/Tools/mingw1120_64/bin
TAGLIB_SRC_DIR=$HOME/projects/taglib/src/taglib
TAGLIB_DST_DIR=$HOME/projects/taglib/src/mingw_build
cd $TAGLIB_SRC_DIR
cmake -B $TAGLIB_DST_DIR -DBUILD_SHARED_LIBS=ON -DVISIBILITY_HIDDEN=ON \
-DBUILD_EXAMPLES=ON -DBUILD_BINDINGS=ON -DWITH_ZLIB=OFF \
-DCMAKE_BUILD_TYPE=Release -G 'MinGW Makefiles'
cmake --build $TAGLIB_DST_DIR --config Release
PATH=$PATH:$TAGLIB_DST_DIR/taglib \
$TAGLIB_DST_DIR/examples/tagreader /path/to/audio-file
# Install to C:\pkg_mingw
cmake --install $TAGLIB_DST_DIR --config Release --prefix /c/pkg_mingw --strip
```
The installed package can then be used by other projects.
```
TAGLIB_PREFIX=/c/pkg_mingw
cmake -B build_mingw -DCMAKE_BUILD_TYPE=Release \
-DCMAKE_INSTALL_PREFIX=$TAGLIB_PREFIX -G 'MinGW Makefiles'
cmake --build build_mingw --config Release
PATH=$PATH:$TAGLIB_PREFIX/bin
build_mingw/tagreader /path/to/audio-file
```
## Android
### Using vcpkg
The bash script below can be used to build TagLib for Android using vcpkg.
It must be started in the parent folder of the taglib source folder and will
build in a folder _android_build_ and install into _android_pkg_.
The package and the unit tests are then transferred to an Android device
and the unit tests are run on the device.
Note that `TESTS_TMPDIR` is set because there is no system-wide temporary folder
on Android. `TESTS_DIR` is set to run the tests on the target.
```
# You may have to adapt the NDK and vcpkg paths and the ABI/triplet.
export ANDROID_NDK_HOME=$HOME/Development/android-sdk/ndk/23.1.7779620
export VCPKG_ROOT=$HOME/Development/vcpkg
PATH=$PATH:$VCPKG_ROOT
# armeabi-v7a/arm-android or arm64-v8a/arm64-android or x86/x86-android or x86_64/x64-android
android_abi=armeabi-v7a
vcpkg_target_triplet=arm-android
vcpkg_toolchain_file=$VCPKG_ROOT/scripts/buildsystems/vcpkg.cmake
android_toolchain_file=$ANDROID_NDK_HOME/build/cmake/android.toolchain.cmake
vcpkg install --triplet $vcpkg_target_triplet utfcpp zlib cppunit
cmake -B android_build -S taglib \
-DCMAKE_TOOLCHAIN_FILE=$vcpkg_toolchain_file \
-DVCPKG_CHAINLOAD_TOOLCHAIN_FILE=$android_toolchain_file \
-DVCPKG_TARGET_TRIPLET=$vcpkg_target_triplet \
-DANDROID_ABI=$android_abi \
-GNinja -DBUILD_SHARED_LIBS=ON -DCMAKE_BUILD_TYPE=Release \
-DVISIBILITY_HIDDEN=ON -DENABLE_CCACHE=ON -DBUILD_EXAMPLES=ON -DBUILD_TESTING=ON \
-DTESTS_DIR=/data/local/tmp/tests/ -DTESTS_TMPDIR=/data/local/tmp
cmake --build android_build --config Release
cmake --install android_build --config Release --prefix android_pkg --strip
cp -a android_build/tests/test_runner android_pkg/bin/
if hash adb 2>/dev/null; then
adb push android_pkg /data/local/tmp/
adb push android_build/tests/test_runner /data/local/tmp/tests/test_runner
adb push taglib/tests/data /data/local/tmp/tests/
adb shell "env LD_LIBRARY_PATH=/data/local/tmp/android_pkg/lib /data/local/tmp/tests/test_runner"
# You could also try an example binary:
# adb shell "env LD_LIBRARY_PATH=/data/local/tmp/android_pkg/lib /data/local/tmp/android_pkg/bin/tagreader '/sdcard/Music/Some Album/A Track.mp3'"
fi
```

View File

@@ -1,16 +1,16 @@
# TagLib
[![Build Status](https://travis-ci.org/taglib/taglib.svg?branch=master)](https://travis-ci.org/taglib/taglib)
[![Build Status](../../actions/workflows/build.yml/badge.svg)](../../actions)
### TagLib Audio Metadata Library
https://taglib.org/
TagLib is a library for reading and editing the metadata of several
popular audio formats. Currently it supports 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,9 +18,7 @@ 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
[Mozilla Public License]: https://www.mozilla.org/MPL/MPL-1.1.html

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -10,16 +10,6 @@
#cmakedefine HAVE_MAC_BYTESWAP 1
#cmakedefine HAVE_OPENBSD_BYTESWAP 1
/* Defined if your compiler supports some atomic operations */
#cmakedefine HAVE_GCC_ATOMIC 1
#cmakedefine HAVE_MAC_ATOMIC 1
#cmakedefine HAVE_WIN_ATOMIC 1
#cmakedefine HAVE_IA64_ATOMIC 1
/* Defined if your compiler supports some safer version of vsprintf */
#cmakedefine HAVE_VSNPRINTF 1
#cmakedefine HAVE_VSPRINTF_S 1
/* Defined if your compiler supports ISO _strdup */
#cmakedefine HAVE_ISO_STRDUP 1
@@ -30,5 +20,6 @@
#cmakedefine TRACE_IN_RELEASE 1
#cmakedefine TESTS_DIR "@TESTS_DIR@"
#cmakedefine TESTS_TMPDIR "@TESTS_TMPDIR@"
#endif

View File

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

View File

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

View File

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

58
doc/taglib.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 11 KiB

View File

@@ -2,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,3 +39,20 @@ target_link_libraries(framelist tag)
add_executable(strip-id3v1 strip-id3v1.cpp)
target_link_libraries(strip-id3v1 tag)
if(WITH_MATROSKA)
########### next target ###############
add_executable(matroskareader matroskareader.cpp)
target_link_libraries(matroskareader tag)
########### next target ###############
add_executable(matroskawriter matroskawriter.cpp)
target_link_libraries(matroskawriter tag)
endif()
install(TARGETS tagreader tagreader_c tagwriter framelist strip-id3v1
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
)

View File

@@ -23,22 +23,20 @@
*/
#include <iostream>
#include <stdlib.h>
#include <cstdlib>
#include <tbytevector.h>
#include "taglib_config.h"
#include "tbytevector.h"
#include "mpegfile.h"
#include "id3v2tag.h"
#include "id3v2frame.h"
#include "id3v2header.h"
#include "commentsframe.h"
#include "id3v1tag.h"
#ifdef TAGLIB_WITH_APE
#include "apetag.h"
#endif
#include <mpegfile.h>
#include <id3v2tag.h>
#include <id3v2frame.h>
#include <id3v2header.h>
#include <commentsframe.h>
#include <id3v1tag.h>
#include <apetag.h>
using namespace std;
using namespace TagLib;
int main(int argc, char *argv[])
@@ -48,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]);
@@ -56,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;
ID3v2::FrameList::ConstIterator it = id3v2tag->frameList().begin();
for(; it != id3v2tag->frameList().end(); it++) {
cout << (*it)->frameID();
const auto &frames = id3v2tag->frameList();
for(auto it = frames.begin(); it != frames.end(); it++) {
std::cout << (*it)->frameID();
if(ID3v2::CommentsFrame *comment = dynamic_cast<ID3v2::CommentsFrame *>(*it))
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) {
for(APE::ItemListMap::ConstIterator it = ape->itemListMap().begin();
it != ape->itemListMap().end(); ++it)
const auto &items = ape->itemListMap();
for(auto it = items.begin(); it != items.end(); ++it)
{
if((*it).second.type() != APE::Item::Binary)
cout << (*it).first << " - \"" << (*it).second.toString() << "\"" << endl;
std::cout << (*it).first << " - \"" << (*it).second.toString() << "\"" << std::endl;
else
cout << (*it).first << " - Binary data (" << (*it).second.binaryData().size() << " bytes)" << endl;
std::cout << (*it).first << " - Binary data (" << (*it).second.binaryData().size() << " bytes)" << std::endl;
}
}
else
cout << "file does not have a valid APE tag" << endl;
std::cout << "file does not have a valid APE tag" << std::endl;
#endif
cout << endl;
std::cout << std::endl;
}
}

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

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

View File

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

View File

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

View File

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

View File

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

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

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

View File

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

View File

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

View File

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

View File

@@ -31,16 +31,14 @@
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#include <tbytevector.h>
#include <tstring.h>
#include <tdebug.h>
#include <tagunion.h>
#include <id3v1tag.h>
#include <id3v2header.h>
#include <tpropertymap.h>
#include <tagutils.h>
#include "apefile.h"
#include "tdebug.h"
#include "tpropertymap.h"
#include "id3v1tag.h"
#include "id3v2header.h"
#include "tagunion.h"
#include "tagutils.h"
#include "apetag.h"
#include "apefooter.h"
@@ -49,38 +47,23 @@ using namespace TagLib;
namespace
{
enum { ApeAPEIndex = 0, ApeID3v1Index = 1 };
}
} // namespace
class APE::File::FilePrivate
{
public:
FilePrivate() :
APELocation(-1),
APESize(0),
ID3v1Location(-1),
ID3v2Header(0),
ID3v2Location(-1),
ID3v2Size(0),
properties(0) {}
offset_t APELocation { -1 };
long APESize { 0 };
~FilePrivate()
{
delete ID3v2Header;
delete properties;
}
offset_t ID3v1Location { -1 };
long APELocation;
long APESize;
long ID3v1Location;
ID3v2::Header *ID3v2Header;
long ID3v2Location;
long ID3v2Size;
std::unique_ptr<ID3v2::Header> ID3v2Header;
offset_t ID3v2Location { -1 };
long ID3v2Size { 0 };
TagUnion tag;
Properties *properties;
std::unique_ptr<Properties> properties;
};
////////////////////////////////////////////////////////////////////////////////
@@ -92,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;
}
////////////////////////////////////////////////////////////////////////////////
@@ -101,7 +84,7 @@ bool APE::File::isSupported(IOStream *stream)
APE::File::File(FileName file, bool readProperties, Properties::ReadStyle) :
TagLib::File(file),
d(new FilePrivate())
d(std::make_unique<FilePrivate>())
{
if(isOpen())
read(readProperties);
@@ -109,16 +92,13 @@ APE::File::File(FileName file, bool readProperties, Properties::ReadStyle) :
APE::File::File(IOStream *stream, bool readProperties, Properties::ReadStyle) :
TagLib::File(stream),
d(new FilePrivate())
d(std::make_unique<FilePrivate>())
{
if(isOpen())
read(readProperties);
}
APE::File::~File()
{
delete d;
}
APE::File::~File() = default;
TagLib::Tag *APE::File::tag() const
{
@@ -145,7 +125,7 @@ PropertyMap APE::File::setProperties(const PropertyMap &properties)
APE::Properties *APE::File::audioProperties() const
{
return d->properties;
return d->properties.get();
}
bool APE::File::save()
@@ -198,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();
}
@@ -233,10 +213,10 @@ APE::Tag *APE::File::APETag(bool create)
void APE::File::strip(int tags)
{
if(tags & ID3v1)
d->tag.set(ApeID3v1Index, 0);
d->tag.set(ApeID3v1Index, nullptr);
if(tags & APE)
d->tag.set(ApeAPEIndex, 0);
d->tag.set(ApeAPEIndex, nullptr);
if(!ID3v1Tag())
APETag(true);
@@ -244,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;
}
////////////////////////////////////////////////////////////////////////////////
@@ -264,7 +244,7 @@ void APE::File::read(bool readProperties)
if(d->ID3v2Location >= 0) {
seek(d->ID3v2Location);
d->ID3v2Header = new ID3v2::Header(readBlock(ID3v2::Header::size()));
d->ID3v2Header = std::make_unique<ID3v2::Header>(readBlock(ID3v2::Header::size()));
d->ID3v2Size = d->ID3v2Header->completeTagSize();
}
@@ -292,7 +272,7 @@ void APE::File::read(bool readProperties)
if(readProperties) {
long streamLength;
offset_t streamLength;
if(d->APELocation >= 0)
streamLength = d->APELocation;
@@ -303,12 +283,12 @@ void APE::File::read(bool readProperties)
if(d->ID3v2Location >= 0) {
seek(d->ID3v2Location + d->ID3v2Size);
streamLength -= (d->ID3v2Location + d->ID3v2Size);
streamLength -= d->ID3v2Location + d->ID3v2Size;
}
else {
seek(0);
}
d->properties = new Properties(this, streamLength);
d->properties = std::make_unique<Properties>(this, streamLength);
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -23,31 +23,29 @@
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#include <taglib.h>
#include <tdebug.h>
#include <trefcounter.h>
#include "asfattribute.h"
#include "tdebug.h"
#include "asffile.h"
#include "asfutils.h"
using namespace TagLib;
class ASF::Attribute::AttributePrivate : public RefCounter
class ASF::Attribute::AttributePrivate
{
public:
AttributePrivate() :
pictureValue(ASF::Picture::fromInvalid()),
numericValue(0),
stream(0),
language(0) {}
AttributeTypes type;
pictureValue(ASF::Picture::fromInvalid())
{
}
AttributeTypes type { UnicodeType };
String stringValue;
ByteVector byteVectorValue;
ASF::Picture pictureValue;
unsigned long long numericValue;
int stream;
int language;
unsigned long long numericValue { 0 };
int stream { 0 };
int language { 0 };
};
////////////////////////////////////////////////////////////////////////////////
@@ -55,84 +53,72 @@ public:
////////////////////////////////////////////////////////////////////////////////
ASF::Attribute::Attribute() :
d(new AttributePrivate())
d(std::make_shared<AttributePrivate>())
{
d->type = UnicodeType;
}
ASF::Attribute::Attribute(const ASF::Attribute &other) :
d(other.d)
{
d->ref();
}
ASF::Attribute::Attribute(const ASF::Attribute &) = default;
ASF::Attribute::Attribute(const String &value) :
d(new AttributePrivate())
d(std::make_shared<AttributePrivate>())
{
d->type = UnicodeType;
d->stringValue = value;
}
ASF::Attribute::Attribute(const ByteVector &value) :
d(new AttributePrivate())
d(std::make_shared<AttributePrivate>())
{
d->type = BytesType;
d->byteVectorValue = value;
}
ASF::Attribute::Attribute(const ASF::Picture &value) :
d(new AttributePrivate())
d(std::make_shared<AttributePrivate>())
{
d->type = BytesType;
d->pictureValue = value;
}
ASF::Attribute::Attribute(unsigned int value) :
d(new AttributePrivate())
d(std::make_shared<AttributePrivate>())
{
d->type = DWordType;
d->numericValue = value;
}
ASF::Attribute::Attribute(unsigned long long value) :
d(new AttributePrivate())
d(std::make_shared<AttributePrivate>())
{
d->type = QWordType;
d->numericValue = value;
}
ASF::Attribute::Attribute(unsigned short value) :
d(new AttributePrivate())
d(std::make_shared<AttributePrivate>())
{
d->type = WordType;
d->numericValue = value;
}
ASF::Attribute::Attribute(bool value) :
d(new AttributePrivate())
d(std::make_shared<AttributePrivate>())
{
d->type = BoolType;
d->numericValue = value;
}
ASF::Attribute &ASF::Attribute::operator=(const ASF::Attribute &other)
{
Attribute(other).swap(*this);
return *this;
}
ASF::Attribute &ASF::Attribute::operator=(const ASF::Attribute &) = default;
void ASF::Attribute::swap(Attribute &other)
void ASF::Attribute::swap(Attribute &other) noexcept
{
using std::swap;
swap(d, other.d);
}
ASF::Attribute::~Attribute()
{
if(d->deref())
delete d;
}
ASF::Attribute::~Attribute() = default;
ASF::Attribute::AttributeTypes ASF::Attribute::type() const
{
@@ -168,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
@@ -176,30 +162,30 @@ ASF::Picture ASF::Attribute::toPicture() const
return d->pictureValue;
}
String ASF::Attribute::parse(ASF::File &f, int kind)
String ASF::Attribute::parse(ASF::File &file, int kind)
{
unsigned int size, nameLength;
String name;
d->pictureValue = Picture::fromInvalid();
// extended content descriptor
if(kind == 0) {
nameLength = readWORD(&f);
name = readString(&f, nameLength);
d->type = ASF::Attribute::AttributeTypes(readWORD(&f));
size = readWORD(&f);
nameLength = readWORD(&file);
name = readString(&file, nameLength);
d->type = static_cast<ASF::Attribute::AttributeTypes>(readWORD(&file));
size = readWORD(&file);
}
// metadata & metadata library
else {
int temp = readWORD(&f);
int temp = readWORD(&file);
// metadata library
if(kind == 2) {
d->language = temp;
}
d->stream = readWORD(&f);
nameLength = readWORD(&f);
d->type = ASF::Attribute::AttributeTypes(readWORD(&f));
size = readDWORD(&f);
name = readString(&f, nameLength);
d->stream = readWORD(&file);
nameLength = readWORD(&file);
d->type = static_cast<ASF::Attribute::AttributeTypes>(readWORD(&file));
size = readDWORD(&file);
name = readString(&file, nameLength);
}
if(kind != 2 && size > 65535) {
@@ -208,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;
}
@@ -254,7 +240,6 @@ int ASF::Attribute::dataSize() const
case WordType:
return 2;
case BoolType:
return 4;
case DWordType:
return 4;
case QWordType:
@@ -262,8 +247,10 @@ int ASF::Attribute::dataSize() const
case UnicodeType:
return d->stringValue.size() * 2 + 2;
case BytesType:
if(d->pictureValue.isValid())
if(d->pictureValue.isValid()) {
return d->pictureValue.dataSize();
}
return d->byteVectorValue.size();
case GuidType:
return d->byteVectorValue.size();
}
@@ -303,8 +290,11 @@ ByteVector ASF::Attribute::render(const String &name, int kind) const
case BytesType:
if(d->pictureValue.isValid()) {
data.append(d->pictureValue.render());
break;
}
else {
data.append(d->byteVectorValue);
}
break;
case GuidType:
data.append(d->byteVectorValue);
break;
@@ -312,16 +302,16 @@ ByteVector ASF::Attribute::render(const String &name, int kind) const
if(kind == 0) {
data = renderString(name, true) +
ByteVector::fromShort((int)d->type, false) +
ByteVector::fromShort(data.size(), false) +
ByteVector::fromShort(static_cast<short>(d->type), false) +
ByteVector::fromShort(static_cast<short>(data.size()), false) +
data;
}
else {
ByteVector nameData = renderString(name);
data = ByteVector::fromShort(kind == 2 ? d->language : 0, false) +
ByteVector::fromShort(d->stream, false) +
ByteVector::fromShort(nameData.size(), false) +
ByteVector::fromShort((int)d->type, false) +
data = ByteVector::fromShort(static_cast<short>(kind == 2 ? d->language : 0), false) +
ByteVector::fromShort(static_cast<short>(d->stream), false) +
ByteVector::fromShort(static_cast<short>(nameData.size()), false) +
ByteVector::fromShort(static_cast<short>(d->type), false) +
ByteVector::fromUInt(data.size(), false) +
nameData +
data;

View File

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

View File

@@ -23,13 +23,14 @@
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#include <tdebug.h>
#include <tbytevectorlist.h>
#include <tpropertymap.h>
#include <tstring.h>
#include <tagutils.h>
#include "asffile.h"
#include <utility>
#include "tdebug.h"
#include "tpropertymap.h"
#include "tbytevectorlist.h"
#include "tagutils.h"
#include "asftag.h"
#include "asfproperties.h"
#include "asfutils.h"
@@ -50,37 +51,28 @@ public:
class MetadataObject;
class MetadataLibraryObject;
FilePrivate():
headerSize(0),
tag(0),
properties(0),
contentDescriptionObject(0),
extendedContentDescriptionObject(0),
headerExtensionObject(0),
metadataObject(0),
metadataLibraryObject(0)
FilePrivate()
{
objects.setAutoDelete(true);
}
~FilePrivate()
{
delete tag;
delete properties;
}
~FilePrivate() = default;
unsigned long long headerSize;
FilePrivate(const FilePrivate &) = delete;
FilePrivate &operator=(const FilePrivate &) = delete;
ASF::Tag *tag;
ASF::Properties *properties;
unsigned long long headerSize { 0 };
std::unique_ptr<ASF::Tag> tag;
std::unique_ptr<ASF::Properties> properties;
List<BaseObject *> objects;
ContentDescriptionObject *contentDescriptionObject;
ExtendedContentDescriptionObject *extendedContentDescriptionObject;
HeaderExtensionObject *headerExtensionObject;
MetadataObject *metadataObject;
MetadataLibraryObject *metadataLibraryObject;
ContentDescriptionObject *contentDescriptionObject { nullptr };
ExtendedContentDescriptionObject *extendedContentDescriptionObject { nullptr };
HeaderExtensionObject *headerExtensionObject { nullptr };
MetadataObject *metadataObject { nullptr };
MetadataLibraryObject *metadataLibraryObject { nullptr };
};
namespace
@@ -97,15 +89,15 @@ namespace
const ByteVector contentEncryptionGuid("\xFB\xB3\x11\x22\x23\xBD\xD2\x11\xB4\xB7\x00\xA0\xC9\x55\xFC\x6E", 16);
const ByteVector extendedContentEncryptionGuid("\x14\xE6\x8A\x29\x22\x26 \x17\x4C\xB9\x35\xDA\xE0\x7E\xE9\x28\x9C", 16);
const ByteVector advancedContentEncryptionGuid("\xB6\x9B\x07\x7A\xA4\xDA\x12\x4E\xA5\xCA\x91\xD3\x8D\xC1\x1A\x8D", 16);
}
} // namespace
class ASF::File::FilePrivate::BaseObject
{
public:
ByteVector data;
virtual ~BaseObject() {}
virtual ~BaseObject() = default;
virtual ByteVector guid() const = 0;
virtual void parse(ASF::File *file, unsigned int size);
virtual void parse(ASF::File *file, long long size);
virtual ByteVector render(ASF::File *file);
};
@@ -114,56 +106,56 @@ class ASF::File::FilePrivate::UnknownObject : public ASF::File::FilePrivate::Bas
ByteVector myGuid;
public:
UnknownObject(const ByteVector &guid);
ByteVector guid() const;
ByteVector guid() const override;
};
class ASF::File::FilePrivate::FilePropertiesObject : public ASF::File::FilePrivate::BaseObject
{
public:
ByteVector guid() const;
void parse(ASF::File *file, unsigned int size);
ByteVector guid() const override;
void parse(ASF::File *file, long long size) override;
};
class ASF::File::FilePrivate::StreamPropertiesObject : public ASF::File::FilePrivate::BaseObject
{
public:
ByteVector guid() const;
void parse(ASF::File *file, unsigned int size);
ByteVector guid() const override;
void parse(ASF::File *file, long long size) override;
};
class ASF::File::FilePrivate::ContentDescriptionObject : public ASF::File::FilePrivate::BaseObject
{
public:
ByteVector guid() const;
void parse(ASF::File *file, unsigned int size);
ByteVector render(ASF::File *file);
ByteVector guid() const override;
void parse(ASF::File *file, long long size) override;
ByteVector render(ASF::File *file) override;
};
class ASF::File::FilePrivate::ExtendedContentDescriptionObject : public ASF::File::FilePrivate::BaseObject
{
public:
ByteVectorList attributeData;
ByteVector guid() const;
void parse(ASF::File *file, unsigned int size);
ByteVector render(ASF::File *file);
ByteVector guid() const override;
void parse(ASF::File *file, long long size) override;
ByteVector render(ASF::File *file) override;
};
class ASF::File::FilePrivate::MetadataObject : public ASF::File::FilePrivate::BaseObject
{
public:
ByteVectorList attributeData;
ByteVector guid() const;
void parse(ASF::File *file, unsigned int size);
ByteVector render(ASF::File *file);
ByteVector guid() const override;
void parse(ASF::File *file, long long size) override;
ByteVector render(ASF::File *file) override;
};
class ASF::File::FilePrivate::MetadataLibraryObject : public ASF::File::FilePrivate::BaseObject
{
public:
ByteVectorList attributeData;
ByteVector guid() const;
void parse(ASF::File *file, unsigned int size);
ByteVector render(ASF::File *file);
ByteVector guid() const override;
void parse(ASF::File *file, long long size) override;
ByteVector render(ASF::File *file) override;
};
class ASF::File::FilePrivate::HeaderExtensionObject : public ASF::File::FilePrivate::BaseObject
@@ -171,16 +163,16 @@ class ASF::File::FilePrivate::HeaderExtensionObject : public ASF::File::FilePriv
public:
List<ASF::File::FilePrivate::BaseObject *> objects;
HeaderExtensionObject();
ByteVector guid() const;
void parse(ASF::File *file, unsigned int size);
ByteVector render(ASF::File *file);
ByteVector guid() const override;
void parse(ASF::File *file, long long size) override;
ByteVector render(ASF::File *file) override;
};
class ASF::File::FilePrivate::CodecListObject : public ASF::File::FilePrivate::BaseObject
{
public:
ByteVector guid() const;
void parse(ASF::File *file, unsigned int size);
ByteVector guid() const override;
void parse(ASF::File *file, long long size) override;
private:
enum CodecType
@@ -191,10 +183,10 @@ private:
};
};
void ASF::File::FilePrivate::BaseObject::parse(ASF::File *file, unsigned int size)
void ASF::File::FilePrivate::BaseObject::parse(ASF::File *file, long long size)
{
data.clear();
if(size > 24 && size <= (unsigned int)(file->length()))
if(size > 24 && size <= file->length())
data = file->readBlock(size - 24);
else
data = ByteVector();
@@ -219,7 +211,7 @@ ByteVector ASF::File::FilePrivate::FilePropertiesObject::guid() const
return filePropertiesGuid;
}
void ASF::File::FilePrivate::FilePropertiesObject::parse(ASF::File *file, unsigned int size)
void ASF::File::FilePrivate::FilePropertiesObject::parse(ASF::File *file, long long size)
{
BaseObject::parse(file, size);
if(data.size() < 64) {
@@ -229,7 +221,8 @@ void ASF::File::FilePrivate::FilePropertiesObject::parse(ASF::File *file, unsign
const long long duration = data.toLongLong(40, false);
const long long preroll = data.toLongLong(56, false);
file->d->properties->setLengthInMilliseconds(static_cast<int>(duration / 10000.0 - preroll + 0.5));
file->d->properties->setLengthInMilliseconds(
static_cast<int>(static_cast<double>(duration) / 10000.0 - static_cast<double>(preroll) + 0.5));
}
ByteVector ASF::File::FilePrivate::StreamPropertiesObject::guid() const
@@ -237,7 +230,7 @@ ByteVector ASF::File::FilePrivate::StreamPropertiesObject::guid() const
return streamPropertiesGuid;
}
void ASF::File::FilePrivate::StreamPropertiesObject::parse(ASF::File *file, unsigned int size)
void ASF::File::FilePrivate::StreamPropertiesObject::parse(ASF::File *file, long long size)
{
BaseObject::parse(file, size);
if(data.size() < 70) {
@@ -257,7 +250,7 @@ ByteVector ASF::File::FilePrivate::ContentDescriptionObject::guid() const
return contentDescriptionGuid;
}
void ASF::File::FilePrivate::ContentDescriptionObject::parse(ASF::File *file, unsigned int /*size*/)
void ASF::File::FilePrivate::ContentDescriptionObject::parse(ASF::File *file, long long /*size*/)
{
const int titleLength = readWORD(file);
const int artistLength = readWORD(file);
@@ -279,11 +272,11 @@ ByteVector ASF::File::FilePrivate::ContentDescriptionObject::render(ASF::File *f
const ByteVector v4 = renderString(file->d->tag->comment());
const ByteVector v5 = renderString(file->d->tag->rating());
data.clear();
data.append(ByteVector::fromShort(v1.size(), false));
data.append(ByteVector::fromShort(v2.size(), false));
data.append(ByteVector::fromShort(v3.size(), false));
data.append(ByteVector::fromShort(v4.size(), false));
data.append(ByteVector::fromShort(v5.size(), false));
data.append(ByteVector::fromShort(static_cast<short>(v1.size()), false));
data.append(ByteVector::fromShort(static_cast<short>(v2.size()), false));
data.append(ByteVector::fromShort(static_cast<short>(v3.size()), false));
data.append(ByteVector::fromShort(static_cast<short>(v4.size()), false));
data.append(ByteVector::fromShort(static_cast<short>(v5.size()), false));
data.append(v1);
data.append(v2);
data.append(v3);
@@ -297,7 +290,7 @@ ByteVector ASF::File::FilePrivate::ExtendedContentDescriptionObject::guid() cons
return extendedContentDescriptionGuid;
}
void ASF::File::FilePrivate::ExtendedContentDescriptionObject::parse(ASF::File *file, unsigned int /*size*/)
void ASF::File::FilePrivate::ExtendedContentDescriptionObject::parse(ASF::File *file, long long /*size*/)
{
int count = readWORD(file);
while(count--) {
@@ -310,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);
}
@@ -320,7 +313,7 @@ ByteVector ASF::File::FilePrivate::MetadataObject::guid() const
return metadataGuid;
}
void ASF::File::FilePrivate::MetadataObject::parse(ASF::File *file, unsigned int /*size*/)
void ASF::File::FilePrivate::MetadataObject::parse(ASF::File *file, long long /*size*/)
{
int count = readWORD(file);
while(count--) {
@@ -333,7 +326,7 @@ void ASF::File::FilePrivate::MetadataObject::parse(ASF::File *file, unsigned int
ByteVector ASF::File::FilePrivate::MetadataObject::render(ASF::File *file)
{
data.clear();
data.append(ByteVector::fromShort(attributeData.size(), false));
data.append(ByteVector::fromShort(static_cast<short>(attributeData.size()), false));
data.append(attributeData.toByteVector(""));
return BaseObject::render(file);
}
@@ -343,7 +336,7 @@ ByteVector ASF::File::FilePrivate::MetadataLibraryObject::guid() const
return metadataLibraryGuid;
}
void ASF::File::FilePrivate::MetadataLibraryObject::parse(ASF::File *file, unsigned int /*size*/)
void ASF::File::FilePrivate::MetadataLibraryObject::parse(ASF::File *file, long long /*size*/)
{
int count = readWORD(file);
while(count--) {
@@ -356,7 +349,7 @@ void ASF::File::FilePrivate::MetadataLibraryObject::parse(ASF::File *file, unsig
ByteVector ASF::File::FilePrivate::MetadataLibraryObject::render(ASF::File *file)
{
data.clear();
data.append(ByteVector::fromShort(attributeData.size(), false));
data.append(ByteVector::fromShort(static_cast<short>(attributeData.size()), false));
data.append(attributeData.toByteVector(""));
return BaseObject::render(file);
}
@@ -371,36 +364,36 @@ ByteVector ASF::File::FilePrivate::HeaderExtensionObject::guid() const
return headerExtensionGuid;
}
void ASF::File::FilePrivate::HeaderExtensionObject::parse(ASF::File *file, unsigned int /*size*/)
void ASF::File::FilePrivate::HeaderExtensionObject::parse(ASF::File *file, long long /*size*/)
{
file->seek(18, File::Current);
long long dataSize = readDWORD(file);
long long dataPos = 0;
while(dataPos < dataSize) {
ByteVector guid = file->readBlock(16);
if(guid.size() != 16) {
ByteVector uid = file->readBlock(16);
if(uid.size() != 16) {
file->setValid(false);
break;
}
bool ok;
long long size = readQWORD(file, &ok);
if(!ok) {
if(!ok || size < 0 || size > dataSize - dataPos) {
file->setValid(false);
break;
}
BaseObject *obj;
if(guid == metadataGuid) {
if(uid == metadataGuid) {
file->d->metadataObject = new MetadataObject();
obj = file->d->metadataObject;
}
else if(guid == metadataLibraryGuid) {
else if(uid == metadataLibraryGuid) {
file->d->metadataLibraryObject = new MetadataLibraryObject();
obj = file->d->metadataLibraryObject;
}
else {
obj = new UnknownObject(guid);
obj = new UnknownObject(uid);
}
obj->parse(file, (unsigned int)size);
obj->parse(file, size);
objects.append(obj);
dataPos += size;
}
@@ -409,8 +402,8 @@ void ASF::File::FilePrivate::HeaderExtensionObject::parse(ASF::File *file, unsig
ByteVector ASF::File::FilePrivate::HeaderExtensionObject::render(ASF::File *file)
{
data.clear();
for(List<BaseObject *>::ConstIterator it = objects.begin(); it != objects.end(); ++it) {
data.append((*it)->render(file));
for(const auto &object : std::as_const(objects)) {
data.append(object->render(file));
}
data = ByteVector("\x11\xD2\xD3\xAB\xBA\xA9\xcf\x11\x8E\xE6\x00\xC0\x0C\x20\x53\x65\x06\x00", 18) + ByteVector::fromUInt(data.size(), false) + data;
return BaseObject::render(file);
@@ -421,7 +414,7 @@ ByteVector ASF::File::FilePrivate::CodecListObject::guid() const
return codecListGuid;
}
void ASF::File::FilePrivate::CodecListObject::parse(ASF::File *file, unsigned int size)
void ASF::File::FilePrivate::CodecListObject::parse(ASF::File *file, long long size)
{
BaseObject::parse(file, size);
if(data.size() <= 20) {
@@ -439,7 +432,7 @@ void ASF::File::FilePrivate::CodecListObject::parse(ASF::File *file, unsigned in
if(pos >= data.size())
break;
const CodecType type = static_cast<CodecType>(data.toUShort(pos, false));
const auto type = static_cast<CodecType>(data.toUShort(pos, false));
pos += 2;
int nameLength = data.toUShort(pos, false);
@@ -480,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;
}
////////////////////////////////////////////////////////////////////////////////
@@ -489,7 +482,7 @@ bool ASF::File::isSupported(IOStream *stream)
ASF::File::File(FileName file, bool, Properties::ReadStyle) :
TagLib::File(file),
d(new FilePrivate())
d(std::make_unique<FilePrivate>())
{
if(isOpen())
read();
@@ -497,20 +490,17 @@ ASF::File::File(FileName file, bool, Properties::ReadStyle) :
ASF::File::File(IOStream *stream, bool, Properties::ReadStyle) :
TagLib::File(stream),
d(new FilePrivate())
d(std::make_unique<FilePrivate>())
{
if(isOpen())
read();
}
ASF::File::~File()
{
delete d;
}
ASF::File::~File() = default;
ASF::Tag *ASF::File::tag() const
{
return d->tag;
return d->tag.get();
}
PropertyMap ASF::File::properties() const
@@ -530,7 +520,7 @@ PropertyMap ASF::File::setProperties(const PropertyMap &properties)
ASF::Properties *ASF::File::audioProperties() const
{
return d->properties;
return d->properties.get();
}
bool ASF::File::save()
@@ -570,21 +560,13 @@ bool ASF::File::save()
d->metadataObject->attributeData.clear();
d->metadataLibraryObject->attributeData.clear();
const AttributeListMap allAttributes = d->tag->attributeListMap();
for(AttributeListMap::ConstIterator it = allAttributes.begin(); it != allAttributes.end(); ++it) {
const String &name = it->first;
const AttributeList &attributes = it->second;
for(const auto &[name, attributes] : std::as_const(d->tag->attributeListMap())) {
bool inExtendedContentDescriptionObject = false;
bool inMetadataObject = false;
for(AttributeList::ConstIterator jt = attributes.begin(); jt != attributes.end(); ++jt) {
const Attribute &attribute = *jt;
const bool largeValue = (attribute.dataSize() > 65535);
const bool guid = (attribute.type() == Attribute::GuidType);
for(const auto &attribute : attributes) {
const bool largeValue = attribute.dataSize() > 65535;
const bool guid = attribute.type() == Attribute::GuidType;
if(!inExtendedContentDescriptionObject && !guid && !largeValue && attribute.language() == 0 && attribute.stream() == 0) {
d->extendedContentDescriptionObject->attributeData.append(attribute.render(name));
@@ -601,8 +583,8 @@ bool ASF::File::save()
}
ByteVector data;
for(List<FilePrivate::BaseObject *>::ConstIterator it = d->objects.begin(); it != d->objects.end(); ++it) {
data.append((*it)->render(this));
for(const auto &object : std::as_const(d->objects)) {
data.append(object->render(this));
}
seek(16);
@@ -632,8 +614,8 @@ void ASF::File::read()
return;
}
d->tag = new ASF::Tag();
d->properties = new ASF::Properties();
d->tag = std::make_unique<ASF::Tag>();
d->properties = std::make_unique<ASF::Properties>();
bool ok;
d->headerSize = readQWORD(this, &ok);
@@ -648,15 +630,15 @@ void ASF::File::read()
}
seek(2, Current);
FilePrivate::FilePropertiesObject *filePropertiesObject = 0;
FilePrivate::StreamPropertiesObject *streamPropertiesObject = 0;
FilePrivate::FilePropertiesObject *filePropertiesObject = nullptr;
FilePrivate::StreamPropertiesObject *streamPropertiesObject = nullptr;
for(int i = 0; i < numObjects; i++) {
const ByteVector guid = readBlock(16);
if(guid.size() != 16) {
setValid(false);
break;
}
long size = (long)readQWORD(this, &ok);
auto size = readQWORD(this, &ok);
if(!ok) {
setValid(false);
break;
@@ -700,6 +682,5 @@ void ASF::File::read()
if(!filePropertiesObject || !streamPropertiesObject) {
debug("ASF::File::read(): Missing mandatory header objects.");
setValid(false);
return;
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,156 @@
/***************************************************************************
copyright : (C) 2016 by Damien Plisson, Audirvana
email : damien78@audirvana.com
***************************************************************************/
/***************************************************************************
* This library is free software; you can redistribute it and/or modify *
* it under the terms of the GNU Lesser General Public License version *
* 2.1 as published by the Free Software Foundation. *
* *
* This library is distributed in the hope that it will be useful, but *
* WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
* Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public *
* License along with this library; if not, write to the Free Software *
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
* 02110-1301 USA *
* *
* Alternatively, this file is available under the Mozilla Public *
* License Version 1.1. You may obtain a copy of the License at *
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#include "dsdiffdiintag.h"
#include <utility>
#include "tstringlist.h"
#include "tpropertymap.h"
#include "tdebug.h"
using namespace TagLib;
using namespace DSDIFF::DIIN;
class DSDIFF::DIIN::Tag::TagPrivate
{
public:
String title;
String artist;
};
DSDIFF::DIIN::Tag::Tag() :
d(std::make_unique<TagPrivate>())
{
}
DSDIFF::DIIN::Tag::~Tag() = default;
String DSDIFF::DIIN::Tag::title() const
{
return d->title;
}
String DSDIFF::DIIN::Tag::artist() const
{
return d->artist;
}
String DSDIFF::DIIN::Tag::album() const
{
return String();
}
String DSDIFF::DIIN::Tag::comment() const
{
return String();
}
String DSDIFF::DIIN::Tag::genre() const
{
return String();
}
unsigned int DSDIFF::DIIN::Tag::year() const
{
return 0;
}
unsigned int DSDIFF::DIIN::Tag::track() const
{
return 0;
}
void DSDIFF::DIIN::Tag::setTitle(const String &title)
{
d->title = title;
}
void DSDIFF::DIIN::Tag::setArtist(const String &artist)
{
d->artist = artist;
}
void DSDIFF::DIIN::Tag::setAlbum(const String &)
{
debug("DSDIFF::DIIN::Tag::setAlbum() -- Ignoring unsupported tag.");
}
void DSDIFF::DIIN::Tag::setComment(const String &)
{
debug("DSDIFF::DIIN::Tag::setComment() -- Ignoring unsupported tag.");
}
void DSDIFF::DIIN::Tag::setGenre(const String &)
{
debug("DSDIFF::DIIN::Tag::setGenre() -- Ignoring unsupported tag.");
}
void DSDIFF::DIIN::Tag::setYear(unsigned int)
{
debug("DSDIFF::DIIN::Tag::setYear() -- Ignoring unsupported tag.");
}
void DSDIFF::DIIN::Tag::setTrack(unsigned int)
{
debug("DSDIFF::DIIN::Tag::setTrack() -- Ignoring unsupported tag.");
}
PropertyMap DSDIFF::DIIN::Tag::properties() const
{
PropertyMap properties;
properties["TITLE"] = d->title;
properties["ARTIST"] = d->artist;
return properties;
}
PropertyMap DSDIFF::DIIN::Tag::setProperties(const PropertyMap &origProps)
{
PropertyMap props(origProps);
props.removeEmpty();
StringList oneValueSet;
if(props.contains("TITLE")) {
d->title = props["TITLE"].front();
oneValueSet.append("TITLE");
} else
d->title.clear();
if(props.contains("ARTIST")) {
d->artist = props["ARTIST"].front();
oneValueSet.append("ARTIST");
} else
d->artist.clear();
// for each tag that has been set above, remove the first entry in the corresponding
// value list. The others will be returned as unsupported by this format.
for(const auto &entry : std::as_const(oneValueSet)) {
if(props[entry].size() == 1)
props.erase(entry);
else
props[entry].erase(props[entry].begin());
}
return props;
}

View File

@@ -0,0 +1,147 @@
/***************************************************************************
copyright : (C) 2016 by Damien Plisson, Audirvana
email : damien78@audirvana.com
***************************************************************************/
/***************************************************************************
* This library is free software; you can redistribute it and/or modify *
* it under the terms of the GNU Lesser General Public License version *
* 2.1 as published by the Free Software Foundation. *
* *
* This library is distributed in the hope that it will be useful, but *
* WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
* Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public *
* License along with this library; if not, write to the Free Software *
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
* 02110-1301 USA *
* *
* Alternatively, this file is available under the Mozilla Public *
* License Version 1.1. You may obtain a copy of the License at *
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#ifndef TAGLIB_DSDIFFDIINTAG_H
#define TAGLIB_DSDIFFDIINTAG_H
#include "tag.h"
namespace TagLib {
namespace DSDIFF {
namespace DIIN {
/*!
* Tags from the Edited Master Chunk Info
*
* Only Title and Artist tags are supported
*/
class TAGLIB_EXPORT Tag : public TagLib::Tag
{
public:
Tag();
~Tag() override;
/*!
* Returns the track name; if no track name is present in the tag
* String() will be returned.
*/
String title() const override;
/*!
* Returns the artist name; if no artist name is present in the tag
* String() will be returned.
*/
String artist() const override;
/*!
* Not supported. Therefore always returns String().
*/
String album() const override;
/*!
* Not supported. Therefore always returns String().
*/
String comment() const override;
/*!
* Not supported. Therefore always returns String().
*/
String genre() const override;
/*!
* Not supported. Therefore always returns 0.
*/
unsigned int year() const override;
/*!
* Not supported. Therefore always returns 0.
*/
unsigned int track() const override;
/*!
* Sets the title to \a title. If \a title is String() then this
* value will be cleared.
*/
void setTitle(const String &title) override;
/*!
* Sets the artist to \a artist. If \a artist is String() then this
* value will be cleared.
*/
void setArtist(const String &artist) override;
/*!
* Not supported and therefore ignored.
*/
void setAlbum(const String &album) override;
/*!
* Not supported and therefore ignored.
*/
void setComment(const String &comment) override;
/*!
* Not supported and therefore ignored.
*/
void setGenre(const String &genre) override;
/*!
* Not supported and therefore ignored.
*/
void setYear(unsigned int year) override;
/*!
* Not supported and therefore ignored.
*/
void setTrack(unsigned int track) override;
/*!
* Implements the unified property interface -- export function.
* Since the DIIN tag is very limited, the exported map is as well.
*/
PropertyMap properties() const override;
/*!
* Implements the unified property interface -- import function.
* Because of the limitations of the DIIN file tag, any tags besides
* TITLE and ARTIST, will be
* returned. Additionally, if the map contains tags with multiple values,
* all but the first will be contained in the returned map of unsupported
* properties.
*/
PropertyMap setProperties(const PropertyMap &) override;
private:
class TagPrivate;
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
std::unique_ptr<TagPrivate> d;
};
} // namespace DIIN
} // namespace DSDIFF
} // namespace TagLib
#endif

View File

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

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

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

View File

@@ -0,0 +1,100 @@
/***************************************************************************
copyright : (C) 2016 by Damien Plisson, Audirvana
email : damien78@audirvana.com
***************************************************************************/
/***************************************************************************
* This library is free software; you can redistribute it and/or modify *
* it under the terms of the GNU Lesser General Public License version *
* 2.1 as published by the Free Software Foundation. *
* *
* This library is distributed in the hope that it will be useful, but *
* WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
* Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public *
* License along with this library; if not, write to the Free Software *
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
* 02110-1301 USA *
* *
* Alternatively, this file is available under the Mozilla Public *
* License Version 1.1. You may obtain a copy of the License at *
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#include "dsdiffproperties.h"
#include "tstring.h"
using namespace TagLib;
class DSDIFF::Properties::PropertiesPrivate
{
public:
int length { 0 };
int bitrate { 0 };
int sampleRate { 0 };
int channels { 0 };
int sampleWidth { 0 };
unsigned long long sampleCount { 0 };
};
////////////////////////////////////////////////////////////////////////////////
// public members
////////////////////////////////////////////////////////////////////////////////
DSDIFF::Properties::Properties(unsigned int sampleRate,
unsigned short channels,
unsigned long long samplesCount,
int bitrate,
ReadStyle style) :
AudioProperties(style),
d(std::make_unique<PropertiesPrivate>())
{
d->channels = channels;
d->sampleCount = samplesCount;
d->sampleWidth = 1;
d->sampleRate = sampleRate;
d->bitrate = bitrate;
d->length = d->sampleRate > 0
? static_cast<int>(static_cast<double>(d->sampleCount) * 1000.0 / d->sampleRate + 0.5)
: 0;
}
DSDIFF::Properties::~Properties() = default;
int DSDIFF::Properties::lengthInSeconds() const
{
return d->length / 1000;
}
int DSDIFF::Properties::lengthInMilliseconds() const
{
return d->length;
}
int DSDIFF::Properties::bitrate() const
{
return d->bitrate;
}
int DSDIFF::Properties::sampleRate() const
{
return d->sampleRate;
}
int DSDIFF::Properties::channels() const
{
return d->channels;
}
int DSDIFF::Properties::bitsPerSample() const
{
return d->sampleWidth;
}
long long DSDIFF::Properties::sampleCount() const
{
return d->sampleCount;
}

View File

@@ -0,0 +1,79 @@
/***************************************************************************
copyright : (C) 2016 by Damien Plisson, Audirvana
email : damien78@audirvana.com
***************************************************************************/
/***************************************************************************
* This library is free software; you can redistribute it and/or modify *
* it under the terms of the GNU Lesser General Public License version *
* 2.1 as published by the Free Software Foundation. *
* *
* This library is distributed in the hope that it will be useful, but *
* WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
* Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public *
* License along with this library; if not, write to the Free Software *
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
* 02110-1301 USA *
* *
* Alternatively, this file is available under the Mozilla Public *
* License Version 1.1. You may obtain a copy of the License at *
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#ifndef TAGLIB_DSDIFFPROPERTIES_H
#define TAGLIB_DSDIFFPROPERTIES_H
#include "audioproperties.h"
namespace TagLib {
namespace DSDIFF {
class File;
//! An implementation of audio property reading for DSDIFF
/*!
* This reads the data from a DSDIFF stream found in the AudioProperties
* API.
*/
class TAGLIB_EXPORT Properties : public AudioProperties
{
public:
/*!
* Create an instance of DSDIFF::Properties with the data read from the
* ByteVector \a data.
*/
Properties(unsigned int sampleRate, unsigned short channels,
unsigned long long samplesCount, int bitrate,
ReadStyle style);
/*!
* Destroys this DSDIFF::Properties instance.
*/
~Properties() override;
// Reimplementations.
int lengthInSeconds() const override;
int lengthInMilliseconds() const override;
int bitrate() const override;
int sampleRate() const override;
int channels() const override;
int bitsPerSample() const;
long long sampleCount() const;
private:
class PropertiesPrivate;
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
std::unique_ptr<PropertiesPrivate> d;
};
} // namespace DSDIFF
} // namespace TagLib
#endif

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

@@ -0,0 +1,230 @@
/***************************************************************************
copyright : (C) 2013-2023 Stephen F. Booth
email : me@sbooth.org
***************************************************************************/
/***************************************************************************
* This library is free software; you can redistribute it and/or modify *
* it under the terms of the GNU Lesser General Public License version *
* 2.1 as published by the Free Software Foundation. *
* *
* This library is distributed in the hope that it will be useful, but *
* WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
* Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public *
* License along with this library; if not, write to the Free Software *
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
* 02110-1301 USA *
* *
* Alternatively, this file is available under the Mozilla Public *
* License Version 1.1. You may obtain a copy of the License at *
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#include "dsffile.h"
#include "tdebug.h"
#include "tpropertymap.h"
#include "tagutils.h"
using namespace TagLib;
class DSF::File::FilePrivate
{
public:
FilePrivate(const ID3v2::FrameFactory *frameFactory)
: ID3v2FrameFactory(frameFactory ? frameFactory
: ID3v2::FrameFactory::instance())
{
}
~FilePrivate() = default;
FilePrivate(const FilePrivate &) = delete;
FilePrivate &operator=(const FilePrivate &) = delete;
const ID3v2::FrameFactory *ID3v2FrameFactory;
long long fileSize = 0;
long long metadataOffset = 0;
std::unique_ptr<Properties> properties;
std::unique_ptr<ID3v2::Tag> tag;
};
bool DSF::File::isSupported(IOStream *stream)
{
// A DSF file has to start with "DSD "
const ByteVector id = Utils::readHeader(stream, 4, false);
return id.startsWith("DSD ");
}
DSF::File::File(FileName file, bool,
AudioProperties::ReadStyle propertiesStyle,
ID3v2::FrameFactory *frameFactory) :
TagLib::File(file),
d(std::make_unique<FilePrivate>(frameFactory))
{
if(isOpen())
read(propertiesStyle);
}
DSF::File::File(IOStream *stream, bool,
AudioProperties::ReadStyle propertiesStyle,
ID3v2::FrameFactory *frameFactory) :
TagLib::File(stream),
d(std::make_unique<FilePrivate>(frameFactory))
{
if(isOpen())
read(propertiesStyle);
}
DSF::File::~File() = default;
ID3v2::Tag *DSF::File::tag() const
{
return d->tag.get();
}
PropertyMap DSF::File::properties() const
{
return d->tag->properties();
}
PropertyMap DSF::File::setProperties(const PropertyMap &properties)
{
return d->tag->setProperties(properties);
}
DSF::Properties *DSF::File::audioProperties() const
{
return d->properties.get();
}
bool DSF::File::save()
{
return save(ID3v2::v4);
}
bool DSF::File::save(ID3v2::Version version)
{
if(readOnly()) {
debug("DSF::File::save() - Cannot save to a read only file.");
return false;
}
// Three things must be updated: the file size, the tag data, and the metadata offset
if(d->tag->isEmpty()) {
long long newFileSize = d->metadataOffset ? d->metadataOffset : d->fileSize;
// Update the file size
if(d->fileSize != newFileSize) {
insert(ByteVector::fromLongLong(newFileSize, false), 12, 8);
d->fileSize = newFileSize;
}
// Update the metadata offset to 0 since there is no longer a tag
if(d->metadataOffset) {
insert(ByteVector::fromLongLong(0ULL, false), 20, 8);
d->metadataOffset = 0;
}
// Delete the old tag
truncate(newFileSize);
}
else {
ByteVector tagData = d->tag->render(version);
long long newMetadataOffset = d->metadataOffset ? d->metadataOffset : d->fileSize;
long long newFileSize = newMetadataOffset + tagData.size();
long long oldTagSize = d->fileSize - newMetadataOffset;
// Update the file size
if(d->fileSize != newFileSize) {
insert(ByteVector::fromLongLong(newFileSize, false), 12, 8);
d->fileSize = newFileSize;
}
// Update the metadata offset
if(d->metadataOffset != newMetadataOffset) {
insert(ByteVector::fromLongLong(newMetadataOffset, false), 20, 8);
d->metadataOffset = newMetadataOffset;
}
// Delete the old tag and write the new one
insert(tagData, newMetadataOffset, static_cast<size_t>(oldTagSize));
}
return true;
}
void DSF::File::read(AudioProperties::ReadStyle propertiesStyle)
{
if(!isOpen())
return;
// A DSF file consists of four chunks: DSD chunk, format chunk, data chunk, and metadata chunk
// The file format is not chunked in the sense of a RIFF File, though
// DSD chunk
ByteVector chunkName = readBlock(4);
if(chunkName != "DSD ") {
debug("DSF::File::read() -- Not a DSF file.");
setValid(false);
return;
}
long long dsdHeaderSize = readBlock(8).toLongLong(false);
// Integrity check
if(dsdHeaderSize != 28) {
debug("DSF::File::read() -- File is corrupted, wrong DSD header size");
setValid(false);
return;
}
d->fileSize = readBlock(8).toLongLong(false);
// File is malformed or corrupted, allow trailing garbage
if(d->fileSize > length()) {
debug("DSF::File::read() -- File is corrupted wrong length");
setValid(false);
return;
}
d->metadataOffset = readBlock(8).toLongLong(false);
// File is malformed or corrupted
if(d->metadataOffset > d->fileSize) {
debug("DSF::File::read() -- Invalid metadata offset.");
setValid(false);
return;
}
// Format chunk
chunkName = readBlock(4);
if(chunkName != "fmt ") {
debug("DSF::File::read() -- Missing 'fmt ' chunk.");
setValid(false);
return;
}
long long fmtHeaderSize = readBlock(8).toLongLong(false);
if(fmtHeaderSize != 52) {
debug("DSF::File::read() -- File is corrupted, wrong FMT header size");
setValid(false);
return;
}
d->properties = std::make_unique<Properties>(readBlock(fmtHeaderSize), propertiesStyle);
// Skip the data chunk
// A metadata offset of 0 indicates the absence of an ID3v2 tag
if(d->metadataOffset == 0)
d->tag = std::make_unique<ID3v2::Tag>(nullptr, 0, d->ID3v2FrameFactory);
else
d->tag = std::make_unique<ID3v2::Tag>(this, d->metadataOffset,
d->ID3v2FrameFactory);
}

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

@@ -0,0 +1,156 @@
/***************************************************************************
copyright : (C) 2013-2023 Stephen F. Booth
email : me@sbooth.org
***************************************************************************/
/***************************************************************************
* This library is free software; you can redistribute it and/or modify *
* it under the terms of the GNU Lesser General Public License version *
* 2.1 as published by the Free Software Foundation. *
* *
* This library is distributed in the hope that it will be useful, but *
* WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
* Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public *
* License along with this library; if not, write to the Free Software *
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
* 02110-1301 USA *
* *
* Alternatively, this file is available under the Mozilla Public *
* License Version 1.1. You may obtain a copy of the License at *
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#ifndef TAGLIB_DSFFILE_H
#define TAGLIB_DSFFILE_H
#include <memory>
#include "taglib_export.h"
#include "tfile.h"
#include "dsfproperties.h"
#include "id3v2tag.h"
namespace TagLib {
//! An implementation of DSF metadata
/*!
* This is an implementation of DSF metadata using an ID3v2 tag inside the
* metadata chunk.
* The DSF specification is located at
* http://dsd-guide.com/sites/default/files/white-papers/DSFFileFormatSpec_E.pdf
*/
namespace DSF {
//! An implementation of TagLib::File with DSF specific methods
/*!
* This implements and provides an interface for DSF files to the
* TagLib::Tag and TagLib::AudioProperties interfaces by way of implementing
* the abstract TagLib::File API as well as providing some additional
* information specific to DSF files.
*/
class TAGLIB_EXPORT File : public TagLib::File {
public:
/*!
* Constructs a DSD stream file from \a file.
*
* \note In the current implementation, both \a readProperties and
* \a propertiesStyle are ignored. The audio properties are always
* read.
*
* If this file contains an ID3v2 tag, the frames will be created using
* \a frameFactory (default if null).
*/
File(FileName file, bool readProperties = true,
AudioProperties::ReadStyle propertiesStyle =
AudioProperties::Average,
ID3v2::FrameFactory *frameFactory = nullptr);
/*!
* Constructs a DSD stream file from \a stream.
*
* \note In the current implementation, both \a readProperties and
* \a propertiesStyle are ignored. The audio properties are always
* read.
*
* If this file contains an ID3v2 tag, the frames will be created using
* \a frameFactory (default if null).
*
* \note TagLib will *not* take ownership of the stream, the caller is
* responsible for deleting it after the File object.
*/
File(IOStream *stream, bool readProperties = true,
AudioProperties::ReadStyle propertiesStyle =
AudioProperties::Average,
ID3v2::FrameFactory *frameFactory = nullptr);
/*!
* Destroys this instance of the File.
*/
~File() override;
/*!
* Returns the ID3v2 Tag for this file.
*/
ID3v2::Tag *tag() const override;
/*!
* Implements the unified property interface -- export function.
* Forwards to ID3v2::Tag::properties().
*/
PropertyMap properties() const override;
/*!
* Implements the unified property interface -- import function.
* Forwards to ID3v2::Tag::setProperties().
*/
PropertyMap setProperties(const PropertyMap &) override;
/*!
* Returns the DSF::Properties for this file. If no audio properties
* were read then this will return a null pointer.
*/
Properties *audioProperties() const override;
/*!
* Save the file.
*
* This returns \c true if the save was successful.
*/
bool save() override;
/*!
* Save the file.
*
* \a version specifies the ID3v2 version to be used for writing tags.
*/
bool save(ID3v2::Version version);
/*!
* Returns whether or not the given \a stream can be opened as a DSF
* file.
*
* \note This method is designed to do a quick check. The result may
* not necessarily be correct.
*/
static bool isSupported(IOStream *stream);
private:
void read(AudioProperties::ReadStyle propertiesStyle);
class FilePrivate;
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
std::unique_ptr<FilePrivate> d;
};
} // namespace DSF
} // namespace TagLib
#endif

View File

@@ -0,0 +1,134 @@
/***************************************************************************
copyright : (C) 2013-2023 Stephen F. Booth
email : me@sbooth.org
***************************************************************************/
/***************************************************************************
* This library is free software; you can redistribute it and/or modify *
* it under the terms of the GNU Lesser General Public License version *
* 2.1 as published by the Free Software Foundation. *
* *
* This library is distributed in the hope that it will be useful, but *
* WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
* Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public *
* License along with this library; if not, write to the Free Software *
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
* 02110-1301 USA *
* *
* Alternatively, this file is available under the Mozilla Public *
* License Version 1.1. You may obtain a copy of the License at *
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#include "dsfproperties.h"
using namespace TagLib;
class DSF::Properties::PropertiesPrivate
{
public:
PropertiesPrivate() = default;
~PropertiesPrivate() = default;
PropertiesPrivate(const PropertiesPrivate &) = delete;
PropertiesPrivate &operator=(const PropertiesPrivate &) = delete;
// Nomenclature is from DSF file format specification
unsigned int formatVersion = 0;
unsigned int formatID = 0;
unsigned int channelType = 0;
unsigned int channelNum = 0;
unsigned int samplingFrequency = 0;
unsigned int bitsPerSample = 0;
long long sampleCount = 0;
unsigned int blockSizePerChannel = 0;
// Computed
unsigned int bitrate = 0;
unsigned int length = 0;
};
DSF::Properties::Properties(const ByteVector &data, ReadStyle style) :
AudioProperties(style),
d(std::make_unique<PropertiesPrivate>())
{
read(data);
}
DSF::Properties::~Properties() = default;
int DSF::Properties::lengthInMilliseconds() const
{
return d->length;
}
int DSF::Properties::bitrate() const
{
return d->bitrate;
}
int DSF::Properties::sampleRate() const
{
return d->samplingFrequency;
}
int DSF::Properties::channels() const
{
return d->channelNum;
}
int DSF::Properties::formatVersion() const
{
return d->formatVersion;
}
int DSF::Properties::formatID() const
{
return d->formatID;
}
int DSF::Properties::channelType() const
{
return d->channelType;
}
int DSF::Properties::bitsPerSample() const
{
return d->bitsPerSample;
}
long long DSF::Properties::sampleCount() const
{
return d->sampleCount;
}
int DSF::Properties::blockSizePerChannel() const
{
return d->blockSizePerChannel;
}
////////////////////////////////////////////////////////////////////////////////
// private members
////////////////////////////////////////////////////////////////////////////////
void DSF::Properties::read(const ByteVector &data)
{
d->formatVersion = data.toUInt(0U,false);
d->formatID = data.toUInt(4U,false);
d->channelType = data.toUInt(8U,false);
d->channelNum = data.toUInt(12U,false);
d->samplingFrequency = data.toUInt(16U,false);
d->bitsPerSample = data.toUInt(20U,false);
d->sampleCount = data.toLongLong(24U,false);
d->blockSizePerChannel = data.toUInt(32U,false);
d->bitrate = static_cast<unsigned int>(
d->samplingFrequency * d->bitsPerSample * d->channelNum / 1000.0 + 0.5);
d->length = d->samplingFrequency > 0
? static_cast<unsigned int>(static_cast<double>(d->sampleCount) * 1000.0 / d->samplingFrequency + 0.5)
: 0;
}

View File

@@ -0,0 +1,73 @@
/***************************************************************************
copyright : (C) 2013-2023 Stephen F. Booth
email : me@sbooth.org
***************************************************************************/
/***************************************************************************
* This library is free software; you can redistribute it and/or modify *
* it under the terms of the GNU Lesser General Public License version *
* 2.1 as published by the Free Software Foundation. *
* *
* This library is distributed in the hope that it will be useful, but *
* WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
* Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public *
* License along with this library; if not, write to the Free Software *
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
* 02110-1301 USA *
* *
* Alternatively, this file is available under the Mozilla Public *
* License Version 1.1. You may obtain a copy of the License at *
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#ifndef TAGLIB_DSFPROPERTIES_H
#define TAGLIB_DSFPROPERTIES_H
#include <memory>
#include "taglib_export.h"
#include "tbytevector.h"
#include "audioproperties.h"
namespace TagLib {
namespace DSF {
//! An implementation of audio properties for DSF
class TAGLIB_EXPORT Properties : public AudioProperties {
public:
Properties(const ByteVector &data, ReadStyle style);
~Properties() override;
Properties(const Properties &) = delete;
Properties &operator=(const Properties &) = delete;
int lengthInMilliseconds() const override;
int bitrate() const override;
int sampleRate() const override;
int channels() const override;
int formatVersion() const;
int formatID() const;
/*!
* Channel type values: 1 = mono, 2 = stereo, 3 = 3 channels,
* 4 = quad, 5 = 4 channels, 6 = 5 channels, 7 = 5.1 channels
*/
int channelType() const;
int bitsPerSample() const;
long long sampleCount() const;
int blockSizePerChannel() const;
private:
void read(const ByteVector &data);
class PropertiesPrivate;
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
std::unique_ptr<PropertiesPrivate> d;
};
} // namespace DSF
} // namespace TagLib
#endif

View File

@@ -27,40 +27,73 @@
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#include <cstring>
#include <tfile.h>
#include <tfilestream.h>
#include <tstring.h>
#include <tdebug.h>
#include <trefcounter.h>
#include "fileref.h"
#include "asffile.h"
#include <cstring>
#include <utility>
#include "taglib_config.h"
#include "tfilestream.h"
#include "tpropertymap.h"
#include "tstringlist.h"
#include "tvariant.h"
#include "tdebug.h"
#include "mpegfile.h"
#include "vorbisfile.h"
#include "flacfile.h"
#include "oggflacfile.h"
#include "mpcfile.h"
#include "mp4file.h"
#include "wavpackfile.h"
#include "speexfile.h"
#include "opusfile.h"
#include "trueaudiofile.h"
#ifdef TAGLIB_WITH_RIFF
#include "aifffile.h"
#include "wavfile.h"
#endif
#ifdef TAGLIB_WITH_APE
#include "apefile.h"
#include "modfile.h"
#include "s3mfile.h"
#include "mpcfile.h"
#include "wavpackfile.h"
#endif
#ifdef TAGLIB_WITH_ASF
#include "asffile.h"
#endif
#ifdef TAGLIB_WITH_VORBIS
#include "flacfile.h"
#include "speexfile.h"
#include "vorbisfile.h"
#include "oggflacfile.h"
#include "opusfile.h"
#endif
#ifdef TAGLIB_WITH_MOD
#include "itfile.h"
#include "modfile.h"
#include "xmfile.h"
#include "s3mfile.h"
#endif
#ifdef TAGLIB_WITH_MP4
#include "mp4file.h"
#endif
#ifdef TAGLIB_WITH_TRUEAUDIO
#include "trueaudiofile.h"
#endif
#ifdef TAGLIB_WITH_DSF
#include "dsffile.h"
#include "dsdifffile.h"
#endif
#ifdef TAGLIB_WITH_SHORTEN
#include "shortenfile.h"
#endif
#ifdef TAGLIB_WITH_MATROSKA
#include "matroskafile.h"
#endif
using namespace TagLib;
class FileRef::FileTypeResolver::FileTypeResolverPrivate
{
};
class FileRef::StreamTypeResolver::StreamTypeResolverPrivate
{
};
namespace
{
typedef List<const FileRef::FileTypeResolver *> ResolverList;
ResolverList fileTypeResolvers;
List<const FileRef::FileTypeResolver *> fileTypeResolvers;
// Detect the file type by user-defined resolvers.
@@ -68,20 +101,33 @@ namespace
AudioProperties::ReadStyle audioPropertiesStyle)
{
#ifdef _WIN32
if(::strlen(fileName) == 0 && ::wcslen(fileName) == 0)
return 0;
if(::wcslen(fileName) == 0)
return nullptr;
#else
if(::strlen(fileName) == 0)
return 0;
return nullptr;
#endif
ResolverList::ConstIterator it = fileTypeResolvers.begin();
for(; it != fileTypeResolvers.end(); ++it) {
File *file = (*it)->createFile(fileName, readAudioProperties, audioPropertiesStyle);
for(const auto &resolver : std::as_const(fileTypeResolvers)) {
File *file = resolver->createFile(fileName, readAudioProperties, audioPropertiesStyle);
if(file)
return file;
}
return 0;
return nullptr;
}
File *detectByResolvers(IOStream* stream, bool readAudioProperties,
AudioProperties::ReadStyle audioPropertiesStyle)
{
for(const auto &resolver : std::as_const(fileTypeResolvers)) {
if(auto streamResolver = dynamic_cast<const FileRef::StreamTypeResolver *>(resolver)) {
if(File *file = streamResolver->createFileFromStream(
stream, readAudioProperties, audioPropertiesStyle))
return file;
}
}
return nullptr;
}
// Detect the file type based on the file extension.
@@ -96,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
@@ -105,47 +150,93 @@ namespace
// that a default file type resolver is created.
if(ext.isEmpty())
return 0;
return nullptr;
// .oga can be any audio in the Ogg container. So leave it to content-based detection.
if(ext == "MP3")
return new MPEG::File(stream, ID3v2::FrameFactory::instance(), readAudioProperties, audioPropertiesStyle);
if(ext == "OGG")
return new Ogg::Vorbis::File(stream, readAudioProperties, audioPropertiesStyle);
if(ext == "FLAC")
return new FLAC::File(stream, ID3v2::FrameFactory::instance(), readAudioProperties, audioPropertiesStyle);
if(ext == "MPC")
return new MPC::File(stream, readAudioProperties, audioPropertiesStyle);
if(ext == "WV")
return new WavPack::File(stream, readAudioProperties, audioPropertiesStyle);
if(ext == "SPX")
return new Ogg::Speex::File(stream, readAudioProperties, audioPropertiesStyle);
if(ext == "OPUS")
return new Ogg::Opus::File(stream, readAudioProperties, audioPropertiesStyle);
if(ext == "TTA")
return new TrueAudio::File(stream, readAudioProperties, audioPropertiesStyle);
if(ext == "M4A" || ext == "M4R" || ext == "M4B" || ext == "M4P" || ext == "MP4" || ext == "3G2" || ext == "M4V")
return new MP4::File(stream, readAudioProperties, audioPropertiesStyle);
if(ext == "WMA" || ext == "ASF")
return new ASF::File(stream, readAudioProperties, audioPropertiesStyle);
if(ext == "AIF" || ext == "AIFF" || ext == "AFC" || ext == "AIFC")
return new RIFF::AIFF::File(stream, readAudioProperties, audioPropertiesStyle);
if(ext == "WAV")
return new RIFF::WAV::File(stream, readAudioProperties, audioPropertiesStyle);
if(ext == "APE")
return new APE::File(stream, readAudioProperties, audioPropertiesStyle);
// module, nst and wow are possible but uncommon extensions
if(ext == "MOD" || ext == "MODULE" || ext == "NST" || ext == "WOW")
return new Mod::File(stream, readAudioProperties, audioPropertiesStyle);
if(ext == "S3M")
return new S3M::File(stream, readAudioProperties, audioPropertiesStyle);
if(ext == "IT")
return new IT::File(stream, readAudioProperties, audioPropertiesStyle);
if(ext == "XM")
return new XM::File(stream, readAudioProperties, audioPropertiesStyle);
File *file = nullptr;
return 0;
if(ext == "MP3" || ext == "MP2" || ext == "AAC")
file = new MPEG::File(stream, readAudioProperties, audioPropertiesStyle);
#ifdef TAGLIB_WITH_VORBIS
else if(ext == "OGG")
file = new Ogg::Vorbis::File(stream, readAudioProperties, audioPropertiesStyle);
else if(ext == "OGA") {
/* .oga can be any audio in the Ogg container. First try FLAC, then Vorbis. */
file = new Ogg::FLAC::File(stream, readAudioProperties, audioPropertiesStyle);
if(!file->isValid()) {
delete file;
file = new Ogg::Vorbis::File(stream, readAudioProperties, audioPropertiesStyle);
}
}
else if(ext == "FLAC")
file = new FLAC::File(stream, readAudioProperties, audioPropertiesStyle);
else if(ext == "SPX")
file = new Ogg::Speex::File(stream, readAudioProperties, audioPropertiesStyle);
else if(ext == "OPUS")
file = new Ogg::Opus::File(stream, readAudioProperties, audioPropertiesStyle);
#endif
#ifdef TAGLIB_WITH_APE
else if(ext == "MPC")
file = new MPC::File(stream, readAudioProperties, audioPropertiesStyle);
else if(ext == "WV")
file = new WavPack::File(stream, readAudioProperties, audioPropertiesStyle);
else if(ext == "APE")
file = new APE::File(stream, readAudioProperties, audioPropertiesStyle);
#endif
#ifdef TAGLIB_WITH_TRUEAUDIO
else if(ext == "TTA")
file = new TrueAudio::File(stream, readAudioProperties, audioPropertiesStyle);
#endif
#ifdef TAGLIB_WITH_MP4
else if(ext == "M4A" || ext == "M4R" || ext == "M4B" || ext == "M4P" || ext == "MP4" || ext == "3G2" || ext == "M4V")
file = new MP4::File(stream, readAudioProperties, audioPropertiesStyle);
#endif
#ifdef TAGLIB_WITH_ASF
else if(ext == "WMA" || ext == "ASF")
file = new ASF::File(stream, readAudioProperties, audioPropertiesStyle);
#endif
#ifdef TAGLIB_WITH_RIFF
else if(ext == "AIF" || ext == "AIFF" || ext == "AFC" || ext == "AIFC")
file = new RIFF::AIFF::File(stream, readAudioProperties, audioPropertiesStyle);
else if(ext == "WAV")
file = new RIFF::WAV::File(stream, readAudioProperties, audioPropertiesStyle);
#endif
#ifdef TAGLIB_WITH_MOD
// module, nst and wow are possible but uncommon extensions
else if(ext == "MOD" || ext == "MODULE" || ext == "NST" || ext == "WOW")
file = new Mod::File(stream, readAudioProperties, audioPropertiesStyle);
else if(ext == "S3M")
file = new S3M::File(stream, readAudioProperties, audioPropertiesStyle);
else if(ext == "IT")
file = new IT::File(stream, readAudioProperties, audioPropertiesStyle);
else if(ext == "XM")
file = new XM::File(stream, readAudioProperties, audioPropertiesStyle);
#endif
#ifdef TAGLIB_WITH_DSF
else if(ext == "DSF")
file = new DSF::File(stream, readAudioProperties, audioPropertiesStyle);
else if(ext == "DFF" || ext == "DSDIFF")
file = new DSDIFF::File(stream, readAudioProperties, audioPropertiesStyle);
#endif
#ifdef TAGLIB_WITH_SHORTEN
else if(ext == "SHN")
file = new Shorten::File(stream, readAudioProperties, audioPropertiesStyle);
#endif
#ifdef TAGLIB_WITH_MATROSKA
else if(ext == "MKA" || ext == "MKV" || ext == "WEBM")
file = new Matroska::File(stream, readAudioProperties);
#endif
// if file is not valid, leave it to content-based detection.
if(file) {
if(file->isValid())
return file;
delete file;
}
return nullptr;
}
// Detect the file type based on the actual content of the stream.
@@ -153,137 +244,105 @@ namespace
File *detectByContent(IOStream *stream, bool readAudioProperties,
AudioProperties::ReadStyle audioPropertiesStyle)
{
File *file = 0;
File *file = nullptr;
if(MPEG::File::isSupported(stream))
file = new MPEG::File(stream, ID3v2::FrameFactory::instance(), readAudioProperties, audioPropertiesStyle);
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, ID3v2::FrameFactory::instance(), 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);
file = new FLAC::File(stream, readAudioProperties, audioPropertiesStyle);
else if(Ogg::Speex::File::isSupported(stream))
file = new Ogg::Speex::File(stream, readAudioProperties, audioPropertiesStyle);
else if(Ogg::Opus::File::isSupported(stream))
file = new Ogg::Opus::File(stream, readAudioProperties, audioPropertiesStyle);
#endif
#ifdef TAGLIB_WITH_APE
else if(MPC::File::isSupported(stream))
file = new MPC::File(stream, readAudioProperties, audioPropertiesStyle);
else if(WavPack::File::isSupported(stream))
file = new WavPack::File(stream, readAudioProperties, audioPropertiesStyle);
else if(APE::File::isSupported(stream))
file = new APE::File(stream, readAudioProperties, audioPropertiesStyle);
#endif
#ifdef TAGLIB_WITH_TRUEAUDIO
else if(TrueAudio::File::isSupported(stream))
file = new TrueAudio::File(stream, readAudioProperties, audioPropertiesStyle);
#endif
#ifdef TAGLIB_WITH_MP4
else if(MP4::File::isSupported(stream))
file = new MP4::File(stream, readAudioProperties, audioPropertiesStyle);
#endif
#ifdef TAGLIB_WITH_ASF
else if(ASF::File::isSupported(stream))
file = new ASF::File(stream, readAudioProperties, audioPropertiesStyle);
#endif
#ifdef TAGLIB_WITH_RIFF
else if(RIFF::AIFF::File::isSupported(stream))
file = new RIFF::AIFF::File(stream, readAudioProperties, audioPropertiesStyle);
else if(RIFF::WAV::File::isSupported(stream))
file = new RIFF::WAV::File(stream, readAudioProperties, audioPropertiesStyle);
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.
if(file) {
if(file->isValid())
return file;
else
delete file;
}
return 0;
}
// Internal function that supports FileRef::create().
// This looks redundant, but necessary in order not to change the previous
// behavior of FileRef::create().
File* createInternal(FileName fileName, bool readAudioProperties,
AudioProperties::ReadStyle audioPropertiesStyle)
{
File *file = detectByResolvers(fileName, readAudioProperties, audioPropertiesStyle);
if(file)
return file;
#ifdef _WIN32
const String s = fileName.toString();
#else
const String s(fileName);
#endif
String ext;
const int pos = s.rfind(".");
if(pos != -1)
ext = s.substr(pos + 1).upper();
if(ext.isEmpty())
return 0;
if(ext == "MP3")
return new MPEG::File(fileName, ID3v2::FrameFactory::instance(), readAudioProperties, audioPropertiesStyle);
if(ext == "OGG")
return new Ogg::Vorbis::File(fileName, readAudioProperties, audioPropertiesStyle);
if(ext == "OGA") {
/* .oga can be any audio in the Ogg container. First try FLAC, then Vorbis. */
File *file = new Ogg::FLAC::File(fileName, readAudioProperties, audioPropertiesStyle);
if(file->isValid())
return file;
delete file;
return new Ogg::Vorbis::File(fileName, readAudioProperties, audioPropertiesStyle);
}
if(ext == "FLAC")
return new FLAC::File(fileName, ID3v2::FrameFactory::instance(), readAudioProperties, audioPropertiesStyle);
if(ext == "MPC")
return new MPC::File(fileName, readAudioProperties, audioPropertiesStyle);
if(ext == "WV")
return new WavPack::File(fileName, readAudioProperties, audioPropertiesStyle);
if(ext == "SPX")
return new Ogg::Speex::File(fileName, readAudioProperties, audioPropertiesStyle);
if(ext == "OPUS")
return new Ogg::Opus::File(fileName, readAudioProperties, audioPropertiesStyle);
if(ext == "TTA")
return new TrueAudio::File(fileName, readAudioProperties, audioPropertiesStyle);
if(ext == "M4A" || ext == "M4R" || ext == "M4B" || ext == "M4P" || ext == "MP4" || ext == "3G2" || ext == "M4V")
return new MP4::File(fileName, readAudioProperties, audioPropertiesStyle);
if(ext == "WMA" || ext == "ASF")
return new ASF::File(fileName, readAudioProperties, audioPropertiesStyle);
if(ext == "AIF" || ext == "AIFF" || ext == "AFC" || ext == "AIFC")
return new RIFF::AIFF::File(fileName, readAudioProperties, audioPropertiesStyle);
if(ext == "WAV")
return new RIFF::WAV::File(fileName, readAudioProperties, audioPropertiesStyle);
if(ext == "APE")
return new APE::File(fileName, readAudioProperties, audioPropertiesStyle);
// module, nst and wow are possible but uncommon extensions
if(ext == "MOD" || ext == "MODULE" || ext == "NST" || ext == "WOW")
return new Mod::File(fileName, readAudioProperties, audioPropertiesStyle);
if(ext == "S3M")
return new S3M::File(fileName, readAudioProperties, audioPropertiesStyle);
if(ext == "IT")
return new IT::File(fileName, readAudioProperties, audioPropertiesStyle);
if(ext == "XM")
return new XM::File(fileName, readAudioProperties, audioPropertiesStyle);
return 0;
return nullptr;
}
}
class FileRef::FileRefPrivate : public RefCounter
} // namespace
class FileRef::FileRefPrivate
{
public:
FileRefPrivate() :
RefCounter(),
file(0),
stream(0) {}
~FileRefPrivate() {
FileRefPrivate() = default;
~FileRefPrivate()
{
delete file;
delete stream;
}
File *file;
IOStream *stream;
FileRefPrivate(const FileRefPrivate &) = delete;
FileRefPrivate &operator=(const FileRefPrivate &) = delete;
bool isNull() const
{
return !file || !file->isValid();
}
bool isNullWithDebugMessage([[maybe_unused]] const String &methodName) const
{
if(isNull()) {
debug("FileRef::" + methodName + "() - Called without a valid file.");
return true;
}
return false;
}
File *file { nullptr };
IOStream *stream { nullptr };
};
////////////////////////////////////////////////////////////////////////////////
@@ -291,55 +350,93 @@ public:
////////////////////////////////////////////////////////////////////////////////
FileRef::FileRef() :
d(new FileRefPrivate())
d(std::make_shared<FileRefPrivate>())
{
}
FileRef::FileRef(FileName fileName, bool readAudioProperties,
AudioProperties::ReadStyle audioPropertiesStyle) :
d(new FileRefPrivate())
d(std::make_shared<FileRefPrivate>())
{
parse(fileName, readAudioProperties, audioPropertiesStyle);
}
FileRef::FileRef(IOStream* stream, bool readAudioProperties, AudioProperties::ReadStyle audioPropertiesStyle) :
d(new FileRefPrivate())
FileRef::FileRef(IOStream *stream, bool readAudioProperties, AudioProperties::ReadStyle audioPropertiesStyle) :
d(std::make_shared<FileRefPrivate>())
{
parse(stream, readAudioProperties, audioPropertiesStyle);
}
FileRef::FileRef(File *file) :
d(new FileRefPrivate())
d(std::make_shared<FileRefPrivate>())
{
d->file = file;
}
FileRef::FileRef(const FileRef &ref) :
d(ref.d)
{
d->ref();
}
FileRef::FileRef(const FileRef &) = default;
FileRef::~FileRef()
{
if(d->deref())
delete d;
}
FileRef::~FileRef() = default;
Tag *FileRef::tag() const
{
if(isNull()) {
debug("FileRef::tag() - Called without a valid file.");
return 0;
if(d->isNullWithDebugMessage(__func__)) {
return nullptr;
}
return d->file->tag();
}
PropertyMap FileRef::properties() const
{
if(d->isNullWithDebugMessage(__func__)) {
return PropertyMap();
}
return d->file->properties();
}
void FileRef::removeUnsupportedProperties(const StringList& properties)
{
if(d->isNullWithDebugMessage(__func__)) {
return;
}
return d->file->removeUnsupportedProperties(properties);
}
PropertyMap FileRef::setProperties(const PropertyMap &properties)
{
if(d->isNullWithDebugMessage(__func__)) {
return PropertyMap();
}
return d->file->setProperties(properties);
}
StringList FileRef::complexPropertyKeys() const
{
if(d->isNullWithDebugMessage(__func__)) {
return StringList();
}
return d->file->complexPropertyKeys();
}
List<VariantMap> FileRef::complexProperties(const String &key) const
{
if(d->isNullWithDebugMessage(__func__)) {
return List<VariantMap>();
}
return d->file->complexProperties(key);
}
bool FileRef::setComplexProperties(const String &key, const List<VariantMap> &value)
{
if(d->isNullWithDebugMessage(__func__)) {
return false;
}
return d->file->setComplexProperties(key, value);
}
AudioProperties *FileRef::audioProperties() const
{
if(isNull()) {
debug("FileRef::audioProperties() - Called without a valid file.");
return 0;
if(d->isNullWithDebugMessage(__func__)) {
return nullptr;
}
return d->file->audioProperties();
}
@@ -351,8 +448,7 @@ File *FileRef::file() const
bool FileRef::save()
{
if(isNull()) {
debug("FileRef::save() - Called without a valid file.");
if(d->isNullWithDebugMessage(__func__)) {
return false;
}
return d->file->save();
@@ -364,19 +460,34 @@ const FileRef::FileTypeResolver *FileRef::addFileTypeResolver(const FileRef::Fil
return resolver;
}
void FileRef::clearFileTypeResolvers() // static
{
fileTypeResolvers.clear();
}
StringList FileRef::defaultFileExtensions()
{
StringList l;
l.append("mp3");
l.append("mp2");
l.append("aac");
#ifdef TAGLIB_WITH_VORBIS
l.append("ogg");
l.append("flac");
l.append("oga");
l.append("opus");
l.append("mp3");
l.append("spx");
#endif
#ifdef TAGLIB_WITH_APE
l.append("mpc");
l.append("wv");
l.append("spx");
l.append("ape");
#endif
#ifdef TAGLIB_WITH_TRUEAUDIO
l.append("tta");
#endif
#ifdef TAGLIB_WITH_MP4
l.append("m4a");
l.append("m4r");
l.append("m4b");
@@ -384,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"
@@ -399,22 +515,32 @@ StringList FileRef::defaultFileExtensions()
l.append("s3m");
l.append("it");
l.append("xm");
#endif
#ifdef TAGLIB_WITH_DSF
l.append("dsf");
l.append("dff");
l.append("dsdiff"); // alias for "dff"
#endif
#ifdef TAGLIB_WITH_SHORTEN
l.append("shn");
#endif
#ifdef TAGLIB_WITH_MATROSKA
l.append("mkv");
l.append("mka");
l.append("webm");
#endif
return l;
}
bool FileRef::isNull() const
{
return (!d->file || !d->file->isValid());
return d->isNull();
}
FileRef &FileRef::operator=(const FileRef &ref)
{
FileRef(ref).swap(*this);
return *this;
}
FileRef &FileRef::operator=(const FileRef &) = default;
void FileRef::swap(FileRef &ref)
void FileRef::swap(FileRef &ref) noexcept
{
using std::swap;
@@ -423,18 +549,12 @@ void FileRef::swap(FileRef &ref)
bool FileRef::operator==(const FileRef &ref) const
{
return (ref.d->file == d->file);
return ref.d->file == d->file;
}
bool FileRef::operator!=(const FileRef &ref) const
{
return (ref.d->file != d->file);
}
File *FileRef::create(FileName fileName, bool readAudioProperties,
AudioProperties::ReadStyle audioPropertiesStyle) // static
{
return createInternal(fileName, readAudioProperties, audioPropertiesStyle);
return ref.d->file != d->file;
}
////////////////////////////////////////////////////////////////////////////////
@@ -466,12 +586,18 @@ void FileRef::parse(FileName fileName, bool readAudioProperties,
// Stream have to be closed here if failed to resolve file types.
delete d->stream;
d->stream = 0;
d->stream = nullptr;
}
void FileRef::parse(IOStream *stream, bool readAudioProperties,
AudioProperties::ReadStyle audioPropertiesStyle)
{
// Try user-defined stream resolvers.
d->file = detectByResolvers(stream, readAudioProperties, audioPropertiesStyle);
if(d->file)
return;
// Try user-defined resolvers.
d->file = detectByResolvers(stream->name(), readAudioProperties, audioPropertiesStyle);
@@ -488,3 +614,9 @@ void FileRef::parse(IOStream *stream, bool readAudioProperties,
d->file = detectByContent(stream, readAudioProperties, audioPropertiesStyle);
}
FileRef::FileTypeResolver::FileTypeResolver() = default;
FileRef::FileTypeResolver::~FileTypeResolver() = default;
FileRef::StreamTypeResolver::StreamTypeResolver() = default;
FileRef::StreamTypeResolver::~StreamTypeResolver() = default;

View File

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

View File

@@ -23,21 +23,19 @@
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#include <tbytevector.h>
#include <tstring.h>
#include <tlist.h>
#include <tdebug.h>
#include <tagunion.h>
#include <tpropertymap.h>
#include <tagutils.h>
#include <id3v2header.h>
#include <id3v2tag.h>
#include <id3v1tag.h>
#include <xiphcomment.h>
#include "flacpicture.h"
#include "flacfile.h"
#include <algorithm>
#include <utility>
#include "tdebug.h"
#include "tpropertymap.h"
#include "tagunion.h"
#include "tagutils.h"
#include "id3v2tag.h"
#include "id3v1tag.h"
#include "xiphcomment.h"
#include "flacpicture.h"
#include "flacmetadatablock.h"
#include "flacunknownmetadatablock.h"
@@ -45,54 +43,38 @@ using namespace TagLib;
namespace
{
typedef List<FLAC::MetadataBlock *> BlockList;
typedef BlockList::Iterator BlockIterator;
typedef BlockList::Iterator BlockConstIterator;
enum { FlacXiphIndex = 0, FlacID3v2Index = 1, FlacID3v1Index = 2 };
const long MinPaddingLength = 4096;
const long MaxPaddingLegnth = 1024 * 1024;
constexpr long MinPaddingLength = 4096;
constexpr long MaxPaddingLegnth = 1024 * 1024;
const char LastBlockFlag = '\x80';
}
constexpr char LastBlockFlag = '\x80';
} // namespace
class FLAC::File::FilePrivate
{
public:
FilePrivate(const ID3v2::FrameFactory *frameFactory = ID3v2::FrameFactory::instance()) :
ID3v2FrameFactory(frameFactory),
ID3v2Location(-1),
ID3v2OriginalSize(0),
ID3v1Location(-1),
properties(0),
flacStart(0),
streamStart(0),
scanned(false)
ID3v2FrameFactory(frameFactory)
{
blocks.setAutoDelete(true);
}
~FilePrivate()
{
delete properties;
}
const ID3v2::FrameFactory *ID3v2FrameFactory;
long ID3v2Location;
long ID3v2OriginalSize;
offset_t ID3v2Location { -1 };
long ID3v2OriginalSize { 0 };
long ID3v1Location;
offset_t ID3v1Location { -1 };
TagUnion tag;
Properties *properties;
std::unique_ptr<Properties> properties;
ByteVector xiphCommentData;
BlockList blocks;
List<FLAC::MetadataBlock *> blocks;
long flacStart;
long streamStart;
bool scanned;
offset_t flacStart { 0 };
offset_t streamStart { 0 };
bool scanned { false };
};
////////////////////////////////////////////////////////////////////////////////
@@ -104,16 +86,19 @@ 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;
}
////////////////////////////////////////////////////////////////////////////////
// public members
////////////////////////////////////////////////////////////////////////////////
FLAC::File::File(FileName file, bool readProperties, Properties::ReadStyle) :
FLAC::File::File(FileName file, bool readProperties,
Properties::ReadStyle,
ID3v2::FrameFactory *frameFactory) :
TagLib::File(file),
d(new FilePrivate())
d(std::make_unique<FilePrivate>(
frameFactory ? frameFactory : ID3v2::FrameFactory::instance()))
{
if(isOpen())
read(readProperties);
@@ -122,7 +107,18 @@ FLAC::File::File(FileName file, bool readProperties, Properties::ReadStyle) :
FLAC::File::File(FileName file, ID3v2::FrameFactory *frameFactory,
bool readProperties, Properties::ReadStyle) :
TagLib::File(file),
d(new FilePrivate(frameFactory))
d(std::make_unique<FilePrivate>(frameFactory))
{
if(isOpen())
read(readProperties);
}
FLAC::File::File(IOStream *stream, bool readProperties,
Properties::ReadStyle,
ID3v2::FrameFactory *frameFactory) :
TagLib::File(stream),
d(std::make_unique<FilePrivate>(
frameFactory ? frameFactory : ID3v2::FrameFactory::instance()))
{
if(isOpen())
read(readProperties);
@@ -131,16 +127,13 @@ FLAC::File::File(FileName file, ID3v2::FrameFactory *frameFactory,
FLAC::File::File(IOStream *stream, ID3v2::FrameFactory *frameFactory,
bool readProperties, Properties::ReadStyle) :
TagLib::File(stream),
d(new FilePrivate(frameFactory))
d(std::make_unique<FilePrivate>(frameFactory))
{
if(isOpen())
read(readProperties);
}
FLAC::File::~File()
{
delete d;
}
FLAC::File::~File() = default;
TagLib::Tag *FLAC::File::tag() const
{
@@ -162,9 +155,72 @@ PropertyMap FLAC::File::setProperties(const PropertyMap &properties)
return xiphComment(true)->setProperties(properties);
}
StringList FLAC::File::complexPropertyKeys() const
{
StringList keys = TagLib::File::complexPropertyKeys();
if(!keys.contains("PICTURE")) {
if(std::any_of(d->blocks.cbegin(), d->blocks.cend(),
[](MetadataBlock *block) {
return dynamic_cast<Picture *>(block) != nullptr;
})) {
keys.append("PICTURE");
}
}
return keys;
}
List<VariantMap> FLAC::File::complexProperties(const String &key) const
{
if(const String uppercaseKey = key.upper(); uppercaseKey == "PICTURE") {
List<VariantMap> props;
for(const auto &block : std::as_const(d->blocks)) {
if(auto picture = dynamic_cast<Picture *>(block)) {
VariantMap property;
property.insert("data", picture->data());
property.insert("mimeType", picture->mimeType());
property.insert("description", picture->description());
property.insert("pictureType",
FLAC::Picture::typeToString(picture->type()));
property.insert("width", picture->width());
property.insert("height", picture->height());
property.insert("numColors", picture->numColors());
property.insert("colorDepth", picture->colorDepth());
props.append(property);
}
}
return props;
}
return TagLib::File::complexProperties(key);
}
bool FLAC::File::setComplexProperties(const String &key, const List<VariantMap> &value)
{
if(const String uppercaseKey = key.upper(); uppercaseKey == "PICTURE") {
removePictures();
for(const auto &property : value) {
auto picture = new FLAC::Picture;
picture->setData(property.value("data").value<ByteVector>());
picture->setMimeType(property.value("mimeType").value<String>());
picture->setDescription(property.value("description").value<String>());
picture->setType(FLAC::Picture::typeFromString(
property.value("pictureType").value<String>()));
picture->setWidth(property.value("width").value<int>());
picture->setHeight(property.value("height").value<int>());
picture->setNumColors(property.value("numColors").value<int>());
picture->setColorDepth(property.value("colorDepth").value<int>());
addPicture(picture);
}
}
else {
return TagLib::File::setComplexProperties(key, value);
}
return true;
}
FLAC::Properties *FLAC::File::audioProperties() const
{
return d->properties;
return d->properties.get();
}
bool FLAC::File::save()
@@ -189,7 +245,7 @@ bool FLAC::File::save()
MetadataBlock *commentBlock =
new UnknownMetadataBlock(MetadataBlock::VorbisComment, d->xiphCommentData);
for(BlockIterator it = d->blocks.begin(); it != d->blocks.end();) {
for(auto it = d->blocks.begin(); it != d->blocks.end();) {
if((*it)->code() == MetadataBlock::VorbisComment) {
// Remove the old Vorbis Comment block
delete *it;
@@ -199,7 +255,7 @@ bool FLAC::File::save()
if(commentBlock && (*it)->code() == MetadataBlock::Picture) {
// Set the new Vorbis Comment block before the first picture block
d->blocks.insert(it, commentBlock);
commentBlock = 0;
commentBlock = nullptr;
}
++it;
}
@@ -209,18 +265,25 @@ bool FLAC::File::save()
// Render data for the metadata blocks
ByteVector data;
for(BlockConstIterator it = d->blocks.begin(); it != d->blocks.end(); ++it) {
for(auto it = d->blocks.begin(); it != d->blocks.end();) {
ByteVector blockData = (*it)->render();
ByteVector blockHeader = ByteVector::fromUInt(blockData.size());
blockHeader[0] = (*it)->code();
if(blockHeader[0] != 0) {
debug("FLAC::File::save() -- Removing too large block.");
delete *it;
it = d->blocks.erase(it);
continue;
}
blockHeader[0] = static_cast<char>((*it)->code());
data.append(blockHeader);
data.append(blockData);
++it;
}
// Compute the amount of padding, and append that to data.
long originalLength = d->streamStart - d->flacStart;
long paddingLength = originalLength - data.size() - 4;
offset_t originalLength = d->streamStart - d->flacStart;
offset_t paddingLength = originalLength - data.size() - 4;
if(paddingLength <= 0) {
paddingLength = MinPaddingLength;
@@ -228,15 +291,15 @@ bool FLAC::File::save()
else {
// Padding won't increase beyond 1% of the file size or 1MB.
long threshold = length() / 100;
threshold = std::max(threshold, MinPaddingLength);
threshold = std::min(threshold, MaxPaddingLegnth);
offset_t threshold = length() / 100;
threshold = std::max<offset_t>(threshold, MinPaddingLength);
threshold = std::min<offset_t>(threshold, MaxPaddingLegnth);
if(paddingLength > threshold)
paddingLength = MinPaddingLength;
}
ByteVector paddingHeader = ByteVector::fromUInt(paddingLength);
ByteVector paddingHeader = ByteVector::fromUInt(static_cast<unsigned int>(paddingLength));
paddingHeader[0] = static_cast<char>(MetadataBlock::Padding | LastBlockFlag);
data.append(paddingHeader);
data.resize(static_cast<unsigned int>(data.size() + paddingLength));
@@ -245,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
@@ -262,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();
}
@@ -317,7 +380,8 @@ bool FLAC::File::save()
ID3v2::Tag *FLAC::File::ID3v2Tag(bool create)
{
return d->tag.access<ID3v2::Tag>(FlacID3v2Index, create);
return d->tag.access<ID3v2::Tag>(FlacID3v2Index, create,
d->ID3v2FrameFactory);
}
ID3v1::Tag *FLAC::File::ID3v1Tag(bool create)
@@ -330,29 +394,11 @@ Ogg::XiphComment *FLAC::File::xiphComment(bool create)
return d->tag.access<Ogg::XiphComment>(FlacXiphIndex, create);
}
void FLAC::File::setID3v2FrameFactory(const ID3v2::FrameFactory *factory)
{
d->ID3v2FrameFactory = factory;
}
ByteVector FLAC::File::streamInfoData()
{
debug("FLAC::File::streamInfoData() -- This function is obsolete. Returning an empty ByteVector.");
return ByteVector();
}
long FLAC::File::streamLength()
{
debug("FLAC::File::streamLength() -- This function is obsolete. Returning zero.");
return 0;
}
List<FLAC::Picture *> FLAC::File::pictureList()
{
List<Picture *> pictures;
for(BlockConstIterator it = d->blocks.begin(); it != d->blocks.end(); ++it) {
Picture *picture = dynamic_cast<Picture *>(*it);
if(picture) {
for(const auto &block : std::as_const(d->blocks)) {
if(auto picture = dynamic_cast<Picture *>(block)) {
pictures.append(picture);
}
}
@@ -366,7 +412,7 @@ void FLAC::File::addPicture(Picture *picture)
void FLAC::File::removePicture(Picture *picture, bool del)
{
BlockIterator it = d->blocks.find(picture);
auto it = d->blocks.find(picture);
if(it != d->blocks.end())
d->blocks.erase(it);
@@ -376,7 +422,7 @@ void FLAC::File::removePicture(Picture *picture, bool del)
void FLAC::File::removePictures()
{
for(BlockIterator it = d->blocks.begin(); it != d->blocks.end(); ) {
for(auto it = d->blocks.begin(); it != d->blocks.end(); ) {
if(dynamic_cast<Picture *>(*it)) {
delete *it;
it = d->blocks.erase(it);
@@ -390,10 +436,10 @@ void FLAC::File::removePictures()
void FLAC::File::strip(int tags)
{
if(tags & ID3v1)
d->tag.set(FlacID3v1Index, 0);
d->tag.set(FlacID3v1Index, nullptr);
if(tags & ID3v2)
d->tag.set(FlacID3v2Index, 0);
d->tag.set(FlacID3v2Index, nullptr);
if(tags & XiphComment) {
xiphComment()->removeAllFields();
@@ -408,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;
}
////////////////////////////////////////////////////////////////////////////////
@@ -456,14 +502,14 @@ void FLAC::File::read(bool readProperties)
const ByteVector infoData = d->blocks.front()->render();
long streamLength;
offset_t streamLength;
if(d->ID3v1Location >= 0)
streamLength = d->ID3v1Location - d->streamStart;
else
streamLength = length() - d->streamStart;
d->properties = new Properties(infoData, streamLength);
d->properties = std::make_unique<Properties>(infoData, streamLength);
}
}
@@ -477,7 +523,7 @@ void FLAC::File::scan()
if(!isValid())
return;
long nextBlockOffset;
offset_t nextBlockOffset;
if(d->ID3v2Location >= 0)
nextBlockOffset = find("fLaC", d->ID3v2Location + d->ID3v2OriginalSize);
@@ -497,6 +543,11 @@ void FLAC::File::scan()
seek(nextBlockOffset);
const ByteVector header = readBlock(4);
if(header.size() != 4) {
debug("FLAC::File::scan() -- Failed to read a block header");
setValid(false);
return;
}
// Header format (from spec):
// <1> Last-metadata-block flag
@@ -537,7 +588,7 @@ void FLAC::File::scan()
return;
}
MetadataBlock *block = 0;
MetadataBlock *block = nullptr;
// Found the vorbis-comment
if(blockType == MetadataBlock::VorbisComment) {
@@ -550,7 +601,7 @@ void FLAC::File::scan()
}
}
else if(blockType == MetadataBlock::Picture) {
FLAC::Picture *picture = new FLAC::Picture();
auto picture = new FLAC::Picture();
if(picture->parse(data)) {
block = picture;
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,70 @@
/***************************************************************************
* This library is free software; you can redistribute it and/or modify *
* it under the terms of the GNU Lesser General Public License version *
* 2.1 as published by the Free Software Foundation. *
* *
* This library is distributed in the hope that it will be useful, but *
* WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
* Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public *
* License along with this library; if not, write to the Free Software *
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
* 02110-1301 USA *
* *
* Alternatively, this file is available under the Mozilla Public *
* License Version 1.1. You may obtain a copy of the License at *
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#include "ebmlbinaryelement.h"
#include "ebmlutils.h"
#include "tfile.h"
#include "tdebug.h"
using namespace TagLib;
EBML::BinaryElement::BinaryElement(Id id, int sizeLength, offset_t dataSize):
Element(id, sizeLength, dataSize)
{
}
EBML::BinaryElement::BinaryElement(Id id, int sizeLength, offset_t dataSize, offset_t):
Element(id, sizeLength, dataSize)
{
}
EBML::BinaryElement::BinaryElement(Id id):
Element(id, 0, 0)
{
}
const ByteVector& EBML::BinaryElement::getValue() const
{
return value;
}
void EBML::BinaryElement::setValue(const ByteVector& val)
{
value = val;
}
bool EBML::BinaryElement::read(File &file)
{
value = file.readBlock(dataSize);
if(value.size() != dataSize) {
debug("Failed to read binary element");
return false;
}
return true;
}
ByteVector EBML::BinaryElement::render()
{
ByteVector buffer = renderId();
dataSize = value.size();
buffer.append(renderVINT(dataSize, 0));
buffer.append(value);
return buffer;
}

View File

@@ -0,0 +1,51 @@
/***************************************************************************
* This library is free software; you can redistribute it and/or modify *
* it under the terms of the GNU Lesser General Public License version *
* 2.1 as published by the Free Software Foundation. *
* *
* This library is distributed in the hope that it will be useful, but *
* WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
* Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public *
* License along with this library; if not, write to the Free Software *
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
* 02110-1301 USA *
* *
* Alternatively, this file is available under the Mozilla Public *
* License Version 1.1. You may obtain a copy of the License at *
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#ifndef TAGLIB_EBMLBINARYELEMENT_H
#define TAGLIB_EBMLBINARYELEMENT_H
#ifndef DO_NOT_DOCUMENT
#include "ebmlelement.h"
#include "tbytevector.h"
namespace TagLib {
class File;
namespace EBML {
class BinaryElement : public Element
{
public:
BinaryElement(Id id, int sizeLength, offset_t dataSize);
BinaryElement(Id id, int sizeLength, offset_t dataSize, offset_t);
explicit BinaryElement(Id id);
const ByteVector &getValue() const;
void setValue(const ByteVector &val);
bool read(File &file) override;
ByteVector render() override;
private:
ByteVector value;
};
}
}
#endif
#endif

View File

@@ -0,0 +1,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

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