160 Commits

Author SHA1 Message Date
Urs Fleisch
9914519d43 Update NEWS for v1.13beta 2022-09-19 18:07:31 +02:00
Urs Fleisch
f80d11ed2a MP4: Fix heap-buffer-overflow in mp4properties.cpp (#1058) 2022-09-05 06:32:53 +02:00
Urs Fleisch
4e7f844ea6 Correctly parse ID3v2.4.0 multiple strings with single BOM (#1055)
Some ID3v2.4.0 frames such as text information frames support multiple strings
separated by the termination code of the character encoding. If the encoding
is $01 UTF-16 with BOM, all strings shall have the same byte order. In the
multi strings written by TagLib, all string elements of such a multi string
have a BOM. However, I have often seen tags where a BOM exists only at the
beginning, i.e. at the start of the first string. In such a case, TagLib will
only return a list with the first string and a second empty string. This
commit will detect such cases and parse the strings without BOM according to
the BOM of the first string.
2022-08-01 09:27:35 +02:00
Stephen F. Booth
50b89ad19a Fix ID3v2 version handling for embedded frames 2022-07-24 15:28:45 +02:00
Urs Fleisch
0470c2894d Use property "WORK" instead of "CONTENTGROUP" for ID3v2 "TIT1" frame
Also use "WORK" for ASF "WM/ContentGroupDescription", as it corresponds
to "TIT1" according to
https://docs.microsoft.com/en-us/windows/win32/wmformat/id3-tag-support.
Add property "COMPILATION" for ID3v2 "TCMP" to be consistent with
MP4 "cpil".
2022-03-15 17:52:44 +01:00
James D. Smith
10721b4b41 Test for ID3v1 false positives. 2022-03-13 08:11:23 +01:00
James D. Smith
cebaf9a8e2 Utils: Fix potential ID3v1 false positive in the presence of an APE tag. 2022-03-13 08:11:23 +01:00
Urs Fleisch
197d2a684b Correctly parse MP4 non-full meta atoms (#1041)
There are m4a files with regular (non-full) meta atoms. When such
a meta atom is not correctly parsed, the subsequent atoms are not
recognized and offsets will not be adjusted when atoms are added,
which will corrupt the MP4 file.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Instead of:

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

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

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

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

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

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

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

This fixes issue #885.

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

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

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

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

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

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

See https://developer.apple.com/library/archive/documentation/QuickTime/QTFF/QTFFChap2/qtff2.html#//apple_ref/doc/uid/TP40000939-CH204-25615
2019-03-17 08:20:43 -05:00
Urs Fleisch
1cf176af61 Do not ignore non zero RIFF padding if leading to parse error (#882) 2019-02-10 08:40:20 +01:00
Nick Shaforostov
2d90d938fe fix reading audioproperties for broken mp3 files 2018-03-02 16:18:10 +01:00
291 changed files with 8604 additions and 12096 deletions

5
.gitignore vendored
View File

@@ -47,3 +47,8 @@ CMakeFiles/
taglib.h.stamp
taglib.xcodeproj
CMakeScripts
/.clang-format
/compile_commands.json
.clangd
.cache
.idea

View File

@@ -12,12 +12,14 @@ compiler:
- gcc
- clang
arch:
- ppc64le
addons:
apt:
packages:
- libcppunit-dev
- zlib1g-dev
- libboost-dev
matrix:
exclude:

View File

@@ -16,6 +16,8 @@ Mathias Panzenböck <grosser.meister.morti@gmx.net>
Mod, S3M, IT and XM metadata implementations
Damien Plisson <damien78@audirvana.com>
DSDIFF metadata implementation
Urs Fleisch <ufleisch@users.sourceforge.net>
Bug fixes, maintainer.
Please send all patches and questions to taglib-devel@kde.org rather than to
individual developers!

View File

@@ -1,16 +1,12 @@
cmake_minimum_required(VERSION 2.8.0 FATAL_ERROR)
cmake_minimum_required(VERSION 3.0.0 FATAL_ERROR)
project(taglib)
if(NOT ${CMAKE_VERSION} VERSION_LESS 2.8.12)
cmake_policy(SET CMP0022 OLD)
endif()
set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules")
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules")
if(DEFINED ENABLE_STATIC)
message(FATAL_ERROR "This option is no longer available, use BUILD_SHARED_LIBS instead")
endif()
include(CTest)
include(FeatureSummary)
include(GNUInstallDirs)
option(BUILD_SHARED_LIBS "Build shared libraries" OFF)
if(APPLE)
@@ -36,7 +32,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)
@@ -50,15 +45,11 @@ endif()
add_definitions(-DHAVE_CONFIG_H)
set(TESTS_DIR "${CMAKE_CURRENT_SOURCE_DIR}/tests/")
## the following are directories where stuff will be installed to
set(LIB_SUFFIX "" CACHE STRING "Define suffix of directory name (32/64)")
set(EXEC_INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}" CACHE PATH "Base directory for executables and libraries")
set(BIN_INSTALL_DIR "${EXEC_INSTALL_PREFIX}/bin" CACHE PATH "The subdirectory to the binaries prefix (default prefix/bin)")
set(LIB_INSTALL_DIR "${EXEC_INSTALL_PREFIX}/lib${LIB_SUFFIX}" CACHE PATH "The subdirectory relative to the install prefix where libraries will be installed (default is /lib${LIB_SUFFIX})")
set(INCLUDE_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/include" CACHE PATH "The subdirectory to the header prefix")
if(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()
@@ -94,9 +85,9 @@ endif()
# 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 18)
set(TAGLIB_SOVERSION_CURRENT 20)
set(TAGLIB_SOVERSION_REVISION 0)
set(TAGLIB_SOVERSION_AGE 17)
set(TAGLIB_SOVERSION_AGE 19)
math(EXPR TAGLIB_SOVERSION_MAJOR "${TAGLIB_SOVERSION_CURRENT} - ${TAGLIB_SOVERSION_AGE}")
math(EXPR TAGLIB_SOVERSION_MINOR "${TAGLIB_SOVERSION_AGE}")
@@ -104,43 +95,66 @@ math(EXPR TAGLIB_SOVERSION_PATCH "${TAGLIB_SOVERSION_REVISION}")
include(ConfigureChecks.cmake)
# Determine whether zlib is installed.
option(WITH_ZLIB "Build with ZLIB" ON)
if(WITH_ZLIB)
if(NOT ZLIB_SOURCE)
find_package(ZLIB)
if(ZLIB_FOUND)
set(HAVE_ZLIB 1)
set(ZLIB_LIBRARIES_FLAGS -lz)
else()
set(HAVE_ZLIB 0)
endif()
endif()
if(NOT HAVE_ZLIB AND ZLIB_SOURCE)
set(HAVE_ZLIB 1)
set(HAVE_ZLIB_SOURCE 1)
endif()
else()
set(HAVE_ZLIB 0)
endif()
if(NOT WIN32)
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/taglib-config.cmake" "${CMAKE_CURRENT_BINARY_DIR}/taglib-config")
install(PROGRAMS "${CMAKE_CURRENT_BINARY_DIR}/taglib-config" DESTINATION "${BIN_INSTALL_DIR}")
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/taglib-config.cmake" "${CMAKE_CURRENT_BINARY_DIR}/taglib-config" @ONLY)
install(PROGRAMS "${CMAKE_CURRENT_BINARY_DIR}/taglib-config" DESTINATION "${CMAKE_INSTALL_BINDIR}")
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}")
endif()
if(NOT BUILD_FRAMEWORK)
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")
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)
option(TRACE_IN_RELEASE "Output debug messages even in release mode" OFF)
if(TRACE_IN_RELEASE)
set(TRACE_IN_RELEASE TRUE)
endif()
configure_file(taglib/taglib_config.h.cmake "${CMAKE_CURRENT_BINARY_DIR}/taglib_config.h")
add_subdirectory(taglib)
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)
@@ -148,7 +162,7 @@ if(BUILD_EXAMPLES)
endif()
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/Doxyfile.cmake" "${CMAKE_CURRENT_BINARY_DIR}/Doxyfile")
file(COPY doc/taglib.png DESTINATION doc)
file(COPY doc/taglib.png DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/doc/html)
add_custom_target(docs doxygen)
# uninstall target
@@ -157,3 +171,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

@@ -1,8 +1,6 @@
include(CheckLibraryExists)
include(CheckTypeSize)
include(CheckCXXCompilerFlag)
include(CheckCXXSourceCompiles)
include(TestLargeFiles)
# Check if the size of numeric types are suitable.
@@ -36,36 +34,9 @@ if(NOT ${SIZEOF_DOUBLE} EQUAL 8)
message(FATAL_ERROR "TagLib requires that double is 64-bit wide.")
endif()
# Determine whether your compiler supports large files.
if(NOT WIN32)
test_large_files(SUPPORT_LARGE_FILES)
if(NOT SUPPORT_LARGE_FILES)
MESSAGE(FATAL_ERROR "TagLib requires large files support.")
endif()
endif()
# Enable check_cxx_source_compiles() to work with Boost "header-only" libraries.
find_package(Boost)
if(Boost_FOUND)
set(CMAKE_REQUIRED_INCLUDES "${CMAKE_REQUIRED_INCLUDES};${Boost_INCLUDE_DIRS}")
endif()
# Determine which kind of atomic operations your compiler supports.
check_cxx_source_compiles("
#include <atomic>
int main() {
std::atomic_int x(1);
++x;
--x;
return 0;
}
" HAVE_STD_ATOMIC)
if(NOT HAVE_STD_ATOMIC)
check_cxx_source_compiles("
int main() {
volatile int x;
__sync_add_and_fetch(&x, 1);
@@ -74,8 +45,8 @@ if(NOT HAVE_STD_ATOMIC)
}
" HAVE_GCC_ATOMIC)
if(NOT HAVE_GCC_ATOMIC)
check_cxx_source_compiles("
if(NOT HAVE_GCC_ATOMIC)
check_cxx_source_compiles("
#include <libkern/OSAtomic.h>
int main() {
volatile int32_t x;
@@ -85,8 +56,8 @@ if(NOT HAVE_STD_ATOMIC)
}
" HAVE_MAC_ATOMIC)
if(NOT HAVE_MAC_ATOMIC)
check_cxx_source_compiles("
if(NOT HAVE_MAC_ATOMIC)
check_cxx_source_compiles("
#include <windows.h>
int main() {
volatile LONG x;
@@ -96,8 +67,8 @@ if(NOT HAVE_STD_ATOMIC)
}
" HAVE_WIN_ATOMIC)
if(NOT HAVE_WIN_ATOMIC)
check_cxx_source_compiles("
if(NOT HAVE_WIN_ATOMIC)
check_cxx_source_compiles("
#include <ia64intrin.h>
int main() {
volatile int x;
@@ -106,22 +77,10 @@ if(NOT HAVE_STD_ATOMIC)
return 0;
}
" HAVE_IA64_ATOMIC)
endif()
endif()
endif()
endif()
# Determine which kind of smart pointers your compiler supports.
check_cxx_source_compiles("
#include <memory>
int main() {
std::shared_ptr<int> x;
std::unique_ptr<int> y;
return 0;
}
" HAVE_STD_SMART_PTR)
# Determine which kind of byte swap functions your compiler supports.
check_cxx_source_compiles("
@@ -217,28 +176,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

@@ -169,9 +169,7 @@ SEARCH_INCLUDES = YES
INCLUDE_PATH =
INCLUDE_FILE_PATTERNS =
PREDEFINED = DO_NOT_DOCUMENT \
DOXYGEN \
WITH_MP4 \
WITH_ASF
DOXYGEN
EXPAND_AS_DEFINED =
SKIP_FUNCTION_MACROS = YES
#---------------------------------------------------------------------------
@@ -196,7 +194,7 @@ INCLUDE_GRAPH = YES
INCLUDED_BY_GRAPH = YES
CALL_GRAPH = NO
GRAPHICAL_HIERARCHY = YES
DOT_IMAGE_FORMAT = png
DOT_IMAGE_FORMAT = svg
DOT_PATH =
DOTFILE_DIRS =
MAX_DOT_GRAPH_WIDTH = 1024

View File

@@ -13,6 +13,12 @@ In order to build the included examples, use the `BUILD_EXAMPLES` option:
cmake -DBUILD_EXAMPLES=ON [...]
If you want to build TagLib without ZLib, you can use
cmake -DCMAKE_INSTALL_PREFIX=/usr/local -DCMAKE_BUILD_TYPE=Release -DWITH_ZLIB=OFF .
make
sudo make install
See http://www.cmake.org/cmake/help/runningcmake.html for generic help on
running CMake.
@@ -22,23 +28,25 @@ Mac OS X
On Mac OS X, you might want to build a framework that can be easily integrated
into your application. If you set the BUILD_FRAMEWORK option on, it will compile
TagLib as a framework. For example, the following command can be used to build
an Universal Binary framework with Mac OS X 10.4 as the deployment target:
a framework with Mac OS X 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 build 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.
@@ -52,7 +60,7 @@ This means you need to adjust things to suit your system, especially paths.
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)
* GCC by mingw-w64.sf.net v4.6.3 (Strawberry Perl 32b)
* MinGW32-4.8.0
Requirements:
@@ -69,15 +77,16 @@ Optional:
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.
| 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) |
| option | description |
| ----------------------- | ----------- |
| `WITH_ZLIB=` | Whether to build with ZLib |
| `ZLIB_ROOT=` | Where to find ZLib's root directory. Assumes parent of: `\include` and `\lib.`|
| `ZLIB_INCLUDE_DIR=` | Where to find ZLib's Include directory.|
| `ZLIB_LIBRARY=` | Where to find ZLib's Library.
| `ZLIB_SOURCE=` | Where to find ZLib's Source Code. Alternative to `ZLIB_INCLUDE_DIR` and `ZLIB_LIBRARY`.
| `CMAKE_INSTALL_PREFIX=` | Where to install Taglib. |
| `CMAKE_BUILD_TYPE=` | Release, Debug, etc ... (Not available in MSVC) |
The easiest way is at the command prompt (Visual C++ command prompt for MSVS users batch file and/or shortcuts are your friends).
@@ -85,11 +94,11 @@ The easiest way is at the command prompt (Visual C++ command prompt for MSVS use
Replace "GENERATOR" with your needs.
* For MSVS: `Visual Studio XX YYYY`, e.g. `Visual Studio 14 2015`.
**Note**: As Visual Studio 2017 supports CMake, you can skip this step and open the taglib
folder in VS instead.
* For MinGW: `MinGW Makefiles`
C:\GitRoot\taglib> cmake -G "GENERATOR" -DCMAKE_INSTALL_PREFIX=C:\Libraries\taglib
Or use the CMake GUI:
@@ -126,13 +135,13 @@ The easiest way is at the command prompt (Visual C++ command prompt for MSVS use
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:
@@ -140,7 +149,7 @@ The easiest way is at the command prompt (Visual C++ command prompt for MSVS use
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.
@@ -151,7 +160,7 @@ The easiest way is at the command prompt (Visual C++ command prompt for MSVS use
C:\GitRoot\taglib> gmake install
OR (Depending on MinGW install)
C:\GitRoot\taglib> mingw32-make install
@@ -167,9 +176,53 @@ Unit Tests
If you want to run the test suite to make sure TagLib works properly on your
system, you need to have cppunit installed. To build the tests, include
the option `-DBUILD_TESTS=on` when running cmake.
the option `-DBUILD_TESTING=ON` when running cmake.
The test suite has a custom target in the build system, so you can run
the tests using make:
make check
Windows MinGW:
* 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_TESTING=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`
Windows MSVS:
* 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_TESTING=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`
macOS:
* 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`

47
NEWS
View File

@@ -1,24 +1,61 @@
============================
==========================
* Added interface StreamTypeResolver to support streams which cannot be
fopen()'ed, e.g. network files.
* Added MP4::File::strip() to remove meta atom from MP4 file.
* Added Map::value() to look up without creating entry.
* Use property "WORK" instead of "CONTENTGROUP" for ID3v2 "TIT1" frame,
use property "WORK" for ASF "WM/ContentGroupDescription",
use property "COMPILATION" for ID3v2 "TCMP" frame.
* Build system improvements: option WITH_ZLIB, BUILD_TESTING instead of
BUILD_TESTS, GNUInstallDirs, FeatureSummary, tests with BUILD_SHARED_LIBS,
cross compilation with Buildroot, systems without HAVE_GCC_ATOMIC, Clang.
* Fixed heap-buffer-overflows when handling ASF, APE, FLAC, ID3v2, MP4, MPC
tags.
* Fixed detection of invalid file by extension when correct type can be
detected by contents.
* Fixed unnecessary creation of map entries in APE and FLAC tags if looked up
tag does not exist.
* Fixed parsing of MP4 non-full meta atoms.
* Fixed potential ID3v1 false positive in the presence of an APE tag.
* Fixed ID3v2 version handling for frames embedded in CHAP or CTOC frames.
* Fixed parsing of multiple strings with a single BOM in ID3v2.4.0.
* Fixed several smaller issues reported by clang-tidy.
TagLib 1.12 (Feb 16, 2021)
==========================
* Added support for DSF and DSDIFF files.
* Added support for WinRT.
* Added support for Linux on POWER.
* Added support for classical music tags of iTunes 12.5.
* Added support for file descriptor to FileStream.
* Added support for 'cmID', 'purl', 'egid' MP4 atoms.
* Added support for 'GRP1' ID3v2 frame.
* Added support for extensible WAV subformat.
* Enabled FileRef to detect file types based on the stream content.
* Dropped support for Windows 9x and NT 4.0 or older.
* Check for mandatory header objects in ASF files.
* More tolerant handling of RIFF padding, WAV files, broken MPEG streams.
* Improved calculation of Ogg, Opus, Speex, WAV, MP4 bitrates.
* Improved Windows compatibility by storing FLAC picture after comments.
* Fixed numerical genres in ID3v2.3.0 'TCON' frames.
* Fixed consistency of API removing MP4 items when empty values are set.
* Fixed consistency of API preferring COMM frames with no description.
* Fixed OOB read on invalid Ogg FLAC files (CVE-2018-11439).
* Fixed handling of empty MPEG files.
* Fixed parsing MP4 mdhd timescale.
* Fixed reading MP4 atoms with zero length.
* Fixed reading FLAC files with zero-sized seektables.
* Fixed handling of lowercase field names in Vorbis Comments.
* Fixed handling of 'rate' atoms in MP4 files.
* Fixed handling of invalid UTF-8 sequences.
* Fixed possible file corruptions when saving Ogg files.
* Fixed handling of non-audio blocks, sampling rates, DSD audio in WavPack files.
* TableOfContentsFrame::toString() improved.
* UserTextIdentificationFrame::toString() improved.
* Marked FileRef::create() deprecated.
* Marked MPEG::File::save() with boolean parameters deprecated,
provide overloads with enum parameters.
* Several smaller bug fixes and performance improvements.
TagLib 1.11.1 (Oct 24, 2016)
@@ -245,7 +282,7 @@ TagLib 1.6.3 (Apr 17, 2010)
* Fixed definitions of the TAGLIB_WITH_MP4 and TAGLIB_WITH_ASF macros.
* Fixed upgrading of ID3v2.3 genre frame with ID3v1 code 0 (Blues).
* New method `int String::toInt(bool *ok)` which can return whether the
conversion to a number was successfull.
conversion to a number was successful.
* Fixed parsing of incorrectly written lengths in ID3v2 (affects mainly
compressed frames). (BUG:231075)
@@ -253,7 +290,7 @@ TagLib 1.6.2 (Apr 9, 2010)
==========================
* Read Vorbis Comments from the first FLAC metadata block, if there are
multipe ones. (BUG:211089)
multiple ones. (BUG:211089)
* Fixed a memory leak in FileRef's OGA format detection.
* Fixed compilation with the Sun Studio compiler. (BUG:215225)
* Handle WM/TrackNumber attributes with DWORD content in WMA files.
@@ -288,7 +325,7 @@ TagLib 1.6 (Sep 13, 2009)
* Added support for disabling dllimport/dllexport on Windows using the
TAGLIB_STATIC macro.
* Support for parsing the obsolete 'gnre' MP4 atom.
* New cpp macros TAGLIB_WITH_MP4 and TAGLIB_WITH_ASF to determin if
* New cpp macros TAGLIB_WITH_MP4 and TAGLIB_WITH_ASF to determine if
TagLib was built with MP4/ASF support.
1.6 RC1:

View File

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

@@ -58,18 +58,18 @@ set_target_properties(tag_c PROPERTIES
VERSION 0.0.0
SOVERSION 0
DEFINE_SYMBOL MAKE_TAGLIB_C_LIB
INSTALL_NAME_DIR ${LIB_INSTALL_DIR}
INSTALL_NAME_DIR ${CMAKE_INSTALL_LIBDIR}
)
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
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/taglib
)
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)
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/taglib_c.pc DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig)
endif()

View File

@@ -69,7 +69,7 @@ namespace
{
return String(s, unicodeStrings ? String::UTF8 : String::Latin1);
}
}
} // namespace
void taglib_set_strings_unicode(BOOL unicode)
{
@@ -92,32 +92,32 @@ void taglib_free(void* pointer)
TagLib_File *taglib_file_new(const char *filename)
{
return reinterpret_cast<TagLib_File *>(new FileRef(filename));
return reinterpret_cast<TagLib_File *>(FileRef::create(filename));
}
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 FileRef(new MPEG::File(filename)));
return reinterpret_cast<TagLib_File *>(new MPEG::File(filename));
case TagLib_File_OggVorbis:
return reinterpret_cast<TagLib_File *>(new FileRef(new Ogg::Vorbis::File(filename)));
return reinterpret_cast<TagLib_File *>(new Ogg::Vorbis::File(filename));
case TagLib_File_FLAC:
return reinterpret_cast<TagLib_File *>(new FileRef(new FLAC::File(filename)));
return reinterpret_cast<TagLib_File *>(new FLAC::File(filename));
case TagLib_File_MPC:
return reinterpret_cast<TagLib_File *>(new FileRef(new MPC::File(filename)));
return reinterpret_cast<TagLib_File *>(new MPC::File(filename));
case TagLib_File_OggFlac:
return reinterpret_cast<TagLib_File *>(new FileRef(new Ogg::FLAC::File(filename)));
return reinterpret_cast<TagLib_File *>(new Ogg::FLAC::File(filename));
case TagLib_File_WavPack:
return reinterpret_cast<TagLib_File *>(new FileRef(new WavPack::File(filename)));
return reinterpret_cast<TagLib_File *>(new WavPack::File(filename));
case TagLib_File_Speex:
return reinterpret_cast<TagLib_File *>(new FileRef(new Ogg::Speex::File(filename)));
return reinterpret_cast<TagLib_File *>(new Ogg::Speex::File(filename));
case TagLib_File_TrueAudio:
return reinterpret_cast<TagLib_File *>(new FileRef(new TrueAudio::File(filename)));
return reinterpret_cast<TagLib_File *>(new TrueAudio::File(filename));
case TagLib_File_MP4:
return reinterpret_cast<TagLib_File *>(new FileRef(new MP4::File(filename)));
return reinterpret_cast<TagLib_File *>(new MP4::File(filename));
case TagLib_File_ASF:
return reinterpret_cast<TagLib_File *>(new FileRef(new ASF::File(filename)));
return reinterpret_cast<TagLib_File *>(new ASF::File(filename));
default:
return 0;
}
@@ -125,29 +125,29 @@ TagLib_File *taglib_file_new_type(const char *filename, TagLib_File_Type type)
void taglib_file_free(TagLib_File *file)
{
delete reinterpret_cast<FileRef *>(file);
delete reinterpret_cast<File *>(file);
}
BOOL taglib_file_is_valid(const TagLib_File *file)
{
return reinterpret_cast<const FileRef *>(file)->isValid();
return reinterpret_cast<const File *>(file)->isValid();
}
TagLib_Tag *taglib_file_tag(const TagLib_File *file)
{
const FileRef *f = reinterpret_cast<const FileRef *>(file);
const File *f = reinterpret_cast<const File *>(file);
return reinterpret_cast<TagLib_Tag *>(f->tag());
}
const TagLib_AudioProperties *taglib_file_audioproperties(const TagLib_File *file)
{
const FileRef *f = reinterpret_cast<const FileRef *>(file);
const File *f = reinterpret_cast<const File *>(file);
return reinterpret_cast<const TagLib_AudioProperties *>(f->audioProperties());
}
BOOL taglib_file_save(TagLib_File *file)
{
return reinterpret_cast<FileRef *>(file)->save();
return reinterpret_cast<File *>(file)->save();
}
////////////////////////////////////////////////////////////////////////////////

View File

@@ -1,12 +1,12 @@
prefix=${CMAKE_INSTALL_PREFIX}
exec_prefix=${CMAKE_INSTALL_PREFIX}
libdir=${LIB_INSTALL_DIR}
includedir=${INCLUDE_INSTALL_DIR}
libdir=${CMAKE_INSTALL_FULL_LIBDIR}
includedir=${CMAKE_INSTALL_FULL_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
Libs: -L${CMAKE_INSTALL_FULL_LIBDIR} -ltag_c
Cflags: -I${CMAKE_INSTALL_FULL_INCLUDEDIR}/taglib

View File

@@ -1,9 +0,0 @@
#include <sys/types.h>
int main(int argc, char **argv)
{
/* Cause a compile-time error if off_t is smaller than 64 bits */
#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))
int off_t_is_large[ (LARGE_OFF_T % 2147483629 == 721 && LARGE_OFF_T % 2147483647 == 1) ? 1 : -1 ];
return 0;
}

View File

@@ -1,23 +0,0 @@
#cmakedefine _LARGEFILE_SOURCE
#cmakedefine _LARGEFILE64_SOURCE
#cmakedefine _LARGE_FILES
#cmakedefine _FILE_OFFSET_BITS ${_FILE_OFFSET_BITS}
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char **argv)
{
/* Cause a compile-time error if off_t is smaller than 64 bits,
* and make sure we have ftello / fseeko.
*/
#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))
int off_t_is_large[ (LARGE_OFF_T % 2147483629 == 721 && LARGE_OFF_T % 2147483647 == 1) ? 1 : -1 ];
FILE *fp = fopen(argv[0],"r");
off_t offset = ftello( fp );
fseeko( fp, offset, SEEK_CUR );
fclose(fp);
return 0;
}

View File

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

View File

@@ -1,104 +0,0 @@
# - Define macro to check large file support
#
# TEST_LARGE_FILES(VARIABLE)
#
# VARIABLE will be set to true if off_t is 64 bits, and fseeko/ftello present.
# This macro will also set defines necessary enable large file support, for instance
# _LARGE_FILES
# _LARGEFILE_SOURCE
# _FILE_OFFSET_BITS 64
# HAVE_FSEEKO
#
# However, it is YOUR job to make sure these defines are set in a cmakedefine so they
# end up in a config.h file that is included in your source if necessary!
# This macro skips the Windows specific checks. Because TagLib uses Win32 API.
MACRO(TEST_LARGE_FILES VARIABLE)
IF(NOT DEFINED ${VARIABLE})
# On most platforms it is probably overkill to first test the flags for 64-bit off_t,
# and then separately fseeko. However, in the future we might have 128-bit filesystems
# (ZFS), so it might be dangerous to indiscriminately set e.g. _FILE_OFFSET_BITS=64.
MESSAGE(STATUS "Checking for 64-bit off_t")
# First check without any special flags
TRY_COMPILE(FILE64_OK "${PROJECT_BINARY_DIR}"
"${CMAKE_SOURCE_DIR}/cmake/TestFileOffsetBits.c")
if(FILE64_OK)
MESSAGE(STATUS "Checking for 64-bit off_t - present")
endif(FILE64_OK)
if(NOT FILE64_OK)
# Test with _FILE_OFFSET_BITS=64
TRY_COMPILE(FILE64_OK "${PROJECT_BINARY_DIR}"
"${CMAKE_SOURCE_DIR}/cmake/TestFileOffsetBits.c"
COMPILE_DEFINITIONS "-D_FILE_OFFSET_BITS=64" )
if(FILE64_OK)
MESSAGE(STATUS "Checking for 64-bit off_t - present with _FILE_OFFSET_BITS=64")
set(_FILE_OFFSET_BITS 64)
endif(FILE64_OK)
endif(NOT FILE64_OK)
if(NOT FILE64_OK)
# Test with _LARGE_FILES
TRY_COMPILE(FILE64_OK "${PROJECT_BINARY_DIR}"
"${CMAKE_SOURCE_DIR}/cmake/TestFileOffsetBits.c"
COMPILE_DEFINITIONS "-D_LARGE_FILES" )
if(FILE64_OK)
MESSAGE(STATUS "Checking for 64-bit off_t - present with _LARGE_FILES")
set(_LARGE_FILES 1)
endif(FILE64_OK)
endif(NOT FILE64_OK)
if(NOT FILE64_OK)
# Test with _LARGEFILE_SOURCE
TRY_COMPILE(FILE64_OK "${PROJECT_BINARY_DIR}"
"${CMAKE_SOURCE_DIR}/cmake/TestFileOffsetBits.c"
COMPILE_DEFINITIONS "-D_LARGEFILE_SOURCE" )
if(FILE64_OK)
MESSAGE(STATUS "Checking for 64-bit off_t - present with _LARGEFILE_SOURCE")
set(_LARGEFILE_SOURCE 1)
endif(FILE64_OK)
endif(NOT FILE64_OK)
if(NOT FILE64_OK)
MESSAGE(STATUS "Checking for 64-bit off_t - not present")
else(NOT FILE64_OK)
# Set the flags we might have determined to be required above
configure_file("${CMAKE_SOURCE_DIR}/cmake/TestLargeFiles.c.cmakein"
"${PROJECT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/TestLargeFiles.c")
MESSAGE(STATUS "Checking for fseeko/ftello")
# Test if ftello/fseeko are available
TRY_COMPILE(FSEEKO_COMPILE_OK "${PROJECT_BINARY_DIR}"
"${PROJECT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/TestLargeFiles.c")
if(FSEEKO_COMPILE_OK)
MESSAGE(STATUS "Checking for fseeko/ftello - present")
endif(FSEEKO_COMPILE_OK)
if(NOT FSEEKO_COMPILE_OK)
# glibc 2.2 neds _LARGEFILE_SOURCE for fseeko (but not 64-bit off_t...)
TRY_COMPILE(FSEEKO_COMPILE_OK "${PROJECT_BINARY_DIR}"
"${PROJECT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/TestLargeFiles.c"
COMPILE_DEFINITIONS "-D_LARGEFILE_SOURCE" )
if(FSEEKO_COMPILE_OK)
MESSAGE(STATUS "Checking for fseeko/ftello - present with _LARGEFILE_SOURCE")
set(_LARGEFILE_SOURCE 1)
endif(FSEEKO_COMPILE_OK)
endif(NOT FSEEKO_COMPILE_OK)
endif(NOT FILE64_OK)
if(FSEEKO_COMPILE_OK)
SET(${VARIABLE} 1 CACHE INTERNAL "Result of test for large file support" FORCE)
set(HAVE_FSEEKO 1)
else(FSEEKO_COMPILE_OK)
MESSAGE(STATUS "Checking for fseeko/ftello - not found")
SET(${VARIABLE} 0 CACHE INTERNAL "Result of test for large file support" FORCE)
endif(FSEEKO_COMPILE_OK)
ENDIF(NOT DEFINED ${VARIABLE})
ENDMACRO(TEST_LARGE_FILES VARIABLE)

View File

@@ -3,11 +3,6 @@
#ifndef TAGLIB_CONFIG_H
#define TAGLIB_CONFIG_H
/* Defined if required for large files support */
#cmakedefine _LARGE_FILES ${_LARGE_FILES}
#cmakedefine _LARGEFILE_SOURCE ${_LARGEFILE_SOURCE}
#cmakedefine _FILE_OFFSET_BITS ${_FILE_OFFSET_BITS}
/* Defined if your compiler supports some byte swap functions */
#cmakedefine HAVE_GCC_BYTESWAP 1
#cmakedefine HAVE_GLIBC_BYTESWAP 1
@@ -16,15 +11,11 @@
#cmakedefine HAVE_OPENBSD_BYTESWAP 1
/* Defined if your compiler supports some atomic operations */
#cmakedefine HAVE_STD_ATOMIC 1
#cmakedefine HAVE_GCC_ATOMIC 1
#cmakedefine HAVE_MAC_ATOMIC 1
#cmakedefine HAVE_WIN_ATOMIC 1
#cmakedefine HAVE_IA64_ATOMIC 1
/* Defined if your compiler supports shared_ptr */
#cmakedefine HAVE_STD_SMART_PTR 1
/* Defined if your compiler supports some safer version of vsprintf */
#cmakedefine HAVE_VSNPRINTF 1
#cmakedefine HAVE_VSPRINTF_S 1

View File

@@ -12,7 +12,7 @@
<table border="0" width="100%">
<tr>
<td width="1">
<img src="../taglib.png">
<img src="taglib.png">
</td>
<td>
<div id="intro">

View File

@@ -5,6 +5,7 @@ include_directories(
${CMAKE_CURRENT_SOURCE_DIR}/../taglib/mpeg
${CMAKE_CURRENT_SOURCE_DIR}/../taglib/mpeg/id3v1
${CMAKE_CURRENT_SOURCE_DIR}/../taglib/mpeg/id3v2
${CMAKE_CURRENT_SOURCE_DIR}/../taglib/mpeg/id3v2/frames
${CMAKE_CURRENT_SOURCE_DIR}/../bindings/c/
)
@@ -37,7 +38,8 @@ target_link_libraries(framelist tag)
add_executable(strip-id3v1 strip-id3v1.cpp)
target_link_libraries(strip-id3v1 tag)
########### next target ###############
add_executable(inspect inspect.cpp)
target_link_libraries(inspect tag)
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

@@ -32,6 +32,7 @@
#include <id3v2tag.h>
#include <id3v2frame.h>
#include <id3v2header.h>
#include <commentsframe.h>
#include <id3v1tag.h>
@@ -65,8 +66,15 @@ int main(int argc, char *argv[])
<< endl;
ID3v2::FrameList::ConstIterator it = id3v2tag->frameList().begin();
for(; it != id3v2tag->frameList().end(); it++)
cout << (*it)->frameID() << " - \"" << (*it)->toString() << "\"" << endl;
for(; it != id3v2tag->frameList().end(); it++) {
cout << (*it)->frameID();
if(ID3v2::CommentsFrame *comment = dynamic_cast<ID3v2::CommentsFrame *>(*it))
if(!comment->description().isEmpty())
cout << " [" << comment->description() << "]";
cout << " - \"" << (*it)->toString() << "\"" << endl;
}
}
else
cout << "file does not have a valid id3v2 tag" << endl;

View File

@@ -1,48 +0,0 @@
/* Copyright (C) 2012 Lukas Lalinsky <lalinsky@gmail.com>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <iostream>
#include <stdlib.h>
#include <fileref.h>
#include <tfile.h>
using namespace std;
using namespace TagLib;
int main(int argc, char *argv[])
{
// process the command line args
for(int i = 1; i < argc; i++) {
cout << "******************** \"" << argv[i] << "\"********************" << endl;
FileRef f(argv[i]);
if(!f.isNull() && f.file()) {
cout << f.file()->toString().to8Bit(true) << endl;
}
}
}

View File

@@ -28,7 +28,6 @@
#include <fileref.h>
#include <tag.h>
#include <tpicturemap.h>
#include <tpropertymap.h>
using namespace std;
@@ -46,15 +45,13 @@ 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;
if(!tag->pictures().isEmpty())
cout << "pictures -" << tag->pictures() << 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;
TagLib::PropertyMap tags = f.file()->properties();

View File

@@ -22,10 +22,8 @@
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <fstream>
#include <iostream>
#include <iomanip>
#include <sstream>
#include <string.h>
#include <stdio.h>
@@ -36,8 +34,6 @@
#include <tlist.h>
#include <fileref.h>
#include <tfile.h>
#include <tpicturemap.h>
#include <tag.h>
#include <tpropertymap.h>
@@ -74,7 +70,6 @@ void usage()
cout << " -R <tagname> <tagvalue>" << endl;
cout << " -I <tagname> <tagvalue>" << endl;
cout << " -D <tagname>" << endl;
cout << " -p <picture(jpg only, file between double quotes)>" << endl;
cout << endl;
exit(1);
@@ -115,16 +110,14 @@ int main(int argc, char *argv[])
if(fileList.isEmpty())
usage();
if(argv[argc-1][1] == 'p')
argc++;
for(int i = 1; i < argc - 1; i += 2) {
if(isArgument(argv[i]) && i + 1 < argc && !isArgument(argv[i + 1])) {
char field = argv[i][1];
TagLib::String value = argv[i + 1];
TagLib::List<TagLib::FileRef>::Iterator it;
TagLib::List<TagLib::FileRef>::ConstIterator it;
for(it = fileList.begin(); it != fileList.end(); ++it) {
TagLib::Tag *t = (*it).tag();
@@ -174,31 +167,6 @@ int main(int argc, char *argv[])
checkForRejectedProperties((*it).file()->setProperties(map));
break;
}
case 'p':
{
if(!isFile(value.toCString())) {
cout << value.toCString() << " not found." << endl;
return 1;
}
ifstream picture;
picture.open(value.toCString());
stringstream buffer;
buffer << picture.rdbuf();
picture.close();
TagLib::String buf(buffer.str());
TagLib::ByteVector data(buf.data(TagLib::String::Latin1));
if(!data.find("JFIF")) {
cout << value.toCString() << " is not a JPEG." << endl;
return 1;
}
TagLib::Picture pic(data,
TagLib::Picture::FrontCover,
"image/jpeg",
"Added with taglib");
TagLib::PictureMap picMap(pic);
t->setPictures(picMap);
}
break;
default:
usage();
break;

View File

@@ -14,10 +14,20 @@ EOH
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,16 +39,16 @@ while test $# -gt 0
do
case $1 in
--libs)
flags="$flags -L$libdir -ltag"
flags="$flags -L$libdir -ltag @ZLIB_LIBRARIES_FLAGS@"
;;
--cflags)
flags="$flags -I$includedir/taglib"
flags="$flags -I$includedir -I$includedir/taglib"
;;
--version)
echo ${TAGLIB_LIB_VERSION_STRING}
echo @TAGLIB_LIB_VERSION_STRING@
;;
--prefix)
echo $prefix
echo ${prefix:-@CMAKE_INSTALL_PREFIX@}
;;
*)
echo "$0: unknown option $1"

View File

@@ -1,35 +1,36 @@
@echo off
goto beginning
*
* It is what it is, you can do with it as you please.
*
* Just don't blame me if it teaches your computer to smoke!
*
* -Enjoy
* fh :)_~
*
:beginning
if /i "%1#" == "--libs#" goto doit
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]
goto theend
*
* NOTE: Windows does not assume libraries are prefixed with 'lib'.
* NOTE: If '-llibtag' is the last element, it is easily appended in the users installation/makefile process
* to allow for static, shared or debug builds.
* It would be preferable if the top level CMakeLists.txt provided the library name during config. ??
:doit
if /i "%1#" == "--libs#" echo -L${LIB_INSTALL_DIR} -llibtag
if /i "%1#" == "--cflags#" echo -I${INCLUDE_INSTALL_DIR}/taglib
if /i "%1#" == "--version#" echo ${TAGLIB_LIB_VERSION_STRING}
if /i "%1#" == "--prefix#" echo ${CMAKE_INSTALL_PREFIX}
:theend
@echo off
goto beginning
*
* It is what it is, you can do with it as you please.
*
* Just don't blame me if it teaches your computer to smoke!
*
* -Enjoy
* fh :)_~
*
:beginning
if /i "%1#" == "--libs#" goto doit
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]
goto theend
*
* NOTE: Windows does not assume libraries are prefixed with 'lib'.
* NOTE: If '-llibtag' is the last element, it is easily appended in the users installation/makefile process
* 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${CMAKE_INSTALL_FULL_LIBDIR} -llibtag
if /i "%1#" == "--cflags#" echo -I${CMAKE_INSTALL_FULL_INCLUDEDIR} -I${CMAKE_INSTALL_FULL_INCLUDEDIR}/taglib
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@
libdir=@CMAKE_INSTALL_FULL_LIBDIR@
includedir=@CMAKE_INSTALL_FULL_INCLUDEDIR@
Name: TagLib
Description: Audio meta-data library
Requires:
Requires:
Version: @TAGLIB_LIB_VERSION_STRING@
Libs: -L${libdir} -ltag
Cflags: -I${includedir}/taglib
Libs: -L${libdir} -ltag @ZLIB_LIBRARIES_FLAGS@
Cflags: -I${includedir} -I${includedir}/taglib

View File

@@ -24,11 +24,7 @@ include_directories(
${CMAKE_CURRENT_SOURCE_DIR}/s3m
${CMAKE_CURRENT_SOURCE_DIR}/it
${CMAKE_CURRENT_SOURCE_DIR}/xm
${CMAKE_CURRENT_SOURCE_DIR}/ebml
${CMAKE_CURRENT_SOURCE_DIR}/ebml/matroska
${CMAKE_CURRENT_SOURCE_DIR}/dsf
${CMAKE_CURRENT_SOURCE_DIR}/dsdiff
${CMAKE_SOURCE_DIR}/3rdparty
${taglib_SOURCE_DIR}/3rdparty
)
if(ZLIB_FOUND)
@@ -42,12 +38,12 @@ set(tag_HDRS
fileref.h
audioproperties.h
taglib_export.h
${CMAKE_CURRENT_BINARY_DIR}/../taglib_config.h
toolkit/taglib.h
toolkit/tstring.h
toolkit/tlist.h
toolkit/tlist.tcc
toolkit/tstringlist.h
toolkit/tstringhandler.h
toolkit/tbytevector.h
toolkit/tbytevectorlist.h
toolkit/tbytevectorstream.h
@@ -56,8 +52,6 @@ set(tag_HDRS
toolkit/tfilestream.h
toolkit/tmap.h
toolkit/tmap.tcc
toolkit/tpicture.h
toolkit/tpicturemap.h
toolkit/tpropertymap.h
toolkit/trefcounter.h
toolkit/tdebuglistener.h
@@ -67,6 +61,7 @@ set(tag_HDRS
mpeg/xingheader.h
mpeg/id3v1/id3v1tag.h
mpeg/id3v1/id3v1genres.h
mpeg/id3v2/id3v2.h
mpeg/id3v2/id3v2extendedheader.h
mpeg/id3v2/id3v2frame.h
mpeg/id3v2/id3v2header.h
@@ -144,17 +139,6 @@ set(tag_HDRS
s3m/s3mproperties.h
xm/xmfile.h
xm/xmproperties.h
ebml/ebmlfile.h
ebml/ebmlelement.h
ebml/ebmlconstants.h
ebml/matroska/ebmlmatroskafile.h
ebml/matroska/ebmlmatroskaconstants.h
ebml/matroska/ebmlmatroskaaudio.h
dsf/dsffile.h
dsf/dsfproperties.h
dsdiff/dsdifffile.h
dsdiff/dsdiffproperties.h
dsdiff/dsdiffdiintag.h
)
set(mpeg_SRCS
@@ -310,32 +294,9 @@ set(xm_SRCS
xm/xmproperties.cpp
)
set(dsf_SRCS
dsf/dsffile.cpp
dsf/dsfproperties.cpp
)
set(ebml_SRCS
ebml/ebmlfile.cpp
ebml/ebmlelement.cpp
)
set(matroska_SRCS
ebml/matroska/ebmlmatroskafile.cpp
ebml/matroska/ebmlmatroskaaudio.cpp
)
set(dsdiff_SRCS
dsdiff/dsdifffile.cpp
dsdiff/dsdiffproperties.cpp
dsdiff/dsdiffdiintag.cpp
)
set(toolkit_SRCS
toolkit/taglib.cpp
toolkit/tstring.cpp
toolkit/tstringlist.cpp
toolkit/tstringhandler.cpp
toolkit/tbytevector.cpp
toolkit/tbytevectorlist.cpp
toolkit/tbytevectorstream.cpp
@@ -343,8 +304,6 @@ set(toolkit_SRCS
toolkit/tfile.cpp
toolkit/tfilestream.cpp
toolkit/tdebug.cpp
toolkit/tpicture.cpp
toolkit/tpicturemap.cpp
toolkit/tpropertymap.cpp
toolkit/trefcounter.cpp
toolkit/tdebuglistener.cpp
@@ -367,7 +326,6 @@ set(tag_LIB_SRCS
${vorbis_SRCS} ${oggflacs_SRCS} ${mpc_SRCS} ${ape_SRCS} ${toolkit_SRCS} ${flacs_SRCS}
${wavpack_SRCS} ${speex_SRCS} ${trueaudio_SRCS} ${riff_SRCS} ${aiff_SRCS} ${wav_SRCS}
${asf_SRCS} ${mp4_SRCS} ${mod_SRCS} ${s3m_SRCS} ${it_SRCS} ${xm_SRCS} ${opus_SRCS}
${ebml_SRCS} ${matroska_SRCS} ${dsf_SRCS} ${dsdiff_SRCS}
${zlib_SRCS}
tag.cpp
tagunion.cpp
@@ -377,6 +335,7 @@ set(tag_LIB_SRCS
)
add_library(tag ${tag_LIB_SRCS} ${tag_HDRS})
set_property(TARGET tag PROPERTY CXX_STANDARD 98)
if(HAVE_ZLIB AND NOT HAVE_ZLIB_SOURCE)
target_link_libraries(tag ${ZLIB_LIBRARIES})
@@ -385,14 +344,13 @@ endif()
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_LIBDIR}
DEFINE_SYMBOL MAKE_TAGLIB_LIB
LINK_INTERFACE_LIBRARIES ""
PUBLIC_HEADER "${tag_HDRS}"
)
if(VISIBILITY_HIDDEN)
set_target_properties(tag PROPERTIES C_VISIBILITY_PRESET hidden
)
set_target_properties(tag PROPERTIES C_VISIBILITY_PRESET hidden)
endif()
if(BUILD_FRAMEWORK)
@@ -407,8 +365,8 @@ endif()
install(TARGETS tag
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
)

View File

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

View File

@@ -39,7 +39,6 @@
#include <id3v2header.h>
#include <tpropertymap.h>
#include <tagutils.h>
#include <tsmartptr.h>
#include "apefile.h"
#include "apetag.h"
@@ -59,21 +58,29 @@ public:
APELocation(-1),
APESize(0),
ID3v1Location(-1),
ID3v2Header(0),
ID3v2Location(-1),
ID3v2Size(0) {}
ID3v2Size(0),
properties(0) {}
long long APELocation;
long long APESize;
~FilePrivate()
{
delete ID3v2Header;
delete properties;
}
long long ID3v1Location;
long APELocation;
long APESize;
SCOPED_PTR<ID3v2::Header> ID3v2Header;
long long ID3v2Location;
long long ID3v2Size;
long ID3v1Location;
DoubleTagUnion tag;
ID3v2::Header *ID3v2Header;
long ID3v2Location;
long ID3v2Size;
SCOPED_PTR<AudioProperties> properties;
TagUnion tag;
Properties *properties;
};
////////////////////////////////////////////////////////////////////////////////
@@ -85,14 +92,14 @@ 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 ") != ByteVector::npos());
return (buffer.find("MAC ") >= 0);
}
////////////////////////////////////////////////////////////////////////////////
// public members
////////////////////////////////////////////////////////////////////////////////
APE::File::File(FileName file, bool readProperties, AudioProperties::ReadStyle) :
APE::File::File(FileName file, bool readProperties, Properties::ReadStyle) :
TagLib::File(file),
d(new FilePrivate())
{
@@ -100,7 +107,7 @@ APE::File::File(FileName file, bool readProperties, AudioProperties::ReadStyle)
read(readProperties);
}
APE::File::File(IOStream *stream, bool readProperties, AudioProperties::ReadStyle) :
APE::File::File(IOStream *stream, bool readProperties, Properties::ReadStyle) :
TagLib::File(stream),
d(new FilePrivate())
{
@@ -118,6 +125,16 @@ TagLib::Tag *APE::File::tag() const
return &d->tag;
}
PropertyMap APE::File::properties() const
{
return d->tag.properties();
}
void APE::File::removeUnsupportedProperties(const StringList &properties)
{
d->tag.removeUnsupportedProperties(properties);
}
PropertyMap APE::File::setProperties(const PropertyMap &properties)
{
if(ID3v1Tag())
@@ -126,9 +143,9 @@ PropertyMap APE::File::setProperties(const PropertyMap &properties)
return APETag(true)->setProperties(properties);
}
APE::AudioProperties *APE::File::audioProperties() const
APE::Properties *APE::File::audioProperties() const
{
return d->properties.get();
return d->properties;
}
bool APE::File::save()
@@ -178,7 +195,7 @@ bool APE::File::save()
}
const ByteVector data = APETag()->render();
insert(data, d->APELocation, static_cast<size_t>(d->APESize));
insert(data, d->APELocation, d->APESize);
if(d->ID3v1Location >= 0)
d->ID3v1Location += (static_cast<long>(data.size()) - d->APESize);
@@ -190,7 +207,7 @@ bool APE::File::save()
// APE tag is empty. Remove the old one.
if(d->APELocation >= 0) {
removeBlock(d->APELocation, static_cast<size_t>(d->APESize));
removeBlock(d->APELocation, d->APESize);
if(d->ID3v1Location >= 0)
d->ID3v1Location -= d->APESize;
@@ -247,7 +264,7 @@ void APE::File::read(bool readProperties)
if(d->ID3v2Location >= 0) {
seek(d->ID3v2Location);
d->ID3v2Header.reset(new ID3v2::Header(readBlock(ID3v2::Header::size())));
d->ID3v2Header = new ID3v2::Header(readBlock(ID3v2::Header::size()));
d->ID3v2Size = d->ID3v2Header->completeTagSize();
}
@@ -275,7 +292,7 @@ void APE::File::read(bool readProperties)
if(readProperties) {
long long streamLength;
long streamLength;
if(d->APELocation >= 0)
streamLength = d->APELocation;
@@ -292,6 +309,6 @@ void APE::File::read(bool readProperties)
seek(0);
}
d->properties.reset(new AudioProperties(this, streamLength));
d->properties = new Properties(this, streamLength);
}
}

View File

@@ -90,7 +90,7 @@ namespace TagLib {
* \note In the current implementation, \a propertiesStyle is ignored.
*/
File(FileName file, bool readProperties = true,
AudioProperties::ReadStyle propertiesStyle = AudioProperties::Average);
Properties::ReadStyle propertiesStyle = Properties::Average);
/*!
* Constructs an APE file from \a stream. If \a readProperties is true the
@@ -102,7 +102,7 @@ namespace TagLib {
* \note In the current implementation, \a propertiesStyle is ignored.
*/
File(IOStream *stream, bool readProperties = true,
AudioProperties::ReadStyle propertiesStyle = AudioProperties::Average);
Properties::ReadStyle propertiesStyle = Properties::Average);
/*!
* Destroys this instance of the File.
@@ -115,18 +115,31 @@ namespace TagLib {
*/
virtual TagLib::Tag *tag() const;
/*!
* 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;
/*!
* Removes unsupported properties. Forwards to the actual Tag's
* removeUnsupportedProperties() function.
*/
void removeUnsupportedProperties(const StringList &properties);
/*!
* Implements the unified property interface -- import function.
* Creates an APEv2 tag if necessary. A potentially existing ID3v1
* tag will be updated as well.
*/
virtual PropertyMap setProperties(const PropertyMap &);
PropertyMap setProperties(const PropertyMap &);
/*!
* Returns the APE::Properties for this file. If no audio properties
* were read then this will return a null pointer.
*/
virtual AudioProperties *audioProperties() const;
virtual Properties *audioProperties() const;
/*!
* Saves the file.

View File

@@ -135,8 +135,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 +157,7 @@ ByteVector APE::Footer::renderHeader() const
{
if(!d->headerPresent)
return ByteVector();
else
return render(true);
return render(true);
}
////////////////////////////////////////////////////////////////////////////////
@@ -175,19 +173,19 @@ void APE::Footer::parse(const ByteVector &data)
// Read the version number
d->version = data.toUInt32LE(8);
d->version = data.toUInt(8, false);
// Read the tag size
d->tagSize = data.toUInt32LE(12);
d->tagSize = data.toUInt(12, false);
// Read the item count
d->itemCount = data.toUInt32LE(16);
d->itemCount = data.toUInt(16, false);
// Read the flags
std::bitset<32> flags(TAGLIB_CONSTRUCT_BITSET(data.toUInt32LE(20)));
std::bitset<32> flags(TAGLIB_CONSTRUCT_BITSET(data.toUInt(20, false)));
d->headerPresent = flags[31];
d->footerPresent = !flags[30];
@@ -206,15 +204,15 @@ ByteVector APE::Footer::render(bool isHeader) const
// add the version number -- we always render a 2.000 tag regardless of what
// the tag originally was.
v.append(ByteVector::fromUInt32LE(2000));
v.append(ByteVector::fromUInt(2000, false));
// add the tag size
v.append(ByteVector::fromUInt32LE(d->tagSize));
v.append(ByteVector::fromUInt(d->tagSize, false));
// add the item count
v.append(ByteVector::fromUInt32LE(d->itemCount));
v.append(ByteVector::fromUInt(d->itemCount, false));
// render and add the flags
@@ -224,11 +222,11 @@ ByteVector APE::Footer::render(bool isHeader) const
flags[30] = false; // footer is always present
flags[29] = isHeader;
v.append(ByteVector::fromUInt32LE(flags.to_ulong()));
v.append(ByteVector::fromUInt(flags.to_ulong(), false));
// add the reserved 64bit
v.append(ByteVector::fromUInt64BE(0));
v.append(ByteVector::fromLongLong(0));
return v;
}

View File

@@ -54,7 +54,7 @@ namespace TagLib {
* Constructs an APE footer based on \a data. parse() is called
* immediately.
*/
explicit Footer(const ByteVector &data);
Footer(const ByteVector &data);
/*!
* Destroys the footer.

View File

@@ -25,17 +25,17 @@
#include <tbytevectorlist.h>
#include <tdebug.h>
#include <tsmartptr.h>
#include "apeitem.h"
using namespace TagLib;
using namespace APE;
struct ItemData
class APE::Item::ItemPrivate
{
ItemData() :
type(Item::Text),
public:
ItemPrivate() :
type(Text),
readOnly(false) {}
Item::ItemTypes type;
@@ -45,15 +45,6 @@ struct ItemData
bool readOnly;
};
class APE::Item::ItemPrivate
{
public:
ItemPrivate() :
data(new ItemData()) {}
SHARED_PTR<ItemData> data;
};
////////////////////////////////////////////////////////////////////////////////
// public members
////////////////////////////////////////////////////////////////////////////////
@@ -66,27 +57,27 @@ APE::Item::Item() :
APE::Item::Item(const String &key, const String &value) :
d(new ItemPrivate())
{
d->data->key = key;
d->data->text.append(value);
d->key = key;
d->text.append(value);
}
APE::Item::Item(const String &key, const StringList &values) :
d(new ItemPrivate())
{
d->data->key = key;
d->data->text = values;
d->key = key;
d->text = values;
}
APE::Item::Item(const String &key, const ByteVector &value, bool binary) :
d(new ItemPrivate())
{
d->data->key = key;
d->key = key;
if(binary) {
d->data->type = Binary;
d->data->value = value;
d->type = Binary;
d->value = value;
}
else {
d->data->text.append(value);
d->text.append(value);
}
}
@@ -115,122 +106,132 @@ void APE::Item::swap(Item &item)
void APE::Item::setReadOnly(bool readOnly)
{
d->data->readOnly = readOnly;
d->readOnly = readOnly;
}
bool APE::Item::isReadOnly() const
{
return d->data->readOnly;
return d->readOnly;
}
void APE::Item::setType(APE::Item::ItemTypes val)
{
d->data->type = val;
d->type = val;
}
APE::Item::ItemTypes APE::Item::type() const
{
return d->data->type;
return d->type;
}
String APE::Item::key() const
{
return d->data->key;
return d->key;
}
ByteVector APE::Item::binaryData() const
{
return d->data->value;
return d->value;
}
void APE::Item::setBinaryData(const ByteVector &value)
{
d->data->type = Binary;
d->data->value = value;
d->data->text.clear();
d->type = Binary;
d->value = 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->data->key = key;
d->key = key;
}
void APE::Item::setValue(const String &value)
{
d->data->type = Text;
d->data->text = value;
d->data->value.clear();
d->type = Text;
d->text = value;
d->value.clear();
}
void APE::Item::setValues(const StringList &value)
{
d->data->type = Text;
d->data->text = value;
d->data->value.clear();
d->type = Text;
d->text = value;
d->value.clear();
}
void APE::Item::appendValue(const String &value)
{
d->data->type = Text;
d->data->text.append(value);
d->data->value.clear();
d->type = Text;
d->text.append(value);
d->value.clear();
}
void APE::Item::appendValues(const StringList &values)
{
d->data->type = Text;
d->data->text.append(values);
d->data->value.clear();
d->type = Text;
d->text.append(values);
d->value.clear();
}
int APE::Item::size() const
{
size_t result = 8 + d->data->key.size() + 1;
switch(d->data->type) {
int result = 8 + d->key.size() + 1;
switch(d->type) {
case Text:
if(!d->data->text.isEmpty()) {
StringList::ConstIterator it = d->data->text.begin();
if(!d->text.isEmpty()) {
StringList::ConstIterator it = d->text.begin();
result += it->data(String::UTF8).size();
it++;
for(; it != d->data->text.end(); ++it)
for(; it != d->text.end(); ++it)
result += 1 + it->data(String::UTF8).size();
}
break;
case Binary:
case Locator:
result += d->data->value.size();
result += d->value.size();
break;
}
return static_cast<int>(result);
return result;
}
StringList APE::Item::toStringList() const
{
return d->text;
}
StringList APE::Item::values() const
{
return d->data->text;
return d->text;
}
String APE::Item::toString() const
{
if(d->data->type == Text && !isEmpty())
return d->data->text.front();
else
return String();
if(d->type == Text && !isEmpty())
return d->text.front();
return String();
}
bool APE::Item::isEmpty() const
{
switch(d->data->type) {
switch(d->type) {
case Text:
if(d->data->text.isEmpty())
if(d->text.isEmpty())
return true;
if(d->data->text.size() == 1 && d->data->text.front().isEmpty())
return true;
return false;
return d->text.size() == 1 && d->text.front().isEmpty();
case Binary:
case Locator:
return d->data->value.isEmpty();
return d->value.isEmpty();
default:
return false;
}
@@ -245,51 +246,51 @@ void APE::Item::parse(const ByteVector &data)
return;
}
const unsigned int valueLength = data.toUInt32LE(0);
const unsigned int flags = data.toUInt32LE(4);
const unsigned int valueLength = data.toUInt(0, false);
const unsigned int flags = data.toUInt(4, false);
// An item key can contain ASCII characters from 0x20 up to 0x7E, not UTF-8.
// We assume that the validity of the given key has been checked.
d->data->key = String(&data[8], String::Latin1);
d->key = String(&data[8], String::Latin1);
const ByteVector value = data.mid(8 + d->data->key.size() + 1, valueLength);
const ByteVector value = data.mid(8 + d->key.size() + 1, valueLength);
setReadOnly(flags & 1);
setType(ItemTypes((flags >> 1) & 3));
if(Text == d->data->type)
d->data->text = StringList(ByteVectorList::split(value, '\0'), String::UTF8);
if(Text == d->type)
d->text = StringList(ByteVectorList::split(value, '\0'), String::UTF8);
else
d->data->value = value;
d->value = value;
}
ByteVector APE::Item::render() const
{
ByteVector data;
unsigned int flags = ((d->data->readOnly) ? 1 : 0) | (d->data->type << 1);
unsigned int flags = ((d->readOnly) ? 1 : 0) | (d->type << 1);
ByteVector value;
if(isEmpty())
return data;
if(d->data->type == Text) {
StringList::ConstIterator it = d->data->text.begin();
if(d->type == Text) {
StringList::ConstIterator it = d->text.begin();
value.append(it->data(String::UTF8));
it++;
for(; it != d->data->text.end(); ++it) {
for(; it != d->text.end(); ++it) {
value.append('\0');
value.append(it->data(String::UTF8));
}
d->data->value = value;
d->value = value;
}
else
value.append(d->data->value);
value.append(d->value);
data.append(ByteVector::fromUInt32LE(value.size()));
data.append(ByteVector::fromUInt32LE(flags));
data.append(d->data->key.data(String::Latin1));
data.append(ByteVector::fromUInt(value.size(), false));
data.append(ByteVector::fromUInt(flags, false));
data.append(d->key.data(String::Latin1));
data.append(ByteVector('\0'));
data.append(value);

View File

@@ -61,6 +61,7 @@ namespace TagLib {
/*!
* 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);
/*!
@@ -111,6 +112,11 @@ 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.
*/
@@ -157,6 +163,11 @@ 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.

View File

@@ -29,7 +29,7 @@
#include <tstring.h>
#include <tdebug.h>
#include <bitset>
#include "id3v2tag.h"
#include "apeproperties.h"
#include "apefile.h"
@@ -38,7 +38,7 @@
using namespace TagLib;
class APE::AudioProperties::PropertiesPrivate
class APE::Properties::PropertiesPrivate
{
public:
PropertiesPrivate() :
@@ -63,59 +63,66 @@ public:
// public members
////////////////////////////////////////////////////////////////////////////////
APE::AudioProperties::AudioProperties(File *file, long long streamLength, ReadStyle) :
TagLib::AudioProperties(),
APE::Properties::Properties(File *, 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())
{
read(file, streamLength);
}
APE::AudioProperties::~AudioProperties()
APE::Properties::~Properties()
{
delete d;
}
int APE::AudioProperties::length() const
int APE::Properties::length() const
{
return lengthInSeconds();
}
int APE::AudioProperties::lengthInSeconds() const
int APE::Properties::lengthInSeconds() const
{
return d->length / 1000;
}
int APE::AudioProperties::lengthInMilliseconds() const
int APE::Properties::lengthInMilliseconds() const
{
return d->length;
}
int APE::AudioProperties::bitrate() const
int APE::Properties::bitrate() const
{
return d->bitrate;
}
int APE::AudioProperties::sampleRate() const
int APE::Properties::sampleRate() const
{
return d->sampleRate;
}
int APE::AudioProperties::channels() const
int APE::Properties::channels() const
{
return d->channels;
}
int APE::AudioProperties::version() const
int APE::Properties::version() const
{
return d->version;
}
int APE::AudioProperties::bitsPerSample() const
int APE::Properties::bitsPerSample() const
{
return d->bitsPerSample;
}
unsigned int APE::AudioProperties::sampleFrames() const
unsigned int APE::Properties::sampleFrames() const
{
return d->sampleFrames;
}
@@ -131,14 +138,14 @@ namespace
if(header.size() < 6 || !header.startsWith("MAC "))
return -1;
return header.toUInt16LE(4);
return header.toUShort(4, false);
}
}
void APE::AudioProperties::read(File *file, long long streamLength)
void APE::Properties::read(File *file, long streamLength)
{
// First, we assume that the file pointer is set at the first descriptor.
long long offset = file->tell();
long offset = file->tell();
int version = headerVersion(file->readBlock(6));
// Next, we look for the descriptor.
@@ -149,7 +156,7 @@ void APE::AudioProperties::read(File *file, long long streamLength)
}
if(version < 0) {
debug("APE::AudioProperties::read() -- APE descriptor not found");
debug("APE::Properties::read() -- APE descriptor not found");
return;
}
@@ -167,17 +174,17 @@ void APE::AudioProperties::read(File *file, long long streamLength)
}
}
void APE::AudioProperties::analyzeCurrent(File *file)
void APE::Properties::analyzeCurrent(File *file)
{
// Read the descriptor
file->seek(2, File::Current);
const ByteVector descriptor = file->readBlock(44);
if(descriptor.size() < 44) {
debug("APE::AudioProperties::analyzeCurrent() -- descriptor is too short.");
debug("APE::Properties::analyzeCurrent() -- descriptor is too short.");
return;
}
const unsigned int descriptorBytes = descriptor.toUInt32LE(0);
const unsigned int descriptorBytes = descriptor.toUInt(0, false);
if((descriptorBytes - 52) > 0)
file->seek(descriptorBytes - 52, File::Current);
@@ -185,39 +192,39 @@ void APE::AudioProperties::analyzeCurrent(File *file)
// Read the header
const ByteVector header = file->readBlock(24);
if(header.size() < 24) {
debug("APE::AudioProperties::analyzeCurrent() -- MAC header is too short.");
debug("APE::Properties::analyzeCurrent() -- MAC header is too short.");
return;
}
// Get the APE info
d->channels = header.toUInt16LE(18);
d->sampleRate = header.toUInt32LE(20);
d->bitsPerSample = header.toUInt16LE(16);
d->channels = header.toShort(18, false);
d->sampleRate = header.toUInt(20, false);
d->bitsPerSample = header.toShort(16, false);
const unsigned int totalFrames = header.toUInt32LE(12);
const unsigned int totalFrames = header.toUInt(12, false);
if(totalFrames == 0)
return;
const unsigned int blocksPerFrame = header.toUInt32LE(4);
const unsigned int finalFrameBlocks = header.toUInt32LE(8);
const unsigned int blocksPerFrame = header.toUInt(4, false);
const unsigned int finalFrameBlocks = header.toUInt(8, false);
d->sampleFrames = (totalFrames - 1) * blocksPerFrame + finalFrameBlocks;
}
void APE::AudioProperties::analyzeOld(File *file)
void APE::Properties::analyzeOld(File *file)
{
const ByteVector header = file->readBlock(26);
if(header.size() < 26) {
debug("APE::AudioProperties::analyzeOld() -- MAC header is too short.");
debug("APE::Properties::analyzeOld() -- MAC header is too short.");
return;
}
const unsigned int totalFrames = header.toUInt32LE(18);
const unsigned int totalFrames = header.toUInt(18, false);
// Fail on 0 length APE files (catches non-finalized APE files)
if(totalFrames == 0)
return;
const short compressionLevel = header.toUInt32LE(0);
const short compressionLevel = header.toShort(0, false);
unsigned int blocksPerFrame;
if(d->version >= 3950)
blocksPerFrame = 73728 * 4;
@@ -227,19 +234,19 @@ void APE::AudioProperties::analyzeOld(File *file)
blocksPerFrame = 9216;
// Get the APE info
d->channels = header.toUInt16LE(4);
d->sampleRate = header.toUInt32LE(6);
d->channels = header.toShort(4, false);
d->sampleRate = header.toUInt(6, false);
const unsigned int finalFrameBlocks = header.toUInt32LE(22);
const unsigned int finalFrameBlocks = header.toUInt(22, false);
d->sampleFrames = (totalFrames - 1) * blocksPerFrame + finalFrameBlocks;
// Get the bit depth from the RIFF-fmt chunk.
file->seek(16, File::Current);
const ByteVector fmt = file->readBlock(28);
if(fmt.size() < 28 || !fmt.startsWith("WAVEfmt ")) {
debug("APE::AudioProperties::analyzeOld() -- fmt header is too short.");
debug("APE::Properties::analyzeOld() -- fmt header is too short.");
return;
}
d->bitsPerSample = fmt.toUInt16LE(26);
d->bitsPerSample = fmt.toShort(26, false);
}

View File

@@ -46,19 +46,27 @@ namespace TagLib {
* API.
*/
class TAGLIB_EXPORT AudioProperties : public TagLib::AudioProperties
class TAGLIB_EXPORT Properties : public AudioProperties
{
public:
/*!
* Create an instance of APE::Properties with the data read from the
* APE::File \a file.
*
* \deprecated
*/
AudioProperties(File *file, long long streamLength, ReadStyle style = Average);
TAGLIB_DEPRECATED Properties(File *file, ReadStyle style = Average);
/*!
* Destroys this APE::AudioProperties instance.
* Create an instance of APE::Properties with the data read from the
* APE::File \a file.
*/
virtual ~AudioProperties();
Properties(File *file, long streamLength, ReadStyle style = Average);
/*!
* Destroys this APE::Properties instance.
*/
virtual ~Properties();
/*!
* Returns the length of the file in seconds. The length is rounded down to
@@ -68,7 +76,7 @@ namespace TagLib {
*
* \deprecated
*/
virtual int length() const;
TAGLIB_DEPRECATED virtual int length() const;
/*!
* Returns the length of the file in seconds. The length is rounded down to
@@ -76,14 +84,16 @@ namespace TagLib {
*
* \see lengthInMilliseconds()
*/
virtual int lengthInSeconds() const;
// BIC: make virtual
int lengthInSeconds() const;
/*!
* Returns the length of the file in milliseconds.
*
* \see lengthInSeconds()
*/
virtual int lengthInMilliseconds() const;
// BIC: make virtual
int lengthInMilliseconds() const;
/*!
* Returns the average bit rate of the file in kb/s.
@@ -116,7 +126,11 @@ namespace TagLib {
int version() const;
private:
void read(File *file, long long streamLength);
Properties(const Properties &);
Properties &operator=(const Properties &);
void read(File *file, long streamLength);
void analyzeCurrent(File *file);
void analyzeOld(File *file);

View File

@@ -34,7 +34,6 @@
#include <tfile.h>
#include <tstring.h>
#include <tmap.h>
#include <tpicturemap.h>
#include <tpropertymap.h>
#include <tdebug.h>
#include <tutils.h>
@@ -71,7 +70,7 @@ namespace
return true;
}
}
} // namespace
class APE::Tag::TagPrivate
{
@@ -81,7 +80,7 @@ public:
footerLocation(0) {}
File *file;
long long footerLocation;
long footerLocation;
Footer footer;
ItemListMap itemListMap;
@@ -92,13 +91,11 @@ public:
////////////////////////////////////////////////////////////////////////////////
APE::Tag::Tag() :
TagLib::Tag(),
d(new TagPrivate())
{
}
APE::Tag::Tag(TagLib::File *file, long long footerLocation) :
TagLib::Tag(),
APE::Tag::Tag(TagLib::File *file, long footerLocation) :
d(new TagPrivate())
{
d->file = file;
@@ -119,87 +116,44 @@ ByteVector APE::Tag::fileIdentifier()
String APE::Tag::title() const
{
if(d->itemListMap["TITLE"].isEmpty())
return String();
return d->itemListMap["TITLE"].values().toString();
Item value = d->itemListMap.value("TITLE");
return value.isEmpty() ? String() : value.values().toString();
}
String APE::Tag::artist() const
{
if(d->itemListMap["ARTIST"].isEmpty())
return String();
return d->itemListMap["ARTIST"].values().toString();
Item value = d->itemListMap.value("ARTIST");
return value.isEmpty() ? String() : value.values().toString();
}
String APE::Tag::album() const
{
if(d->itemListMap["ALBUM"].isEmpty())
return String();
return d->itemListMap["ALBUM"].values().toString();
Item value = d->itemListMap.value("ALBUM");
return value.isEmpty() ? String() : value.values().toString();
}
String APE::Tag::comment() const
{
if(d->itemListMap["COMMENT"].isEmpty())
return String();
return d->itemListMap["COMMENT"].values().toString();
Item value = d->itemListMap.value("COMMENT");
return value.isEmpty() ? String() : value.values().toString();
}
String APE::Tag::genre() const
{
if(d->itemListMap["GENRE"].isEmpty())
return String();
return d->itemListMap["GENRE"].values().toString();
Item value = d->itemListMap.value("GENRE");
return value.isEmpty() ? String() : value.values().toString();
}
unsigned int APE::Tag::year() const
{
if(d->itemListMap["YEAR"].isEmpty())
return 0;
return d->itemListMap["YEAR"].toString().toInt();
Item value = d->itemListMap.value("YEAR");
return value.isEmpty() ? 0 : value.toString().toInt();
}
unsigned int APE::Tag::track() const
{
if(d->itemListMap["TRACK"].isEmpty())
return 0;
return d->itemListMap["TRACK"].toString().toInt();
}
TagLib::PictureMap APE::Tag::pictures() const
{
PictureMap map;
if(d->itemListMap.contains(FRONT_COVER)) {
Item front = d->itemListMap[FRONT_COVER];
if(Item::Binary == front.type()) {
ByteVector picture = front.binaryData();
const size_t index = picture.find('\0');
if(index < picture.size()) {
ByteVector desc = picture.mid(0, index + 1);
String mime = "image/jpeg";
ByteVector data = picture.mid(index + 1);
Picture p(data, Picture::FrontCover, mime, desc);
map.insert(p);
}
}
}
if(d->itemListMap.contains(BACK_COVER)) {
Item back = d->itemListMap[BACK_COVER];
if(Item::Binary == back.type()) {
ByteVector picture = back.binaryData();
const size_t index = picture.find('\0');
if(index < picture.size()) {
ByteVector desc = picture.mid(0, index + 1);
String mime = "image/jpeg";
ByteVector data = picture.mid(index + 1);
Picture p(data, Picture::BackCover, mime, desc);
map.insert(p);
}
}
}
return PictureMap(map);
Item value = d->itemListMap.value("TRACK");
return value.isEmpty() ? 0 : value.toString().toInt();
}
void APE::Tag::setTitle(const String &s)
@@ -243,75 +197,22 @@ void APE::Tag::setTrack(unsigned int i)
addValue("TRACK", String::number(i), true);
}
void APE::Tag::setPictures(const PictureMap &l)
{
removeItem(FRONT_COVER);
removeItem(BACK_COVER);
for(PictureMap::ConstIterator pictureMapIt = l.begin();
pictureMapIt != l.end();
++pictureMapIt) {
Picture::Type type = pictureMapIt->first;
if(Picture::FrontCover != type && Picture::BackCover != type) {
std::cout << "APE: Trying to add a picture with wrong type"
<< std::endl;
continue;
}
const char *id;
switch(type) {
case Picture::FrontCover:
id = FRONT_COVER;
break;
case Picture::BackCover:
id = BACK_COVER;
break;
default:
id = FRONT_COVER;
break;
}
PictureList list = pictureMapIt->second;
for(PictureList::ConstIterator pictureListIt = list.begin();
pictureListIt != list.end();
++pictureListIt) {
Picture picture = *pictureListIt;
if(d->itemListMap.contains(id)) {
std::cout << "APE: Already added a picture of type "
<< id
<< " '"
<< picture.description()
<< "' "
<< "and next are being ignored"
<< std::endl;
break;
}
ByteVector data = picture.description().data(String::Latin1)
.append('\0')
.append(picture.data());
Item item;
item.setKey(id);
item.setType(Item::Binary);
item.setBinaryData(data);
setItem(item.key(), item);
}
}
}
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" }};
// usual, APE
const std::pair<const char *, const char *> keyConversions[] = {
std::make_pair("TRACKNUMBER", "TRACK"),
std::make_pair("DATE", "YEAR"),
std::make_pair("ALBUMARTIST", "ALBUM ARTIST"),
std::make_pair("DISCNUMBER", "DISC"),
std::make_pair("REMIXER", "MIXARTIST"),
std::make_pair("RELEASESTATUS", "MUSICBRAINZ_ALBUMSTATUS"),
std::make_pair("RELEASETYPE", "MUSICBRAINZ_ALBUMTYPE"),
};
const size_t keyConversionsSize = sizeof(keyConversions) / sizeof(keyConversions[0]);
}
} // namespace
PropertyMap APE::Tag::properties() const
{
@@ -327,10 +228,10 @@ PropertyMap APE::Tag::properties() const
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];
if(tagName == keyConversions[i].second)
tagName = keyConversions[i].first;
}
properties[tagName].append(it->second.values());
properties[tagName].append(it->second.toStringList());
}
}
return properties;
@@ -349,9 +250,9 @@ PropertyMap APE::Tag::setProperties(const PropertyMap &origProps)
// 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]);
if(properties.contains(keyConversions[i].first)) {
properties.insert(keyConversions[i].second, properties[keyConversions[i].first]);
properties.erase(keyConversions[i].first);
}
// first check if tags need to be removed completely
@@ -487,7 +388,7 @@ ByteVector APE::Tag::render() const
}
d->footer.setItemCount(itemCount);
d->footer.setTagSize(static_cast<unsigned int>(data.size() + Footer::size()));
d->footer.setTagSize(data.size() + Footer::size());
d->footer.setHeaderPresent(true);
return d->footer.renderHeader() + data + d->footer.renderFooter();
@@ -500,18 +401,23 @@ void APE::Tag::parse(const ByteVector &data)
if(data.size() < 11)
return;
size_t pos = 0;
unsigned int pos = 0;
for(unsigned int i = 0; i < d->footer.itemCount() && pos <= data.size() - 11; i++) {
const size_t nullPos = data.find('\0', pos + 8);
if(nullPos == ByteVector::npos()) {
const int nullPos = data.find('\0', pos + 8);
if(nullPos < 0) {
debug("APE::Tag::parse() - Couldn't find a key/value separator. Stopped parsing.");
return;
}
const size_t keyLength = nullPos - pos - 8;
const size_t valLegnth = data.toUInt32LE(pos);
const unsigned int keyLength = nullPos - pos - 8;
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
@@ -526,6 +432,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

@@ -34,9 +34,6 @@
#include "apeitem.h"
#define FRONT_COVER "COVER ART (FRONT)"
#define BACK_COVER "COVER ART (BACK)"
namespace TagLib {
class File;
@@ -52,7 +49,7 @@ namespace TagLib {
*
* \see APE::Tag::itemListMap()
*/
typedef Map<String, Item> ItemListMap;
typedef Map<const String, Item> ItemListMap;
//! An APE tag implementation
@@ -69,7 +66,7 @@ namespace TagLib {
* Create an APE tag and parse the data in \a file with APE footer at
* \a tagOffset.
*/
Tag(TagLib::File *file, long long footerLocation);
Tag(TagLib::File *file, long footerLocation);
/*!
* Destroys this Tag instance.
@@ -98,15 +95,6 @@ namespace TagLib {
virtual unsigned int year() const;
virtual unsigned int track() const;
/**
* @brief pictures
* According to :
* http://www.hydrogenaud.io/forums/index.php?showtopic=40603&st=50&p=504669&#entry504669
* http://git.videolan.org/?p=vlc.git;a=blob;f=modules/meta_engine/taglib.cpp
* @return
*/
virtual PictureMap pictures() const;
virtual void setTitle(const String &s);
virtual void setArtist(const String &s);
virtual void setAlbum(const String &s);
@@ -114,7 +102,6 @@ namespace TagLib {
virtual void setGenre(const String &s);
virtual void setYear(unsigned int i);
virtual void setTrack(unsigned int i);
virtual void setPictures(const PictureMap &l);
/*!
* Implements the unified tag dictionary interface -- export function.

View File

@@ -1,4 +1,4 @@
/**************************************************************************
/**************************************************************************
copyright : (C) 2005-2007 by Lukáš Lalinský
email : lalinsky@gmail.com
**************************************************************************/
@@ -25,7 +25,7 @@
#include <taglib.h>
#include <tdebug.h>
#include <tsmartptr.h>
#include <trefcounter.h>
#include "asfattribute.h"
#include "asffile.h"
@@ -33,35 +33,21 @@
using namespace TagLib;
namespace
{
struct AttributeData
{
AttributeData() :
numericValue(0),
stream(0),
language(0) {}
ASF::Attribute::AttributeTypes type;
String stringValue;
ByteVector byteVectorValue;
ASF::Picture pictureValue;
unsigned long long numericValue;
int stream;
int language;
};
}
class ASF::Attribute::AttributePrivate
class ASF::Attribute::AttributePrivate : public RefCounter
{
public:
AttributePrivate() :
data(new AttributeData())
{
data->pictureValue = ASF::Picture::fromInvalid();
}
SHARED_PTR<AttributeData> data;
pictureValue(ASF::Picture::fromInvalid()),
numericValue(0),
stream(0),
language(0) {}
AttributeTypes type;
String stringValue;
ByteVector byteVectorValue;
ASF::Picture pictureValue;
unsigned long long numericValue;
int stream;
int language;
};
////////////////////////////////////////////////////////////////////////////////
@@ -71,61 +57,62 @@ public:
ASF::Attribute::Attribute() :
d(new AttributePrivate())
{
d->data->type = UnicodeType;
d->type = UnicodeType;
}
ASF::Attribute::Attribute(const ASF::Attribute &other) :
d(new AttributePrivate(*other.d))
d(other.d)
{
d->ref();
}
ASF::Attribute::Attribute(const String &value) :
d(new AttributePrivate())
{
d->data->type = UnicodeType;
d->data->stringValue = value;
d->type = UnicodeType;
d->stringValue = value;
}
ASF::Attribute::Attribute(const ByteVector &value) :
d(new AttributePrivate())
{
d->data->type = BytesType;
d->data->byteVectorValue = value;
d->type = BytesType;
d->byteVectorValue = value;
}
ASF::Attribute::Attribute(const ASF::Picture &value) :
d(new AttributePrivate())
{
d->data->type = BytesType;
d->data->pictureValue = value;
d->type = BytesType;
d->pictureValue = value;
}
ASF::Attribute::Attribute(unsigned int value) :
d(new AttributePrivate())
{
d->data->type = DWordType;
d->data->numericValue = value;
d->type = DWordType;
d->numericValue = value;
}
ASF::Attribute::Attribute(unsigned long long value) :
d(new AttributePrivate())
{
d->data->type = QWordType;
d->data->numericValue = value;
d->type = QWordType;
d->numericValue = value;
}
ASF::Attribute::Attribute(unsigned short value) :
d(new AttributePrivate())
{
d->data->type = WordType;
d->data->numericValue = value;
d->type = WordType;
d->numericValue = value;
}
ASF::Attribute::Attribute(bool value) :
d(new AttributePrivate())
{
d->data->type = BoolType;
d->data->numericValue = value;
d->type = BoolType;
d->numericValue = value;
}
ASF::Attribute &ASF::Attribute::operator=(const ASF::Attribute &other)
@@ -143,62 +130,62 @@ void ASF::Attribute::swap(Attribute &other)
ASF::Attribute::~Attribute()
{
delete d;
if(d->deref())
delete d;
}
ASF::Attribute::AttributeTypes ASF::Attribute::type() const
{
return d->data->type;
return d->type;
}
String ASF::Attribute::toString() const
{
return d->data->stringValue;
return d->stringValue;
}
ByteVector ASF::Attribute::toByteVector() const
{
if(d->data->pictureValue.isValid())
return d->data->pictureValue.render();
return d->data->byteVectorValue;
if(d->pictureValue.isValid())
return d->pictureValue.render();
return d->byteVectorValue;
}
unsigned short ASF::Attribute::toBool() const
{
return d->data->numericValue ? 1 : 0;
return d->numericValue ? 1 : 0;
}
unsigned short ASF::Attribute::toUShort() const
{
return static_cast<unsigned short>(d->data->numericValue);
return static_cast<unsigned short>(d->numericValue);
}
unsigned int ASF::Attribute::toUInt() const
{
return static_cast<unsigned int>(d->data->numericValue);
return static_cast<unsigned int>(d->numericValue);
}
unsigned long long ASF::Attribute::toULongLong() const
{
return static_cast<unsigned long long>(d->data->numericValue);
return static_cast<unsigned long long>(d->numericValue);
}
ASF::Picture ASF::Attribute::toPicture() const
{
return d->data->pictureValue;
return d->pictureValue;
}
String ASF::Attribute::parse(ASF::File &f, int kind)
{
unsigned int size, nameLength;
String name;
d->data->pictureValue = Picture::fromInvalid();
d->pictureValue = Picture::fromInvalid();
// extended content descriptor
if(kind == 0) {
nameLength = readWORD(&f);
name = readString(&f, nameLength);
d->data->type = ASF::Attribute::AttributeTypes(readWORD(&f));
d->type = ASF::Attribute::AttributeTypes(readWORD(&f));
size = readWORD(&f);
}
// metadata & metadata library
@@ -206,11 +193,11 @@ String ASF::Attribute::parse(ASF::File &f, int kind)
int temp = readWORD(&f);
// metadata library
if(kind == 2) {
d->data->language = temp;
d->language = temp;
}
d->data->stream = readWORD(&f);
d->stream = readWORD(&f);
nameLength = readWORD(&f);
d->data->type = ASF::Attribute::AttributeTypes(readWORD(&f));
d->type = ASF::Attribute::AttributeTypes(readWORD(&f));
size = readDWORD(&f);
name = readString(&f, nameLength);
}
@@ -219,42 +206,42 @@ String ASF::Attribute::parse(ASF::File &f, int kind)
debug("ASF::Attribute::parse() -- Value larger than 64kB");
}
switch(d->data->type) {
switch(d->type) {
case WordType:
d->data->numericValue = readWORD(&f);
d->numericValue = readWORD(&f);
break;
case BoolType:
if(kind == 0) {
d->data->numericValue = (readDWORD(&f) != 0);
d->numericValue = (readDWORD(&f) != 0);
}
else {
d->data->numericValue = (readWORD(&f) != 0);
d->numericValue = (readWORD(&f) != 0);
}
break;
case DWordType:
d->data->numericValue = readDWORD(&f);
d->numericValue = readDWORD(&f);
break;
case QWordType:
d->data->numericValue = readQWORD(&f);
d->numericValue = readQWORD(&f);
break;
case UnicodeType:
d->data->stringValue = readString(&f, size);
d->stringValue = readString(&f, size);
break;
case BytesType:
case GuidType:
d->data->byteVectorValue = f.readBlock(size);
d->byteVectorValue = f.readBlock(size);
break;
}
if(d->data->type == BytesType && name == "WM/Picture") {
d->data->pictureValue.parse(d->data->byteVectorValue);
if(d->data->pictureValue.isValid()) {
d->data->byteVectorValue.clear();
if(d->type == BytesType && name == "WM/Picture") {
d->pictureValue.parse(d->byteVectorValue);
if(d->pictureValue.isValid()) {
d->byteVectorValue.clear();
}
}
@@ -263,7 +250,7 @@ String ASF::Attribute::parse(ASF::File &f, int kind)
int ASF::Attribute::dataSize() const
{
switch (d->data->type) {
switch (d->type) {
case WordType:
return 2;
case BoolType:
@@ -273,12 +260,12 @@ int ASF::Attribute::dataSize() const
case QWordType:
return 5;
case UnicodeType:
return static_cast<int>(d->data->stringValue.size() * 2 + 2);
return d->stringValue.size() * 2 + 2;
case BytesType:
if(d->data->pictureValue.isValid())
return d->data->pictureValue.dataSize();
if(d->pictureValue.isValid())
return d->pictureValue.dataSize();
case GuidType:
return static_cast<int>(d->data->byteVectorValue.size());
return d->byteVectorValue.size();
}
return 0;
}
@@ -287,55 +274,55 @@ ByteVector ASF::Attribute::render(const String &name, int kind) const
{
ByteVector data;
switch (d->data->type) {
switch (d->type) {
case WordType:
data.append(ByteVector::fromUInt16LE(toUShort()));
data.append(ByteVector::fromShort(toUShort(), false));
break;
case BoolType:
if(kind == 0) {
data.append(ByteVector::fromUInt32LE(toBool()));
data.append(ByteVector::fromUInt(toBool(), false));
}
else {
data.append(ByteVector::fromUInt16LE(toBool()));
data.append(ByteVector::fromShort(toBool(), false));
}
break;
case DWordType:
data.append(ByteVector::fromUInt32LE(toUInt()));
data.append(ByteVector::fromUInt(toUInt(), false));
break;
case QWordType:
data.append(ByteVector::fromUInt64LE(toULongLong()));
data.append(ByteVector::fromLongLong(toULongLong(), false));
break;
case UnicodeType:
data.append(renderString(d->data->stringValue));
data.append(renderString(d->stringValue));
break;
case BytesType:
if(d->data->pictureValue.isValid()) {
data.append(d->data->pictureValue.render());
if(d->pictureValue.isValid()) {
data.append(d->pictureValue.render());
break;
}
case GuidType:
data.append(d->data->byteVectorValue);
data.append(d->byteVectorValue);
break;
}
if(kind == 0) {
data = renderString(name, true) +
ByteVector::fromUInt16LE((int)d->data->type) +
ByteVector::fromUInt16LE(data.size()) +
ByteVector::fromShort(static_cast<int>(d->type), false) +
ByteVector::fromShort(data.size(), false) +
data;
}
else {
ByteVector nameData = renderString(name);
data = ByteVector::fromUInt16LE(kind == 2 ? d->data->language : 0) +
ByteVector::fromUInt16LE(d->data->stream) +
ByteVector::fromUInt16LE(nameData.size()) +
ByteVector::fromUInt16LE((int)d->data->type) +
ByteVector::fromUInt32LE(data.size()) +
data = ByteVector::fromShort(kind == 2 ? d->language : 0, false) +
ByteVector::fromShort(d->stream, false) +
ByteVector::fromShort(nameData.size(), false) +
ByteVector::fromShort(static_cast<int>(d->type), false) +
ByteVector::fromUInt(data.size(), false) +
nameData +
data;
}
@@ -345,20 +332,20 @@ ByteVector ASF::Attribute::render(const String &name, int kind) const
int ASF::Attribute::language() const
{
return d->data->language;
return d->language;
}
void ASF::Attribute::setLanguage(int value)
{
d->data->language = value;
d->language = value;
}
int ASF::Attribute::stream() const
{
return d->data->stream;
return d->stream;
}
void ASF::Attribute::setStream(int value)
{
d->data->stream = value;
d->stream = value;
}

View File

@@ -36,6 +36,7 @@ namespace TagLib
namespace ASF
{
class File;
class Picture;
@@ -184,13 +185,17 @@ namespace TagLib
*/
void setStream(int value);
#ifndef DO_NOT_DOCUMENT
/* THIS IS PRIVATE, DON'T TOUCH IT! */
String parse(ASF::File &file, int kind = 0);
#endif
//! Returns the size of the stored data
int dataSize() const;
private:
friend class File;
String parse(ASF::File &file, int kind = 0);
ByteVector render(const String &name, int kind = 0) const;
class AttributePrivate;

View File

@@ -28,7 +28,6 @@
#include <tpropertymap.h>
#include <tstring.h>
#include <tagutils.h>
#include <tsmartptr.h>
#include "asffile.h"
#include "asftag.h"
@@ -51,24 +50,37 @@ public:
class MetadataObject;
class MetadataLibraryObject;
typedef List<SHARED_PTR<BaseObject> > ObjectList;
typedef ObjectList::ConstIterator ObjectConstIterator;
FilePrivate():
headerSize(0) {}
headerSize(0),
tag(0),
properties(0),
contentDescriptionObject(0),
extendedContentDescriptionObject(0),
headerExtensionObject(0),
metadataObject(0),
metadataLibraryObject(0)
{
objects.setAutoDelete(true);
}
~FilePrivate()
{
delete tag;
delete properties;
}
unsigned long long headerSize;
SCOPED_PTR<ASF::Tag> tag;
SCOPED_PTR<ASF::AudioProperties> properties;
ASF::Tag *tag;
ASF::Properties *properties;
ObjectList objects;
List<BaseObject *> objects;
SHARED_PTR<ContentDescriptionObject> contentDescriptionObject;
SHARED_PTR<ExtendedContentDescriptionObject> extendedContentDescriptionObject;
SHARED_PTR<HeaderExtensionObject> headerExtensionObject;
SHARED_PTR<MetadataObject> metadataObject;
SHARED_PTR<MetadataLibraryObject> metadataLibraryObject;
ContentDescriptionObject *contentDescriptionObject;
ExtendedContentDescriptionObject *extendedContentDescriptionObject;
HeaderExtensionObject *headerExtensionObject;
MetadataObject *metadataObject;
MetadataLibraryObject *metadataLibraryObject;
};
namespace
@@ -85,7 +97,7 @@ 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
{
@@ -101,7 +113,7 @@ class ASF::File::FilePrivate::UnknownObject : public ASF::File::FilePrivate::Bas
{
ByteVector myGuid;
public:
explicit UnknownObject(const ByteVector &guid);
UnknownObject(const ByteVector &guid);
ByteVector guid() const;
};
@@ -157,7 +169,7 @@ public:
class ASF::File::FilePrivate::HeaderExtensionObject : public ASF::File::FilePrivate::BaseObject
{
public:
ObjectList objects;
List<ASF::File::FilePrivate::BaseObject *> objects;
HeaderExtensionObject();
ByteVector guid() const;
void parse(ASF::File *file, unsigned int size);
@@ -182,7 +194,7 @@ private:
void ASF::File::FilePrivate::BaseObject::parse(ASF::File *file, unsigned int size)
{
data.clear();
if(size > 24 && static_cast<long long>(size) <= file->length())
if(size > 24 && size <= static_cast<unsigned int>(file->length()))
data = file->readBlock(size - 24);
else
data = ByteVector();
@@ -190,7 +202,7 @@ void ASF::File::FilePrivate::BaseObject::parse(ASF::File *file, unsigned int siz
ByteVector ASF::File::FilePrivate::BaseObject::render(ASF::File * /*file*/)
{
return guid() + ByteVector::fromUInt64LE(data.size() + 24) + data;
return guid() + ByteVector::fromLongLong(data.size() + 24, false) + data;
}
ASF::File::FilePrivate::UnknownObject::UnknownObject(const ByteVector &guid) : myGuid(guid)
@@ -215,8 +227,8 @@ void ASF::File::FilePrivate::FilePropertiesObject::parse(ASF::File *file, unsign
return;
}
const long long duration = data.toInt64LE(40);
const long long preroll = data.toInt64LE(56);
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));
}
@@ -233,11 +245,11 @@ void ASF::File::FilePrivate::StreamPropertiesObject::parse(ASF::File *file, unsi
return;
}
file->d->properties->setCodec(data.toUInt16LE(54));
file->d->properties->setChannels(data.toUInt16LE(56));
file->d->properties->setSampleRate(data.toUInt32LE(58));
file->d->properties->setBitrate(static_cast<int>(data.toUInt32LE(62) * 8.0 / 1000.0 + 0.5));
file->d->properties->setBitsPerSample(data.toUInt16LE(68));
file->d->properties->setCodec(data.toUShort(54, false));
file->d->properties->setChannels(data.toUShort(56, false));
file->d->properties->setSampleRate(data.toUInt(58, false));
file->d->properties->setBitrate(static_cast<int>(data.toUInt(62, false) * 8.0 / 1000.0 + 0.5));
file->d->properties->setBitsPerSample(data.toUShort(68, false));
}
ByteVector ASF::File::FilePrivate::ContentDescriptionObject::guid() const
@@ -267,11 +279,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::fromUInt16LE(v1.size()));
data.append(ByteVector::fromUInt16LE(v2.size()));
data.append(ByteVector::fromUInt16LE(v3.size()));
data.append(ByteVector::fromUInt16LE(v4.size()));
data.append(ByteVector::fromUInt16LE(v5.size()));
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(v1);
data.append(v2);
data.append(v3);
@@ -298,7 +310,7 @@ void ASF::File::FilePrivate::ExtendedContentDescriptionObject::parse(ASF::File *
ByteVector ASF::File::FilePrivate::ExtendedContentDescriptionObject::render(ASF::File *file)
{
data.clear();
data.append(ByteVector::fromUInt16LE(attributeData.size()));
data.append(ByteVector::fromShort(attributeData.size(), false));
data.append(attributeData.toByteVector(""));
return BaseObject::render(file);
}
@@ -321,7 +333,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::fromUInt16LE(attributeData.size()));
data.append(ByteVector::fromShort(attributeData.size(), false));
data.append(attributeData.toByteVector(""));
return BaseObject::render(file);
}
@@ -344,13 +356,14 @@ void ASF::File::FilePrivate::MetadataLibraryObject::parse(ASF::File *file, unsig
ByteVector ASF::File::FilePrivate::MetadataLibraryObject::render(ASF::File *file)
{
data.clear();
data.append(ByteVector::fromUInt16LE(attributeData.size()));
data.append(ByteVector::fromShort(attributeData.size(), false));
data.append(attributeData.toByteVector(""));
return BaseObject::render(file);
}
ASF::File::FilePrivate::HeaderExtensionObject::HeaderExtensionObject()
{
objects.setAutoDelete(true);
}
ByteVector ASF::File::FilePrivate::HeaderExtensionObject::guid() const
@@ -371,23 +384,23 @@ void ASF::File::FilePrivate::HeaderExtensionObject::parse(ASF::File *file, unsig
}
bool ok;
long long size = readQWORD(file, &ok);
if(!ok) {
if(!ok || size < 0 || size > dataSize - dataPos) {
file->setValid(false);
break;
}
SHARED_PTR<BaseObject> obj;
BaseObject *obj;
if(guid == metadataGuid) {
file->d->metadataObject.reset(new MetadataObject());
file->d->metadataObject = new MetadataObject();
obj = file->d->metadataObject;
}
else if(guid == metadataLibraryGuid) {
file->d->metadataLibraryObject.reset(new MetadataLibraryObject());
file->d->metadataLibraryObject = new MetadataLibraryObject();
obj = file->d->metadataLibraryObject;
}
else {
obj.reset(new UnknownObject(guid));
obj = new UnknownObject(guid);
}
obj->parse(file, (unsigned int)size);
obj->parse(file, static_cast<unsigned int>(size));
objects.append(obj);
dataPos += size;
}
@@ -396,10 +409,10 @@ void ASF::File::FilePrivate::HeaderExtensionObject::parse(ASF::File *file, unsig
ByteVector ASF::File::FilePrivate::HeaderExtensionObject::render(ASF::File *file)
{
data.clear();
for(ObjectConstIterator it = objects.begin(); it != objects.end(); ++it) {
for(List<BaseObject *>::ConstIterator it = objects.begin(); it != objects.end(); ++it) {
data.append((*it)->render(file));
}
data = ByteVector("\x11\xD2\xD3\xAB\xBA\xA9\xcf\x11\x8E\xE6\x00\xC0\x0C\x20\x53\x65\x06\x00", 18) + ByteVector::fromUInt32LE(data.size()) + data;
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);
}
@@ -418,7 +431,7 @@ void ASF::File::FilePrivate::CodecListObject::parse(ASF::File *file, unsigned in
unsigned int pos = 16;
const int count = data.toUInt32LE(pos);
const int count = data.toUInt(pos, false);
pos += 4;
for(int i = 0; i < count; ++i) {
@@ -426,22 +439,22 @@ void ASF::File::FilePrivate::CodecListObject::parse(ASF::File *file, unsigned in
if(pos >= data.size())
break;
const CodecType type = static_cast<CodecType>(data.toUInt16LE(pos));
const CodecType type = static_cast<CodecType>(data.toUShort(pos, false));
pos += 2;
int nameLength = data.toUInt16LE(pos);
int nameLength = data.toUShort(pos, false);
pos += 2;
const unsigned int namePos = pos;
pos += nameLength * 2;
const int descLength = data.toUInt16LE(pos);
const int descLength = data.toUShort(pos, false);
pos += 2;
const unsigned int descPos = pos;
pos += descLength * 2;
const int infoLength = data.toUInt16LE(pos);
const int infoLength = data.toUShort(pos, false);
pos += 2 + infoLength * 2;
if(type == CodecListObject::Audio) {
@@ -474,7 +487,7 @@ bool ASF::File::isSupported(IOStream *stream)
// public members
////////////////////////////////////////////////////////////////////////////////
ASF::File::File(FileName file, bool, AudioProperties::ReadStyle) :
ASF::File::File(FileName file, bool, Properties::ReadStyle) :
TagLib::File(file),
d(new FilePrivate())
{
@@ -482,7 +495,7 @@ ASF::File::File(FileName file, bool, AudioProperties::ReadStyle) :
read();
}
ASF::File::File(IOStream *stream, bool, AudioProperties::ReadStyle) :
ASF::File::File(IOStream *stream, bool, Properties::ReadStyle) :
TagLib::File(stream),
d(new FilePrivate())
{
@@ -497,12 +510,27 @@ ASF::File::~File()
ASF::Tag *ASF::File::tag() const
{
return d->tag.get();
return d->tag;
}
ASF::AudioProperties *ASF::File::audioProperties() const
PropertyMap ASF::File::properties() const
{
return d->properties.get();
return d->tag->properties();
}
void ASF::File::removeUnsupportedProperties(const StringList &properties)
{
d->tag->removeUnsupportedProperties(properties);
}
PropertyMap ASF::File::setProperties(const PropertyMap &properties)
{
return d->tag->setProperties(properties);
}
ASF::Properties *ASF::File::audioProperties() const
{
return d->properties;
}
bool ASF::File::save()
@@ -518,23 +546,23 @@ bool ASF::File::save()
}
if(!d->contentDescriptionObject) {
d->contentDescriptionObject.reset(new FilePrivate::ContentDescriptionObject());
d->contentDescriptionObject = new FilePrivate::ContentDescriptionObject();
d->objects.append(d->contentDescriptionObject);
}
if(!d->extendedContentDescriptionObject) {
d->extendedContentDescriptionObject.reset(new FilePrivate::ExtendedContentDescriptionObject());
d->extendedContentDescriptionObject = new FilePrivate::ExtendedContentDescriptionObject();
d->objects.append(d->extendedContentDescriptionObject);
}
if(!d->headerExtensionObject) {
d->headerExtensionObject.reset(new FilePrivate::HeaderExtensionObject());
d->headerExtensionObject = new FilePrivate::HeaderExtensionObject();
d->objects.append(d->headerExtensionObject);
}
if(!d->metadataObject) {
d->metadataObject.reset(new FilePrivate::MetadataObject());
d->metadataObject = new FilePrivate::MetadataObject();
d->headerExtensionObject->objects.append(d->metadataObject);
}
if(!d->metadataLibraryObject) {
d->metadataLibraryObject.reset(new FilePrivate::MetadataLibraryObject());
d->metadataLibraryObject = new FilePrivate::MetadataLibraryObject();
d->headerExtensionObject->objects.append(d->metadataLibraryObject);
}
@@ -573,16 +601,16 @@ bool ASF::File::save()
}
ByteVector data;
for(FilePrivate::ObjectConstIterator it = d->objects.begin(); it != d->objects.end(); ++it) {
for(List<FilePrivate::BaseObject *>::ConstIterator it = d->objects.begin(); it != d->objects.end(); ++it) {
data.append((*it)->render(this));
}
seek(16);
writeBlock(ByteVector::fromUInt64LE(data.size() + 30));
writeBlock(ByteVector::fromUInt32LE(d->objects.size()));
writeBlock(ByteVector::fromLongLong(data.size() + 30, false));
writeBlock(ByteVector::fromUInt(d->objects.size(), false));
writeBlock(ByteVector("\x01\x02", 2));
insert(data, 30, static_cast<size_t>(d->headerSize - 30));
insert(data, 30, static_cast<unsigned long>(d->headerSize - 30));
d->headerSize = data.size() + 30;
@@ -604,8 +632,8 @@ void ASF::File::read()
return;
}
d->tag.reset(new ASF::Tag());
d->properties.reset(new ASF::AudioProperties());
d->tag = new ASF::Tag();
d->properties = new ASF::Properties();
bool ok;
d->headerSize = readQWORD(this, &ok);
@@ -620,42 +648,42 @@ void ASF::File::read()
}
seek(2, Current);
SHARED_PTR<FilePrivate::FilePropertiesObject> filePropertiesObject;
SHARED_PTR<FilePrivate::StreamPropertiesObject> streamPropertiesObject;
FilePrivate::FilePropertiesObject *filePropertiesObject = 0;
FilePrivate::StreamPropertiesObject *streamPropertiesObject = 0;
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);
long size = static_cast<long>(readQWORD(this, &ok));
if(!ok) {
setValid(false);
break;
}
SHARED_PTR<FilePrivate::BaseObject> obj;
FilePrivate::BaseObject *obj;
if(guid == filePropertiesGuid) {
filePropertiesObject.reset(new FilePrivate::FilePropertiesObject());
filePropertiesObject = new FilePrivate::FilePropertiesObject();
obj = filePropertiesObject;
}
else if(guid == streamPropertiesGuid) {
streamPropertiesObject.reset(new FilePrivate::StreamPropertiesObject());
streamPropertiesObject = new FilePrivate::StreamPropertiesObject();
obj = streamPropertiesObject;
}
else if(guid == contentDescriptionGuid) {
d->contentDescriptionObject.reset(new FilePrivate::ContentDescriptionObject());
d->contentDescriptionObject = new FilePrivate::ContentDescriptionObject();
obj = d->contentDescriptionObject;
}
else if(guid == extendedContentDescriptionGuid) {
d->extendedContentDescriptionObject.reset(new FilePrivate::ExtendedContentDescriptionObject());
d->extendedContentDescriptionObject = new FilePrivate::ExtendedContentDescriptionObject();
obj = d->extendedContentDescriptionObject;
}
else if(guid == headerExtensionGuid) {
d->headerExtensionObject.reset(new FilePrivate::HeaderExtensionObject());
d->headerExtensionObject = new FilePrivate::HeaderExtensionObject();
obj = d->headerExtensionObject;
}
else if(guid == codecListGuid) {
obj.reset(new FilePrivate::CodecListObject());
obj = new FilePrivate::CodecListObject();
}
else {
if(guid == contentEncryptionGuid ||
@@ -663,7 +691,7 @@ void ASF::File::read()
guid == advancedContentEncryptionGuid) {
d->properties->setEncrypted(true);
}
obj.reset(new FilePrivate::UnknownObject(guid));
obj = new FilePrivate::UnknownObject(guid);
}
obj->parse(this, size);
d->objects.append(obj);

View File

@@ -48,21 +48,17 @@ namespace TagLib {
public:
/*!
* Contructs an ASF file from \a file. If \a readProperties is true the
* file's audio properties will also be read using \a propertiesStyle. If
* false, \a propertiesStyle is ignored.
* Constructs an ASF file from \a file.
*
* \note In the current implementation, both \a readProperties and
* \a propertiesStyle are ignored. The audio properties are always
* read.
*/
File(FileName file, bool readProperties = true,
AudioProperties::ReadStyle propertiesStyle = AudioProperties::Average);
Properties::ReadStyle propertiesStyle = Properties::Average);
/*!
* Contructs an ASF file from \a file. If \a readProperties is true the
* file's audio properties will also be read using \a propertiesStyle. If
* false, \a propertiesStyle is ignored.
* Constructs an ASF file from \a stream.
*
* \note In the current implementation, both \a readProperties and
* \a propertiesStyle are ignored. The audio properties are always
@@ -72,7 +68,7 @@ namespace TagLib {
* responsible for deleting it after the File object.
*/
File(IOStream *stream, bool readProperties = true,
AudioProperties::ReadStyle propertiesStyle = AudioProperties::Average);
Properties::ReadStyle propertiesStyle = Properties::Average);
/*!
* Destroys this instance of the File.
@@ -91,10 +87,26 @@ namespace TagLib {
*/
virtual Tag *tag() const;
/*!
* Implements the unified property interface -- export function.
*/
PropertyMap properties() const;
/*!
* Removes unsupported properties. Forwards to the actual Tag's
* removeUnsupportedProperties() function.
*/
void removeUnsupportedProperties(const StringList &properties);
/*!
* Implements the unified property interface -- import function.
*/
PropertyMap setProperties(const PropertyMap &);
/*!
* Returns the ASF audio properties for this file.
*/
virtual AudioProperties *audioProperties() const;
virtual Properties *audioProperties() const;
/*!
* Save the file.

View File

@@ -25,7 +25,7 @@
#include <taglib.h>
#include <tdebug.h>
#include <tsmartptr.h>
#include <trefcounter.h>
#include "asfattribute.h"
#include "asffile.h"
@@ -34,25 +34,14 @@
using namespace TagLib;
namespace
{
struct PictureData
{
bool valid;
ASF::Picture::Type type;
String mimeType;
String description;
ByteVector picture;
};
}
class ASF::Picture::PicturePrivate
class ASF::Picture::PicturePrivate : public RefCounter
{
public:
PicturePrivate() :
data(new PictureData()) {}
SHARED_PTR<PictureData> data;
bool valid;
Type type;
String mimeType;
String description;
ByteVector picture;
};
////////////////////////////////////////////////////////////////////////////////
@@ -62,69 +51,71 @@ public:
ASF::Picture::Picture() :
d(new PicturePrivate())
{
d->data->valid = true;
d->valid = true;
}
ASF::Picture::Picture(const Picture& other) :
d(new PicturePrivate(*other.d))
d(other.d)
{
d->ref();
}
ASF::Picture::~Picture()
{
delete d;
if(d->deref())
delete d;
}
bool ASF::Picture::isValid() const
{
return d->data->valid;
return d->valid;
}
String ASF::Picture::mimeType() const
{
return d->data->mimeType;
return d->mimeType;
}
void ASF::Picture::setMimeType(const String &value)
{
d->data->mimeType = value;
d->mimeType = value;
}
ASF::Picture::Type ASF::Picture::type() const
{
return d->data->type;
return d->type;
}
void ASF::Picture::setType(const ASF::Picture::Type& t)
{
d->data->type = t;
d->type = t;
}
String ASF::Picture::description() const
{
return d->data->description;
return d->description;
}
void ASF::Picture::setDescription(const String &desc)
{
d->data->description = desc;
d->description = desc;
}
ByteVector ASF::Picture::picture() const
{
return d->data->picture;
return d->picture;
}
void ASF::Picture::setPicture(const ByteVector &p)
{
d->data->picture = p;
d->picture = p;
}
int ASF::Picture::dataSize() const
{
return static_cast<int>(
9 + (d->data->mimeType.length() + d->data->description.length()) * 2 +
d->data->picture.size());
return
9 + (d->mimeType.length() + d->description.length()) * 2 +
d->picture.size();
}
ASF::Picture& ASF::Picture::operator=(const ASF::Picture& other)
@@ -146,47 +137,47 @@ ByteVector ASF::Picture::render() const
return ByteVector();
return
ByteVector((char)d->data->type) +
ByteVector::fromUInt32LE(d->data->picture.size()) +
renderString(d->data->mimeType) +
renderString(d->data->description) +
d->data->picture;
ByteVector(static_cast<char>(d->type)) +
ByteVector::fromUInt(d->picture.size(), false) +
renderString(d->mimeType) +
renderString(d->description) +
d->picture;
}
void ASF::Picture::parse(const ByteVector& bytes)
{
d->data->valid = false;
d->valid = false;
if(bytes.size() < 9)
return;
size_t pos = 0;
d->data->type = (Type)bytes[0]; ++pos;
const unsigned int dataLen = bytes.toUInt32LE(pos); pos+=4;
int pos = 0;
d->type = static_cast<Type>(bytes[0]); ++pos;
const unsigned int dataLen = bytes.toUInt(pos, false); pos+=4;
const ByteVector nullStringTerminator(2, 0);
size_t endPos = bytes.find(nullStringTerminator, pos, 2);
if(endPos == ByteVector::npos())
int endPos = bytes.find(nullStringTerminator, pos, 2);
if(endPos < 0)
return;
d->data->mimeType = String(bytes.mid(pos, endPos - pos), String::UTF16LE);
d->mimeType = String(bytes.mid(pos, endPos - pos), String::UTF16LE);
pos = endPos+2;
endPos = bytes.find(nullStringTerminator, pos, 2);
if(endPos == ByteVector::npos())
if(endPos < 0)
return;
d->data->description = String(bytes.mid(pos, endPos - pos), String::UTF16LE);
d->description = String(bytes.mid(pos, endPos - pos), String::UTF16LE);
pos = endPos+2;
if(dataLen + pos != bytes.size())
return;
d->data->picture = bytes.mid(pos, dataLen);
d->data->valid = true;
d->picture = bytes.mid(pos, dataLen);
d->valid = true;
return;
}
ASF::Picture ASF::Picture::fromInvalid()
{
Picture ret;
ret.d->data->valid = false;
ret.d->valid = false;
return ret;
}

View File

@@ -35,7 +35,6 @@ namespace TagLib
{
namespace ASF
{
class Attribute;
//! An ASF attached picture interface implementation
@@ -47,8 +46,7 @@ namespace TagLib
* \see Attribute::toPicture()
* \see Attribute::Attribute(const Picture& picture)
*/
class TAGLIB_EXPORT Picture
{
class TAGLIB_EXPORT Picture {
public:
/*!
@@ -105,7 +103,7 @@ namespace TagLib
Picture();
/*!
* Constructs an picture as a copy of \a other.
* Construct an picture as a copy of \a other.
*/
Picture(const Picture& other);
@@ -208,15 +206,16 @@ namespace TagLib
*/
int dataSize() const;
private:
friend class Attribute;
void parse(const ByteVector &);
#ifndef DO_NOT_DOCUMENT
/* THIS IS PRIVATE, DON'T TOUCH IT! */
void parse(const ByteVector& );
static Picture fromInvalid();
#endif
class PicturePrivate;
PicturePrivate *d;
};
private:
class PicturePrivate;
PicturePrivate *d;
};
}
}

View File

@@ -29,7 +29,7 @@
using namespace TagLib;
class ASF::AudioProperties::PropertiesPrivate
class ASF::Properties::PropertiesPrivate
{
public:
PropertiesPrivate() :
@@ -38,7 +38,7 @@ public:
sampleRate(0),
channels(0),
bitsPerSample(0),
codec(ASF::AudioProperties::Unknown),
codec(ASF::Properties::Unknown),
encrypted(false) {}
int length;
@@ -46,7 +46,7 @@ public:
int sampleRate;
int channels;
int bitsPerSample;
ASF::AudioProperties::Codec codec;
ASF::Properties::Codec codec;
String codecName;
String codecDescription;
bool encrypted;
@@ -56,68 +56,68 @@ public:
// public members
////////////////////////////////////////////////////////////////////////////////
ASF::AudioProperties::AudioProperties() :
TagLib::AudioProperties(),
ASF::Properties::Properties() :
AudioProperties(AudioProperties::Average),
d(new PropertiesPrivate())
{
}
ASF::AudioProperties::~AudioProperties()
ASF::Properties::~Properties()
{
delete d;
}
int ASF::AudioProperties::length() const
int ASF::Properties::length() const
{
return lengthInSeconds();
}
int ASF::AudioProperties::lengthInSeconds() const
int ASF::Properties::lengthInSeconds() const
{
return d->length / 1000;
}
int ASF::AudioProperties::lengthInMilliseconds() const
int ASF::Properties::lengthInMilliseconds() const
{
return d->length;
}
int ASF::AudioProperties::bitrate() const
int ASF::Properties::bitrate() const
{
return d->bitrate;
}
int ASF::AudioProperties::sampleRate() const
int ASF::Properties::sampleRate() const
{
return d->sampleRate;
}
int ASF::AudioProperties::channels() const
int ASF::Properties::channels() const
{
return d->channels;
}
int ASF::AudioProperties::bitsPerSample() const
int ASF::Properties::bitsPerSample() const
{
return d->bitsPerSample;
}
ASF::AudioProperties::Codec ASF::AudioProperties::codec() const
ASF::Properties::Codec ASF::Properties::codec() const
{
return d->codec;
}
String ASF::AudioProperties::codecName() const
String ASF::Properties::codecName() const
{
return d->codecName;
}
String ASF::AudioProperties::codecDescription() const
String ASF::Properties::codecDescription() const
{
return d->codecDescription;
}
bool ASF::AudioProperties::isEncrypted() const
bool ASF::Properties::isEncrypted() const
{
return d->encrypted;
}
@@ -126,32 +126,37 @@ bool ASF::AudioProperties::isEncrypted() const
// private members
////////////////////////////////////////////////////////////////////////////////
void ASF::AudioProperties::setLengthInMilliseconds(int value)
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;
}
void ASF::AudioProperties::setBitrate(int value)
void ASF::Properties::setBitrate(int value)
{
d->bitrate = value;
}
void ASF::AudioProperties::setSampleRate(int value)
void ASF::Properties::setSampleRate(int value)
{
d->sampleRate = value;
}
void ASF::AudioProperties::setChannels(int value)
void ASF::Properties::setChannels(int value)
{
d->channels = value;
}
void ASF::AudioProperties::setBitsPerSample(int value)
void ASF::Properties::setBitsPerSample(int value)
{
d->bitsPerSample = value;
}
void ASF::AudioProperties::setCodec(int value)
void ASF::Properties::setCodec(int value)
{
switch(value)
{
@@ -173,17 +178,17 @@ void ASF::AudioProperties::setCodec(int value)
}
}
void ASF::AudioProperties::setCodecName(const String &value)
void ASF::Properties::setCodecName(const String &value)
{
d->codecName = value;
}
void ASF::AudioProperties::setCodecDescription(const String &value)
void ASF::Properties::setCodecDescription(const String &value)
{
d->codecDescription = value;
}
void ASF::AudioProperties::setEncrypted(bool value)
void ASF::Properties::setEncrypted(bool value)
{
d->encrypted = value;
}

View File

@@ -34,14 +34,11 @@ namespace TagLib {
namespace ASF {
class File;
//! An implementation of ASF audio properties
class TAGLIB_EXPORT AudioProperties : public TagLib::AudioProperties
class TAGLIB_EXPORT Properties : public AudioProperties
{
friend class File;
public:
/*!
* Audio codec types can be used in ASF file.
*/
@@ -74,14 +71,14 @@ namespace TagLib {
};
/*!
* Creates an instance of ASF::AudioProperties.
* Creates an instance of ASF::Properties.
*/
AudioProperties();
Properties();
/*!
* Destroys this ASF::AudioProperties instance.
* Destroys this ASF::Properties instance.
*/
virtual ~AudioProperties();
virtual ~Properties();
/*!
* Returns the length of the file in seconds. The length is rounded down to
@@ -91,7 +88,7 @@ namespace TagLib {
*
* \deprecated
*/
virtual int length() const;
TAGLIB_DEPRECATED virtual int length() const;
/*!
* Returns the length of the file in seconds. The length is rounded down to
@@ -99,14 +96,16 @@ namespace TagLib {
*
* \see lengthInMilliseconds()
*/
virtual int lengthInSeconds() const;
// BIC: make virtual
int lengthInSeconds() const;
/*!
* Returns the length of the file in milliseconds.
*
* \see lengthInSeconds()
*/
virtual int lengthInMilliseconds() const;
// BIC: make virtual
int lengthInMilliseconds() const;
/*!
* Returns the average bit rate of the file in kb/s.
@@ -160,7 +159,10 @@ namespace TagLib {
*/
bool isEncrypted() const;
private:
#ifndef DO_NOT_DOCUMENT
// deprecated
void setLength(int value);
void setLengthInMilliseconds(int value);
void setBitrate(int value);
void setSampleRate(int value);
@@ -170,7 +172,9 @@ namespace TagLib {
void setCodecName(const String &value);
void setCodecDescription(const String &value);
void setEncrypted(bool value);
#endif
private:
class PropertiesPrivate;
PropertiesPrivate *d;
};

View File

@@ -23,7 +23,6 @@
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#include <tpicturemap.h>
#include <tpropertymap.h>
#include "asftag.h"
@@ -41,7 +40,6 @@ public:
};
ASF::Tag::Tag() :
TagLib::Tag(),
d(new TagPrivate())
{
}
@@ -96,8 +94,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();
@@ -111,91 +108,6 @@ String ASF::Tag::genre() const
return String();
}
PictureMap ASF::Tag::pictures() const
{
PictureMap map;
if(d->attributeListMap.contains("WM/Picture")) {
AttributeList list = d->attributeListMap["WM/Picture"];
for(AttributeList::ConstIterator it = list.begin();
it != list.end();
++it) {
ASF::Picture asfPicture = (*it).toPicture();
TagLib::Picture::Type type;
switch(asfPicture.type()) {
case ASF::Picture::FileIcon:
type = TagLib::Picture::FileIcon;
break;
case ASF::Picture::OtherFileIcon:
type = TagLib::Picture::OtherFileIcon;
break;
case ASF::Picture::FrontCover:
type = TagLib::Picture::FrontCover;
break;
case ASF::Picture::BackCover:
type = TagLib::Picture::BackCover;
break;
case ASF::Picture::LeafletPage:
type = TagLib::Picture::LeafletPage;
break;
case ASF::Picture::Media:
type = TagLib::Picture::Media;
break;
case ASF::Picture::LeadArtist:
type = TagLib::Picture::LeadArtist;
break;
case ASF::Picture::Artist:
type = TagLib::Picture::Artist;
break;
case ASF::Picture::Conductor:
type = TagLib::Picture::Conductor;
break;
case ASF::Picture::Band:
type = TagLib::Picture::Band;
break;
case ASF::Picture::Composer:
type = TagLib::Picture::Composer;
break;
case ASF::Picture::Lyricist:
type = TagLib::Picture::Lyricist;
break;
case ASF::Picture::RecordingLocation:
type = TagLib::Picture::RecordingLocation;
break;
case ASF::Picture::DuringRecording:
type = TagLib::Picture::DuringRecording;
break;
case ASF::Picture::DuringPerformance:
type = TagLib::Picture::DuringPerformance;
break;
case ASF::Picture::MovieScreenCapture:
type = TagLib::Picture::MovieScreenCapture;
break;
case ASF::Picture::ColouredFish:
type = TagLib::Picture::ColouredFish;
break;
case ASF::Picture::Illustration:
type = TagLib::Picture::Illustration;
break;
case ASF::Picture::BandLogo:
type = TagLib::Picture::BandLogo;
break;
case ASF::Picture::PublisherLogo:
type = TagLib::Picture::PublisherLogo;
break;
default:
type = TagLib::Picture::Other;
break;
}
TagLib::Picture picture(asfPicture.picture(),
type,
asfPicture.mimeType(),
asfPicture.description());
map.insert(picture);
}
}
return PictureMap(map);
}
void ASF::Tag::setTitle(const String &value)
{
d->title = value;
@@ -241,94 +153,6 @@ void ASF::Tag::setTrack(unsigned int value)
setAttribute("WM/TrackNumber", String::number(value));
}
void ASF::Tag::setPictures(const PictureMap &l)
{
removeItem("WM/Picture");
for(PictureMap::ConstIterator pictureMapIt = l.begin();
pictureMapIt != l.end();
++pictureMapIt)
{
PictureList list = pictureMapIt->second;
for( PictureList::ConstIterator pictureListIt = list.begin();
pictureListIt != list.end();
++pictureListIt)
{
const TagLib::Picture picture = (*pictureListIt);
ASF::Picture asfPicture;
asfPicture.setPicture(picture.data());
asfPicture.setMimeType(picture.mime());
asfPicture.setDescription(picture.description());
switch (picture.type()) {
case TagLib::Picture::Other:
asfPicture.setType(ASF::Picture::Other);
break;
case TagLib::Picture::FileIcon:
asfPicture.setType(ASF::Picture::FileIcon);
break;
case TagLib::Picture::OtherFileIcon:
asfPicture.setType(ASF::Picture::OtherFileIcon);
break;
case TagLib::Picture::FrontCover:
asfPicture.setType(ASF::Picture::FrontCover);
break;
case TagLib::Picture::BackCover:
asfPicture.setType(ASF::Picture::BackCover);
break;
case TagLib::Picture::LeafletPage:
asfPicture.setType(ASF::Picture::LeafletPage);
break;
case TagLib::Picture::Media:
asfPicture.setType(ASF::Picture::Media);
break;
case TagLib::Picture::LeadArtist:
asfPicture.setType(ASF::Picture::LeadArtist);
break;
case TagLib::Picture::Artist:
asfPicture.setType(ASF::Picture::Artist);
break;
case TagLib::Picture::Conductor:
asfPicture.setType(ASF::Picture::Conductor);
break;
case TagLib::Picture::Band:
asfPicture.setType(ASF::Picture::Band);
break;
case TagLib::Picture::Composer:
asfPicture.setType(ASF::Picture::Composer);
break;
case TagLib::Picture::Lyricist:
asfPicture.setType(ASF::Picture::Lyricist);
break;
case TagLib::Picture::RecordingLocation:
asfPicture.setType(ASF::Picture::RecordingLocation);
break;
case TagLib::Picture::DuringRecording:
asfPicture.setType(ASF::Picture::DuringRecording);
break;
case TagLib::Picture::DuringPerformance:
asfPicture.setType(ASF::Picture::DuringPerformance);
break;
case TagLib::Picture::MovieScreenCapture:
asfPicture.setType(ASF::Picture::MovieScreenCapture);
break;
case TagLib::Picture::ColouredFish:
asfPicture.setType(ASF::Picture::ColouredFish);
break;
case TagLib::Picture::Illustration:
asfPicture.setType(ASF::Picture::Illustration);
break;
case TagLib::Picture::BandLogo:
asfPicture.setType(ASF::Picture::BandLogo);
break;
case TagLib::Picture::PublisherLogo:
asfPicture.setType(ASF::Picture::PublisherLogo);
break;
}
addAttribute("WM/Picture", Attribute(asfPicture));
}
}
}
ASF::AttributeListMap& ASF::Tag::attributeListMap()
{
return d->attributeListMap;
@@ -386,59 +210,65 @@ bool ASF::Tag::isEmpty() const
namespace
{
const char *keyTranslation[][2] = {
{ "WM/AlbumTitle", "ALBUM" },
{ "WM/AlbumArtist", "ALBUMARTIST" },
{ "WM/Composer", "COMPOSER" },
{ "WM/Writer", "WRITER" },
{ "WM/Conductor", "CONDUCTOR" },
{ "WM/ModifiedBy", "REMIXER" },
{ "WM/Year", "DATE" },
{ "WM/OriginalReleaseYear", "ORIGINALDATE" },
{ "WM/Producer", "PRODUCER" },
{ "WM/ContentGroupDescription", "GROUPING" },
{ "WM/SubTitle", "SUBTITLE" },
{ "WM/SetSubTitle", "DISCSUBTITLE" },
{ "WM/TrackNumber", "TRACKNUMBER" },
{ "WM/PartOfSet", "DISCNUMBER" },
{ "WM/Genre", "GENRE" },
{ "WM/BeatsPerMinute", "BPM" },
{ "WM/Mood", "MOOD" },
{ "WM/ISRC", "ISRC" },
{ "WM/Lyrics", "LYRICS" },
{ "WM/Media", "MEDIA" },
{ "WM/Publisher", "LABEL" },
{ "WM/CatalogNo", "CATALOGNUMBER" },
{ "WM/Barcode", "BARCODE" },
{ "WM/EncodedBy", "ENCODEDBY" },
{ "WM/AlbumSortOrder", "ALBUMSORT" },
{ "WM/AlbumArtistSortOrder", "ALBUMARTISTSORT" },
{ "WM/ArtistSortOrder", "ARTISTSORT" },
{ "WM/TitleSortOrder", "TITLESORT" },
{ "WM/Script", "SCRIPT" },
{ "WM/Language", "LANGUAGE" },
{ "MusicBrainz/Track Id", "MUSICBRAINZ_TRACKID" },
{ "MusicBrainz/Artist Id", "MUSICBRAINZ_ARTISTID" },
{ "MusicBrainz/Album Id", "MUSICBRAINZ_ALBUMID" },
{ "MusicBrainz/Album Artist Id", "MUSICBRAINZ_ALBUMARTISTID" },
{ "MusicBrainz/Release Group Id", "MUSICBRAINZ_RELEASEGROUPID" },
{ "MusicBrainz/Work Id", "MUSICBRAINZ_WORKID" },
{ "MusicIP/PUID", "MUSICIP_PUID" },
{ "Acoustid/Id", "ACOUSTID_ID" },
{ "Acoustid/Fingerprint", "ACOUSTID_FINGERPRINT" },
const std::pair<const char *, const char *> keyTranslation[] = {
std::make_pair("WM/AlbumTitle", "ALBUM"),
std::make_pair("WM/AlbumArtist", "ALBUMARTIST"),
std::make_pair("WM/Composer", "COMPOSER"),
std::make_pair("WM/Writer", "LYRICIST"),
std::make_pair("WM/Conductor", "CONDUCTOR"),
std::make_pair("WM/ModifiedBy", "REMIXER"),
std::make_pair("WM/Year", "DATE"),
std::make_pair("WM/OriginalReleaseYear", "ORIGINALDATE"),
std::make_pair("WM/Producer", "PRODUCER"),
std::make_pair("WM/ContentGroupDescription", "WORK"),
std::make_pair("WM/SubTitle", "SUBTITLE"),
std::make_pair("WM/SetSubTitle", "DISCSUBTITLE"),
std::make_pair("WM/TrackNumber", "TRACKNUMBER"),
std::make_pair("WM/PartOfSet", "DISCNUMBER"),
std::make_pair("WM/Genre", "GENRE"),
std::make_pair("WM/BeatsPerMinute", "BPM"),
std::make_pair("WM/Mood", "MOOD"),
std::make_pair("WM/ISRC", "ISRC"),
std::make_pair("WM/Lyrics", "LYRICS"),
std::make_pair("WM/Media", "MEDIA"),
std::make_pair("WM/Publisher", "LABEL"),
std::make_pair("WM/CatalogNo", "CATALOGNUMBER"),
std::make_pair("WM/Barcode", "BARCODE"),
std::make_pair("WM/EncodedBy", "ENCODEDBY"),
std::make_pair("WM/AlbumSortOrder", "ALBUMSORT"),
std::make_pair("WM/AlbumArtistSortOrder", "ALBUMARTISTSORT"),
std::make_pair("WM/ArtistSortOrder", "ARTISTSORT"),
std::make_pair("WM/TitleSortOrder", "TITLESORT"),
std::make_pair("WM/Script", "SCRIPT"),
std::make_pair("WM/Language", "LANGUAGE"),
std::make_pair("WM/ARTISTS", "ARTISTS"),
std::make_pair("ASIN", "ASIN"),
std::make_pair("MusicBrainz/Track Id", "MUSICBRAINZ_TRACKID"),
std::make_pair("MusicBrainz/Artist Id", "MUSICBRAINZ_ARTISTID"),
std::make_pair("MusicBrainz/Album Id", "MUSICBRAINZ_ALBUMID"),
std::make_pair("MusicBrainz/Album Artist Id", "MUSICBRAINZ_ALBUMARTISTID"),
std::make_pair("MusicBrainz/Album Release Country", "RELEASECOUNTRY"),
std::make_pair("MusicBrainz/Album Status", "RELEASESTATUS"),
std::make_pair("MusicBrainz/Album Type", "RELEASETYPE"),
std::make_pair("MusicBrainz/Release Group Id", "MUSICBRAINZ_RELEASEGROUPID"),
std::make_pair("MusicBrainz/Release Track Id", "MUSICBRAINZ_RELEASETRACKID"),
std::make_pair("MusicBrainz/Work Id", "MUSICBRAINZ_WORKID"),
std::make_pair("MusicIP/PUID", "MUSICIP_PUID"),
std::make_pair("Acoustid/Id", "ACOUSTID_ID"),
std::make_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];
if(key == keyTranslation[i].first)
return keyTranslation[i].second;
}
return String();
}
}
} // namespace
PropertyMap ASF::Tag::properties() const
{
@@ -492,9 +322,8 @@ 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(size_t i = 0; i < keyTranslationSize; i++) {
reverseKeyMap[keyTranslation[i].second] = keyTranslation[i].first;
}
}

View File

@@ -98,8 +98,6 @@ namespace TagLib {
*/
virtual unsigned int track() const;
virtual PictureMap pictures() const;
/*!
* Sets the title to \a s.
*/
@@ -146,8 +144,6 @@ namespace TagLib {
*/
virtual void setTrack(unsigned int i);
virtual void setPictures(const PictureMap &l);
/*!
* Returns true if the tag does not contain any data. This should be
* reimplemented in subclasses that provide more than the basic tagging
@@ -164,6 +160,7 @@ 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;
/*!

View File

@@ -45,7 +45,7 @@ namespace TagLib
return 0;
}
if(ok) *ok = true;
return v.toUInt16LE(0);
return v.toUShort(false);
}
inline unsigned int readDWORD(File *file, bool *ok = 0)
@@ -56,7 +56,7 @@ namespace TagLib
return 0;
}
if(ok) *ok = true;
return v.toUInt32LE(0);
return v.toUInt(false);
}
inline long long readQWORD(File *file, bool *ok = 0)
@@ -67,13 +67,13 @@ namespace TagLib
return 0;
}
if(ok) *ok = true;
return v.toInt64LE(0);
return v.toLongLong(false);
}
inline String readString(File *file, int length)
{
ByteVector data = file->readBlock(length);
size_t size = data.size();
unsigned int size = data.size();
while (size >= 2) {
if(data[size - 1] != '\0' || data[size - 2] != '\0') {
break;
@@ -88,9 +88,9 @@ namespace TagLib
inline ByteVector renderString(const String &str, bool includeLength = false)
{
ByteVector data = str.data(String::UTF16LE) + ByteVector::fromUInt16LE(0);
ByteVector data = str.data(String::UTF16LE) + ByteVector::fromShort(0, false);
if(includeLength) {
data = ByteVector::fromUInt16LE(data.size()) + data;
data = ByteVector::fromShort(data.size(), false) + data;
}
return data;
}

View File

@@ -23,34 +23,88 @@
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#include <tstringlist.h>
#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(); \
if(dynamic_cast<const ASF::Properties*>(this)) \
return dynamic_cast<const ASF::Properties*>(this)->function_name(); \
if(dynamic_cast<const FLAC::Properties*>(this)) \
return dynamic_cast<const FLAC::Properties*>(this)->function_name(); \
if(dynamic_cast<const MP4::Properties*>(this)) \
return dynamic_cast<const MP4::Properties*>(this)->function_name(); \
if(dynamic_cast<const MPC::Properties*>(this)) \
return dynamic_cast<const MPC::Properties*>(this)->function_name(); \
if(dynamic_cast<const MPEG::Properties*>(this)) \
return dynamic_cast<const MPEG::Properties*>(this)->function_name(); \
if(dynamic_cast<const Ogg::Opus::Properties*>(this)) \
return dynamic_cast<const Ogg::Opus::Properties*>(this)->function_name(); \
if(dynamic_cast<const Ogg::Speex::Properties*>(this)) \
return dynamic_cast<const Ogg::Speex::Properties*>(this)->function_name(); \
if(dynamic_cast<const TrueAudio::Properties*>(this)) \
return dynamic_cast<const TrueAudio::Properties*>(this)->function_name(); \
if(dynamic_cast<const RIFF::AIFF::Properties*>(this)) \
return dynamic_cast<const RIFF::AIFF::Properties*>(this)->function_name(); \
if(dynamic_cast<const RIFF::WAV::Properties*>(this)) \
return dynamic_cast<const RIFF::WAV::Properties*>(this)->function_name(); \
if(dynamic_cast<const Vorbis::Properties*>(this)) \
return dynamic_cast<const Vorbis::Properties*>(this)->function_name(); \
if(dynamic_cast<const WavPack::Properties*>(this)) \
return dynamic_cast<const WavPack::Properties*>(this)->function_name(); \
return (default_value);
class AudioProperties::AudioPropertiesPrivate
{
};
////////////////////////////////////////////////////////////////////////////////
// public methods
////////////////////////////////////////////////////////////////////////////////
AudioProperties::~AudioProperties()
{
}
String AudioProperties::toString() const
int AudioProperties::lengthInSeconds() const
{
StringList desc;
desc.append("Audio");
desc.append(String::number(length()) + " seconds");
desc.append(String::number(bitrate()) + " kbps");
return desc.toString(", ");
VIRTUAL_FUNCTION_WORKAROUND(lengthInSeconds, 0)
}
int AudioProperties::lengthInMilliseconds() const
{
VIRTUAL_FUNCTION_WORKAROUND(lengthInMilliseconds, 0)
}
////////////////////////////////////////////////////////////////////////////////
// protected methods
////////////////////////////////////////////////////////////////////////////////
AudioProperties::AudioProperties() :
AudioProperties::AudioProperties(ReadStyle) :
d(0)
{
}

View File

@@ -27,7 +27,6 @@
#define TAGLIB_AUDIOPROPERTIES_H
#include "taglib_export.h"
#include "tstring.h"
namespace TagLib {
@@ -76,14 +75,16 @@ namespace TagLib {
*
* \see lengthInMilliseconds()
*/
virtual int lengthInSeconds() const = 0;
// BIC: make virtual
int lengthInSeconds() const;
/*!
* Returns the length of the file in milliseconds.
*
* \see lengthInSeconds()
*/
virtual int lengthInMilliseconds() const = 0;
// BIC: make virtual
int lengthInMilliseconds() const;
/*!
* Returns the most appropriate bit rate for the file in kb/s. For constant
@@ -102,27 +103,23 @@ namespace TagLib {
*/
virtual int channels() const = 0;
/*!
* Returns description of the audio file.
*/
virtual String toString() const;
protected:
/*!
* Constructs an audio properties instance. This is protected as this class
* Construct an audio properties instance. This is protected as this class
* should not be instantiated directly, but should be instantiated via its
* subclasses and can be fetched from the FileRef or File APIs.
*
* \see ReadStyle
*/
AudioProperties();
AudioProperties(ReadStyle style);
private:
// Noncopyable. Derived classes as well.
AudioProperties(const AudioProperties &);
AudioProperties &operator=(const AudioProperties &);
class PropertiesPrivate;
PropertiesPrivate *d;
class AudioPropertiesPrivate;
AudioPropertiesPrivate *d;
};
}

View File

@@ -1,166 +0,0 @@
/***************************************************************************
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 "tstringlist.h"
#include "tpropertymap.h"
#include "tpicturemap.h"
using namespace TagLib;
using namespace DSDIFF::DIIN;
class DSDIFF::DIIN::Tag::TagPrivate
{
public:
TagPrivate()
{
}
String title;
String artist;
};
DSDIFF::DIIN::Tag::Tag() : TagLib::Tag()
{
d = new TagPrivate;
}
DSDIFF::DIIN::Tag::~Tag()
{
delete d;
}
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;
}
PictureMap DSDIFF::DIIN::Tag::pictures() const
{
return PictureMap();
}
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 &)
{
}
void DSDIFF::DIIN::Tag::setComment(const String &)
{
}
void DSDIFF::DIIN::Tag::setGenre(const String &)
{
}
void DSDIFF::DIIN::Tag::setYear(unsigned int)
{
}
void DSDIFF::DIIN::Tag::setTrack(unsigned int)
{
}
void DSDIFF::DIIN::Tag::setPictures( const PictureMap& l )
{
}
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 properties(origProps);
properties.removeEmpty();
StringList oneValueSet;
if(properties.contains("TITLE")) {
d->title = properties["TITLE"].front();
oneValueSet.append("TITLE");
} else
d->title = String();
if(properties.contains("ARTIST")) {
d->artist = properties["ARTIST"].front();
oneValueSet.append("ARTIST");
} else
d->artist = String();
// 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(StringList::Iterator it = oneValueSet.begin(); it != oneValueSet.end(); ++it) {
if(properties[*it].size() == 1)
properties.erase(*it);
else
properties[*it].erase(properties[*it].begin());
}
return properties;
}

View File

@@ -1,160 +0,0 @@
/***************************************************************************
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();
virtual ~Tag();
/*!
* Returns the track name; if no track name is present in the tag
* String() will be returned.
*/
virtual String title() const;
/*!
* Returns the artist name; if no artist name is present in the tag
* String() will be returned.
*/
virtual String artist() const;
/*!
* Not supported. Therefore always returns String().
*/
virtual String album() const;
/*!
* Not supported. Therefore always returns String().
*/
virtual String comment() const;
/*!
* Not supported. Therefore always returns String().
*/
virtual String genre() const;
/*!
* Not supported. Therefore always returns 0.
*/
virtual unsigned int year() const;
/*!
* Not supported. Therefore always returns 0.
*/
virtual unsigned int track() const;
/*!
* Not supported. Therefore always returns an empty list.
*/
virtual PictureMap pictures() const;
/*!
* Sets the title to \a title. If \a title is String() then this
* value will be cleared.
*/
virtual void setTitle(const String &title);
/*!
* Sets the artist to \a artist. If \a artist is String() then this
* value will be cleared.
*/
virtual void setArtist(const String &artist);
/*!
* Not supported and therefore ignored.
*/
virtual void setAlbum(const String &album);
/*!
* Not supported and therefore ignored.
*/
virtual void setComment(const String &comment);
/*!
* Not supported and therefore ignored.
*/
virtual void setGenre(const String &genre);
/*!
* Not supported and therefore ignored.
*/
virtual void setYear(unsigned int year);
/*!
* Not supported and therefore ignored.
*/
virtual void setTrack(unsigned int track);
/*!
* Not supported and therefore ignored.
*/
virtual void setPictures( const PictureMap& l );
/*!
* Implements the unified property interface -- export function.
* Since the DIIN tag is very limited, the exported map is as well.
*/
PropertyMap properties() const;
/*!
* 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 &);
private:
Tag(const Tag &);
Tag &operator=(const Tag &);
class TagPrivate;
TagPrivate *d;
};
}
}
}
#endif

View File

@@ -1,844 +0,0 @@
/***************************************************************************
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 <tbytevector.h>
#include <tdebug.h>
#include <id3v2tag.h>
#include <tstringlist.h>
#include <tpropertymap.h>
#include <tagutils.h>
#include <tsmartptr.h>
#include "tagunion.h"
#include "dsdifffile.h"
using namespace TagLib;
struct Chunk64
{
ByteVector name;
unsigned long long offset;
unsigned long long size;
char padding;
};
namespace
{
enum {
ID3v2Index = 0,
DIINIndex = 1
};
enum {
PROPChunk = 0,
DIINChunk = 1
};
}
class DSDIFF::File::FilePrivate
{
public:
FilePrivate() :
endianness(BigEndian),
size(0),
isID3InPropChunk(false),
duplicateID3V2chunkIndex(-1),
id3v2TagChunkID("ID3 "),
hasID3v2(false),
hasDiin(false)
{
childChunkIndex[ID3v2Index] = -1;
childChunkIndex[DIINIndex] = -1;
}
Endianness endianness;
ByteVector type;
unsigned long long size;
ByteVector format;
std::vector<Chunk64> chunks;
std::vector<Chunk64> childChunks[2];
int childChunkIndex[2];
bool isID3InPropChunk; // Two possibilities can be found: ID3V2 chunk inside PROP chunk or at root level
int duplicateID3V2chunkIndex; // 2 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.
SCOPED_PTR<AudioProperties> properties;
DoubleTagUnion tag;
ByteVector id3v2TagChunkID;
bool hasID3v2;
bool hasDiin;
};
////////////////////////////////////////////////////////////////////////////////
// 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,
AudioProperties::ReadStyle propertiesStyle) : TagLib::File(file)
{
d = new FilePrivate;
d->endianness = BigEndian;
if(isOpen())
read(readProperties, propertiesStyle);
}
DSDIFF::File::File(IOStream *stream, bool readProperties,
AudioProperties::ReadStyle propertiesStyle) : TagLib::File(stream)
{
d = new FilePrivate;
d->endianness = BigEndian;
if(isOpen())
read(readProperties, propertiesStyle);
}
DSDIFF::File::~File()
{
delete d;
}
TagLib::Tag *DSDIFF::File::tag() const
{
return &d->tag;
}
ID3v2::Tag *DSDIFF::File::ID3v2Tag() const
{
return d->tag.access<ID3v2::Tag>(ID3v2Index, false);
}
bool DSDIFF::File::hasID3v2Tag() const
{
return d->hasID3v2;
}
DSDIFF::DIIN::Tag *DSDIFF::File::DIINTag() const
{
return d->tag.access<DSDIFF::DIIN::Tag>(DIINIndex, false);
}
bool DSDIFF::File::hasDIINTag() const
{
return d->hasDiin;
}
PropertyMap DSDIFF::File::properties() const
{
if(d->hasID3v2)
return d->tag.access<ID3v2::Tag>(ID3v2Index, false)->properties();
return PropertyMap();
}
void DSDIFF::File::removeUnsupportedProperties(const StringList &unsupported)
{
if(d->hasID3v2)
d->tag.access<ID3v2::Tag>(ID3v2Index, false)->removeUnsupportedProperties(unsupported);
if(d->hasDiin)
d->tag.access<DSDIFF::DIIN::Tag>(DIINIndex, false)->removeUnsupportedProperties(unsupported);
}
PropertyMap DSDIFF::File::setProperties(const PropertyMap &properties)
{
return d->tag.access<ID3v2::Tag>(ID3v2Index, true)->setProperties(properties);
}
DSDIFF::AudioProperties *DSDIFF::File::audioProperties() const
{
return d->properties.get();
}
bool DSDIFF::File::save()
{
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;
}
// First: save ID3V2 chunk
ID3v2::Tag *id3v2Tag = d->tag.access<ID3v2::Tag>(ID3v2Index, false);
if(d->isID3InPropChunk) {
if(id3v2Tag != NULL && !id3v2Tag->isEmpty()) {
setChildChunkData(d->id3v2TagChunkID, id3v2Tag->render(), PROPChunk);
d->hasID3v2 = true;
}
else {
// Empty tag: remove it
setChildChunkData(d->id3v2TagChunkID, ByteVector(), PROPChunk);
d->hasID3v2 = false;
}
}
else {
if(id3v2Tag != NULL && !id3v2Tag->isEmpty()) {
setRootChunkData(d->id3v2TagChunkID, id3v2Tag->render());
d->hasID3v2 = true;
}
else {
// Empty tag: remove it
setRootChunkData(d->id3v2TagChunkID, ByteVector());
d->hasID3v2 = false;
}
}
// Second: save the DIIN chunk
if(d->hasDiin) {
DSDIFF::DIIN::Tag *diinTag = d->tag.access<DSDIFF::DIIN::Tag>(DIINIndex, false);
if(!diinTag->title().isEmpty()) {
ByteVector diinTitle;
if(d->endianness == BigEndian)
diinTitle.append(ByteVector::fromUInt32BE(diinTag->title().size()));
else
diinTitle.append(ByteVector::fromUInt32LE(diinTag->title().size()));
diinTitle.append(ByteVector::fromCString(diinTag->title().toCString()));
setChildChunkData("DITI", diinTitle, DIINChunk);
}
else
setChildChunkData("DITI", ByteVector(), DIINChunk);
if(!diinTag->artist().isEmpty()) {
ByteVector diinArtist;
if(d->endianness == BigEndian)
diinArtist.append(ByteVector::fromUInt32BE(diinTag->artist().size()));
else
diinArtist.append(ByteVector::fromUInt32LE(diinTag->artist().size()));
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;
}
////////////////////////////////////////////////////////////////////////////////
// private members
////////////////////////////////////////////////////////////////////////////////
void DSDIFF::File::setRootChunkData(unsigned int i, const ByteVector &data)
{
if(data.isEmpty()) {
// Null data: remove chunk
// Update global size
unsigned long long removedChunkTotalSize = d->chunks[i].size + d->chunks[i].padding + 12;
d->size -= removedChunkTotalSize;
if(d->endianness == BigEndian)
insert(ByteVector::fromUInt64BE(d->size), 4, 8);
else
insert(ByteVector::fromUInt64LE(d->size), 4, 8);
removeBlock(d->chunks[i].offset - 12, removedChunkTotalSize);
// Update the internal offsets
for(unsigned long r = i + 1; r < d->chunks.size(); r++)
d->chunks[r].offset = d->chunks[r - 1].offset + 12
+ d->chunks[r - 1].size + d->chunks[r - 1].padding;
d->chunks.erase(d->chunks.begin() + i);
}
else {
// 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);
if(d->endianness == BigEndian)
insert(ByteVector::fromUInt64BE(d->size), 4, 8);
else
insert(ByteVector::fromUInt64LE(d->size), 4, 8);
// Now update the specific chunk
writeChunk(d->chunks[i].name,
data,
d->chunks[i].offset - 12,
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.size() == 0) {
debug("DSDIFF::File::setPropChunkData - No valid chunks found.");
return;
}
for(unsigned int i = 0; i < d->chunks.size(); i++) {
if(d->chunks[i].name == name) {
setRootChunkData(i, data);
return;
}
}
// Couldn't find an existing chunk, so let's create a new one.
unsigned int i = d->chunks.size() - 1;
unsigned 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;
if(d->endianness == BigEndian)
insert(ByteVector::fromUInt64BE(d->size), 4, 8);
else
insert(ByteVector::fromUInt64LE(d->size), 4, 8);
// Now add the chunk to the file
writeChunk(name,
data,
offset,
std::max<unsigned long long>(0, length() - offset),
(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::setChildChunkData(unsigned int i,
const ByteVector &data,
unsigned int childChunkNum)
{
std::vector<Chunk64> &childChunks = d->childChunks[childChunkNum];
if(data.isEmpty()) {
// Null data: remove chunk
// Update global size
unsigned long long removedChunkTotalSize = childChunks[i].size + childChunks[i].padding + 12;
d->size -= removedChunkTotalSize;
if(d->endianness == BigEndian)
insert(ByteVector::fromUInt64BE(d->size), 4, 8);
else
insert(ByteVector::fromUInt64LE(d->size), 4, 8);
// Update child chunk size
d->chunks[d->childChunkIndex[childChunkNum]].size -= removedChunkTotalSize;
if(d->endianness == BigEndian)
insert(ByteVector::fromUInt64BE(d->chunks[d->childChunkIndex[childChunkNum]].size),
d->chunks[d->childChunkIndex[childChunkNum]].offset - 8, 8);
else
insert(ByteVector::fromUInt64LE(d->chunks[d->childChunkIndex[childChunkNum]].size),
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;
i++;
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
for(i = d->childChunkIndex[childChunkNum] + 1; 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;
childChunks.erase(childChunks.begin() + i);
}
else {
// Non null data: update chunk
// First we update the global size
d->size += ((data.size() + 1) & ~1) - (childChunks[i].size + childChunks[i].padding);
if(d->endianness == BigEndian)
insert(ByteVector::fromUInt64BE(d->size), 4, 8);
else
insert(ByteVector::fromUInt64LE(d->size), 4, 8);
// And the PROP chunk size
d->chunks[d->childChunkIndex[childChunkNum]].size += ((data.size() + 1) & ~1)
- (childChunks[i].size + childChunks[i].padding);
if(d->endianness == BigEndian)
insert(ByteVector::fromUInt64BE(d->chunks[d->childChunkIndex[childChunkNum]].size),
d->chunks[d->childChunkIndex[childChunkNum]].offset - 8, 8);
else
insert(ByteVector::fromUInt64LE(d->chunks[d->childChunkIndex[childChunkNum]].size),
d->chunks[d->childChunkIndex[childChunkNum]].offset - 8, 8);
// Now update the specific chunk
writeChunk(childChunks[i].name,
data,
childChunks[i].offset - 12,
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)
{
std::vector<Chunk64> &childChunks = d->childChunks[childChunkNum];
if(childChunks.size() == 0) {
debug("DSDIFF::File::setPropChunkData - No valid chunks found.");
return;
}
for(unsigned int i = 0; i < childChunks.size(); i++) {
if(childChunks[i].name == name) {
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 int i = childChunks.size() - 1;
unsigned long offset = childChunks[i].offset + childChunks[i].size + childChunks[i].padding;
// First we update the global size
d->size += (offset & 1) + ((data.size() + 1) & ~1) + 12;
if(d->endianness == BigEndian)
insert(ByteVector::fromUInt64BE(d->size), 4, 8);
else
insert(ByteVector::fromUInt64LE(d->size), 4, 8);
// And the child chunk size
d->chunks[d->childChunkIndex[childChunkNum]].size += (offset & 1)
+ ((data.size() + 1) & ~1) + 12;
if(d->endianness == BigEndian)
insert(ByteVector::fromUInt64BE(d->chunks[d->childChunkIndex[childChunkNum]].size),
d->chunks[d->childChunkIndex[childChunkNum]].offset - 8, 8);
else
insert(ByteVector::fromUInt64LE(d->chunks[d->childChunkIndex[childChunkNum]].size),
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,
std::max<unsigned long long>(0, nextRootChunkIdx - offset),
(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);
}
static bool isValidChunkID(const ByteVector &name)
{
if(name.size() != 4)
return false;
for(int i = 0; i < 4; i++) {
if(name[i] < 32 || name[i] > 127)
return false;
}
return true;
}
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 childchunks structure as well
if(d->childChunkIndex[PROPChunk] >= static_cast<int>(startingChunk)) {
std::vector<Chunk64> &childChunksToUpdate = d->childChunks[PROPChunk];
if(childChunksToUpdate.size() > 0) {
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)) {
std::vector<Chunk64> &childChunksToUpdate = d->childChunks[DIINChunk];
if(childChunksToUpdate.size() > 0) {
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, AudioProperties::ReadStyle propertiesStyle)
{
bool bigEndian = (d->endianness == BigEndian);
d->type = readBlock(4);
d->size = bigEndian ? readBlock(8).toInt64BE(0) : readBlock(8).toInt64LE(0);
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 = bigEndian ? readBlock(8).toInt64BE(0) : readBlock(8).toInt64LE(0);
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;
long uPosNotPadded = tell();
if((uPosNotPadded & 0x01) != 0) {
ByteVector iByte = readBlock(1);
if((iByte.size() != 1) || (iByte[0] != 0))
// Not well formed, re-seek
seek(uPosNotPadded, Beginning);
else
chunk.padding = 1;
}
d->chunks.push_back(chunk);
}
unsigned long long lengthDSDSamplesTimeChannels = 0; // For DSD uncompressed
unsigned long long audioDataSizeinBytes = 0; // For computing bitrate
unsigned long dstNumFrames = 0; // For DST compressed frames
unsigned short dstFrameRate = 0; // For DST compressed frames
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 = bigEndian ? readBlock(8).toInt64BE(0) : readBlock(8).toInt64LE(0);
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 = bigEndian ? readBlock(4).toUInt32BE(0) : readBlock(4).toUInt32LE(0);
dstFrameRate = bigEndian ? readBlock(2).toUInt16BE(0) : readBlock(2).toUInt16LE(0);
break; // Found the wanted one, no need to look at the others
}
seek(dstChunkSize, Current);
// Check padding
long uPosNotPadded = tell();
if((uPosNotPadded & 0x01) != 0) {
ByteVector iByte = readBlock(1);
if((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;
seek(d->chunks[i].offset + 4); // +4 to remove the 'SND ' marker at beginning of 'PROP' chunk
while(tell() + 12 <= propChunkEnd) {
ByteVector propChunkName = readBlock(4);
long long propChunkSize = bigEndian ? readBlock(8).toInt64BE(0) : readBlock(8).toInt64LE(0);
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;
long uPosNotPadded = tell();
if((uPosNotPadded & 0x01) != 0) {
ByteVector iByte = readBlock(1);
if((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 = bigEndian ? readBlock(8).toInt64BE(0) : readBlock(8).toInt64LE(0);
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;
long uPosNotPadded = tell();
if((uPosNotPadded & 0x01) != 0) {
ByteVector iByte = readBlock(1);
if((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->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;
continue; // ID3V2 tag has already been found at root level
}
d->id3v2TagChunkID = d->childChunks[PROPChunk][i].name;
d->tag.set(ID3v2Index, new ID3v2::Tag(this, d->childChunks[PROPChunk][i].offset));
d->isID3InPropChunk = true;
d->hasID3v2 = true;
}
else if(d->childChunks[PROPChunk][i].name == "FS ") {
// Sample rate
seek(d->childChunks[PROPChunk][i].offset);
sampleRate = bigEndian ? readBlock(4).toUInt32BE(0) : readBlock(4).toUInt32LE(0);
}
else if(d->childChunks[PROPChunk][i].name == "CHNL") {
// Channels
seek(d->childChunks[PROPChunk][i].offset);
channels = bigEndian ? readBlock(2).toInt16BE(0) : readBlock(2).toInt16LE(0);
}
}
// 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);
unsigned int titleStrLength = bigEndian ? readBlock(4).toUInt32BE(0) : readBlock(4).toUInt32LE(0);
if(titleStrLength <= d->childChunks[DIINChunk][i].size) {
ByteVector titleStr = readBlock(titleStrLength);
d->tag.access<DSDIFF::DIIN::Tag>(DIINIndex, false)->setTitle(titleStr);
}
}
else if(d->childChunks[DIINChunk][i].name == "DIAR") {
seek(d->childChunks[DIINChunk][i].offset);
unsigned int artistStrLength = bigEndian ? readBlock(4).toUInt32BE(0) : readBlock(4).toUInt32LE(0);
if(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 = (unsigned long long)dstNumFrames
* (unsigned long long)sampleRate / (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 = (audioDataSizeinBytes*8*sampleRate) / lengthDSDSamplesTimeChannels / 1000;
d->properties.reset(new AudioProperties(sampleRate,
channels,
lengthDSDSamplesTimeChannels,
bitrate,
propertiesStyle));
}
if(!ID3v2Tag()) {
d->tag.access<ID3v2::Tag>(ID3v2Index, true);
d->isID3InPropChunk = false; // By default, ID3 chunk is at root level
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);
if(d->endianness == BigEndian)
combined.append(ByteVector::fromUInt64BE(data.size()));
else
combined.append(ByteVector::fromUInt64LE(data.size()));
combined.append(data);
if((data.size() & 0x01) != 0)
combined.append('\x00');
insert(combined, offset, replace);
}

View File

@@ -1,260 +0,0 @@
/***************************************************************************
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 "rifffile.h"
#include "id3v2tag.h"
#include "dsdiffproperties.h"
#include "dsdiffdiintag.h"
namespace TagLib {
//! An implementation of DSDIFF metadata
/*!
* This is implementation of DSDIFF metadata.
*
* This supports an ID3v2 tag as well as reading stream from the ID3 RIFF
* chunk as well as properties from the file.
* Description of the DSDIFF format is available
* at http://dsd-guide.com/sites/default/files/white-papers/DSDIFF_1.5_Spec.pdf
* DSDIFF standard does not explictly specify the ID3V2 chunk
* It can be found at the root level, but also sometimes inside the PROP chunk
* In addition, title and artist info are stored as part of the standard
*/
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:
/*!
* Constructs an DSDIFF file from \a file. If \a readProperties is true
* the file's audio properties will also be read.
*
* \note In the current implementation, \a propertiesStyle is ignored.
*/
File(FileName file, bool readProperties = true,
AudioProperties::ReadStyle propertiesStyle = AudioProperties::Average);
/*!
* Constructs an DSDIFF file from \a stream. If \a readProperties is 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.
*
* \note In the current implementation, \a propertiesStyle is ignored.
*/
File(IOStream *stream, bool readProperties = true,
AudioProperties::ReadStyle propertiesStyle = AudioProperties::Average);
/*!
* Destroys this instance of the File.
*/
virtual ~File();
/*!
* 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()
*/
virtual Tag *tag() const;
/*!
* 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()
*/
virtual ID3v2::Tag *ID3v2Tag() const;
/*!
* Returns the DSDIFF DIIN Tag for this file
*
*/
DSDIFF::DIIN::Tag *DIINTag() const;
/*!
* Implements the unified property interface -- export function.
* This method forwards to ID3v2::Tag::properties().
*/
PropertyMap properties() const;
void removeUnsupportedProperties(const StringList &properties);
/*!
* Implements the unified property interface -- import function.
* This method forwards to ID3v2::Tag::setProperties().
*/
PropertyMap setProperties(const PropertyMap &);
/*!
* Returns the DSDIFF::Properties for this file. If no audio properties
* were read then this will return a null pointer.
*/
virtual AudioProperties *audioProperties() const;
/*!
* Save the file. If at least one tag -- ID3v1 or DIIN -- exists this
* will duplicate its content into the other tag. This returns 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 paramaterized save call below.
*
* \see save(int tags)
*/
virtual bool save();
/*!
* Save the file. This will attempt to save all of the tag types that are
* specified by OR-ing together TagTypes values. The save() method above
* uses AllTags. This returns true if saving was successful.
*
* This strips all tags not included in the mask, but does not modify them
* in memory, so later calls to save() which make use of these tags will
* remain valid. This also strips empty tags.
*/
bool save(int tags);
/*!
* 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 & Artist tag.
*
* \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 };
File(FileName file, Endianness endianness);
File(IOStream *stream, Endianness endianness);
private:
File(const File &);
File &operator=(const File &);
/*!
* Sets the data for the 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 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 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, AudioProperties::ReadStyle propertiesStyle);
void writeChunk(const ByteVector &name, const ByteVector &data,
unsigned long long offset, unsigned long replace = 0,
unsigned int leadingPadding = 0);
class FilePrivate;
FilePrivate *d;
};
}
}
#endif

View File

@@ -1,117 +0,0 @@
/***************************************************************************
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 <tstring.h>
#include <tdebug.h>
#include "dsdiffproperties.h"
using namespace TagLib;
class DSDIFF::AudioProperties::AudioPropertiesPrivate
{
public:
AudioPropertiesPrivate() :
length(0),
bitrate(0),
sampleRate(0),
channels(0),
sampleWidth(0),
sampleCount(0)
{
}
int length;
int bitrate;
int sampleRate;
int channels;
int sampleWidth;
unsigned long long sampleCount;
};
////////////////////////////////////////////////////////////////////////////////
// public members
////////////////////////////////////////////////////////////////////////////////
DSDIFF::AudioProperties::AudioProperties(const unsigned int sampleRate,
const unsigned short channels,
const unsigned long long samplesCount,
const int bitrate,
ReadStyle style) : TagLib::AudioProperties(), d(new AudioPropertiesPrivate)
{
d->channels = channels;
d->sampleCount = samplesCount;
d->sampleWidth = 1;
d->sampleRate = sampleRate;
d->bitrate = bitrate;
d->length = d->sampleRate > 0
? static_cast<int>((d->sampleCount * 1000.0) / d->sampleRate + 0.5)
: 0;
}
DSDIFF::AudioProperties::~AudioProperties()
{
delete d;
}
int DSDIFF::AudioProperties::length() const
{
return lengthInSeconds();
}
int DSDIFF::AudioProperties::lengthInSeconds() const
{
return d->length / 1000;
}
int DSDIFF::AudioProperties::lengthInMilliseconds() const
{
return d->length;
}
int DSDIFF::AudioProperties::bitrate() const
{
return d->bitrate;
}
int DSDIFF::AudioProperties::sampleRate() const
{
return d->sampleRate;
}
int DSDIFF::AudioProperties::channels() const
{
return d->channels;
}
int DSDIFF::AudioProperties::bitsPerSample() const
{
return d->sampleWidth;
}
long long DSDIFF::AudioProperties::sampleCount() const
{
return d->sampleCount;
}

View File

@@ -1,83 +0,0 @@
/***************************************************************************
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 an DSDIFF stream found in the AudioProperties
* API.
*/
class TAGLIB_EXPORT AudioProperties : public TagLib::AudioProperties
{
public:
/*!
* Create an instance of DSDIFF::AudioProperties with the data read from the
* ByteVector \a data.
*/
AudioProperties(const unsigned int sampleRate, const unsigned short channels,
const unsigned long long samplesCount, const int bitrate,
ReadStyle style);
/*!
* Destroys this DSDIFF::AudioProperties instance.
*/
virtual ~AudioProperties();
// Reimplementations.
virtual int length() const;
virtual int lengthInSeconds() const;
virtual int lengthInMilliseconds() const;
virtual int bitrate() const;
virtual int sampleRate() const;
virtual int channels() const;
int bitsPerSample() const;
long long sampleCount() const;
private:
AudioProperties(const AudioProperties &);
AudioProperties &operator=(const AudioProperties &);
class AudioPropertiesPrivate;
AudioPropertiesPrivate *d;
};
}
}
#endif

View File

@@ -1,234 +0,0 @@
/***************************************************************************
copyright : (C) 2013 - 2018 by 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 <tbytevector.h>
#include <tdebug.h>
#include <id3v2tag.h>
#include <tstringlist.h>
#include <tpropertymap.h>
#include <tsmartptr.h>
#include <tagutils.h>
#include "dsffile.h"
using namespace TagLib;
// The DSF specification is located at http://dsd-guide.com/sites/default/files/white-papers/DSFFileFormatSpec_E.pdf
class DSF::File::FilePrivate
{
public:
FilePrivate() :
fileSize(0),
metadataOffset(0) {}
long long fileSize;
long long metadataOffset;
SCOPED_PTR<AudioProperties> properties;
SCOPED_PTR<ID3v2::Tag> tag;
};
////////////////////////////////////////////////////////////////////////////////
// static members
////////////////////////////////////////////////////////////////////////////////
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 ");
}
////////////////////////////////////////////////////////////////////////////////
// public members
////////////////////////////////////////////////////////////////////////////////
DSF::File::File(FileName file, bool readProperties,
AudioProperties::ReadStyle propertiesStyle) :
TagLib::File(file),
d(new FilePrivate())
{
if(isOpen())
read(readProperties, propertiesStyle);
}
DSF::File::File(IOStream *stream, bool readProperties,
AudioProperties::ReadStyle propertiesStyle) :
TagLib::File(stream),
d(new FilePrivate())
{
if(isOpen())
read(readProperties, propertiesStyle);
}
DSF::File::~File()
{
delete d;
}
ID3v2::Tag *DSF::File::tag() const
{
return d->tag.get();
}
DSF::AudioProperties *DSF::File::audioProperties() const
{
return d->properties.get();
}
PropertyMap DSF::File::properties() const
{
return d->tag->properties();
}
PropertyMap DSF::File::setProperties(const PropertyMap &properties)
{
return d->tag->setProperties(properties);
}
bool DSF::File::save()
{
if(readOnly()) {
debug("DSF::File::save() -- File is read only.");
return false;
}
if(!isValid()) {
debug("DSF::File::save() -- Trying to save invalid 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::fromUInt64LE(newFileSize), 12, 8);
d->fileSize = newFileSize;
}
// Update the metadata offset to 0 since there is no longer a tag
if(d->metadataOffset) {
insert(ByteVector::fromUInt64LE(0ULL), 20, 8);
d->metadataOffset = 0;
}
// Delete the old tag
truncate(newFileSize);
}
else {
ByteVector tagData = d->tag->render();
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::fromUInt64LE(newFileSize), 12, 8);
d->fileSize = newFileSize;
}
// Update the metadata offset
if(d->metadataOffset != newMetadataOffset) {
insert(ByteVector::fromUInt64LE(newMetadataOffset), 20, 8);
d->metadataOffset = newMetadataOffset;
}
// Delete the old tag and write the new one
insert(tagData, newMetadataOffset, static_cast<size_t>(oldTagSize));
}
return true;
}
////////////////////////////////////////////////////////////////////////////////
// private members
////////////////////////////////////////////////////////////////////////////////
void DSF::File::read(bool readProperties, AudioProperties::ReadStyle propertiesStyle)
{
// 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 chunkSize = readBlock(8).toInt64LE(0);
// Integrity check
if(28 != chunkSize) {
debug("DSF::File::read() -- File is corrupted.");
setValid(false);
return;
}
d->fileSize = readBlock(8).toInt64LE(0);
// File is malformed or corrupted
if(d->fileSize != length()) {
debug("DSF::File::read() -- File is corrupted.");
setValid(false);
return;
}
d->metadataOffset = readBlock(8).toInt64LE(0);
// 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;
}
chunkSize = readBlock(8).toInt64LE(0);
d->properties.reset(
new AudioProperties(readBlock(static_cast<size_t>(chunkSize)), propertiesStyle));
// Skip the data chunk
// A metadata offset of 0 indicates the absence of an ID3v2 tag
if(0 == d->metadataOffset)
d->tag.reset(new ID3v2::Tag());
else
d->tag.reset(new ID3v2::Tag(this, d->metadataOffset));
}

View File

@@ -1,127 +0,0 @@
/***************************************************************************
copyright : (C) 2013 - 2018 by 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 "tfile.h"
#include "id3v2tag.h"
#include "dsfproperties.h"
namespace TagLib {
//! An implementation of DSF metadata
/*!
* This is implementation of DSF metadata.
*
* This supports an ID3v2 tag as well as properties from the file.
*/
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:
/*!
* Contructs an DSF file from \a file. If \a readProperties is true the
* file's audio properties will also be read using \a propertiesStyle. If
* false, \a propertiesStyle is ignored.
*/
File(FileName file, bool readProperties = true,
AudioProperties::ReadStyle propertiesStyle = AudioProperties::Average);
/*!
* Contructs an DSF file from \a file. If \a readProperties is true the
* file's audio properties will also be read using \a propertiesStyle. If
* false, \a propertiesStyle is ignored.
*/
File(IOStream *stream, bool readProperties = true,
AudioProperties::ReadStyle propertiesStyle = AudioProperties::Average);
/*!
* Destroys this instance of the File.
*/
virtual ~File();
/*!
* Returns the Tag for this file.
*/
virtual ID3v2::Tag *tag() const;
/*!
* Returns the DSF::AudioProperties for this file. If no audio properties
* were read then this will return a null pointer.
*/
virtual AudioProperties *audioProperties() const;
/*!
* Implements the unified property interface -- export function.
* This method forwards to ID3v2::Tag::properties().
*/
virtual PropertyMap properties() const;
/*!
* Implements the unified property interface -- import function.
* This method forwards to ID3v2::Tag::setProperties().
*/
virtual PropertyMap setProperties(const PropertyMap &);
/*!
* Saves the file.
*/
virtual bool save();
/*!
* 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:
File(const File &);
File &operator=(const File &);
void read(bool readProperties, AudioProperties::ReadStyle propertiesStyle);
class FilePrivate;
FilePrivate *d;
};
}
}
#endif

View File

@@ -1,158 +0,0 @@
/***************************************************************************
copyright : (C) 2013 by 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 <tstring.h>
#include <tdebug.h>
#include "dsfproperties.h"
using namespace TagLib;
class DSF::AudioProperties::PropertiesPrivate
{
public:
PropertiesPrivate() :
formatVersion(0),
formatID(0),
channelType(0),
channelNum(0),
samplingFrequency(0),
bitsPerSample(0),
sampleCount(0),
blockSizePerChannel(0),
bitrate(0),
length(0) {}
// Nomenclature is from DSF file format specification
unsigned int formatVersion;
unsigned int formatID;
unsigned int channelType;
unsigned int channelNum;
unsigned int samplingFrequency;
unsigned int bitsPerSample;
long long sampleCount;
unsigned int blockSizePerChannel;
// Computed
int bitrate;
int length;
};
////////////////////////////////////////////////////////////////////////////////
// public members
////////////////////////////////////////////////////////////////////////////////
DSF::AudioProperties::AudioProperties(const ByteVector &data, ReadStyle style) :
d(new PropertiesPrivate())
{
read(data);
}
DSF::AudioProperties::~AudioProperties()
{
delete d;
}
int DSF::AudioProperties::length() const
{
return lengthInSeconds();
}
int DSF::AudioProperties::lengthInSeconds() const
{
return d->length / 1000;
}
int DSF::AudioProperties::lengthInMilliseconds() const
{
return d->length;
}
int DSF::AudioProperties::bitrate() const
{
return d->bitrate;
}
int DSF::AudioProperties::sampleRate() const
{
return d->samplingFrequency;
}
int DSF::AudioProperties::channels() const
{
return d->channelNum;
}
// DSF specific
int DSF::AudioProperties::formatVersion() const
{
return d->formatVersion;
}
int DSF::AudioProperties::formatID() const
{
return d->formatID;
}
int DSF::AudioProperties::channelType() const
{
return d->channelType;
}
int DSF::AudioProperties::bitsPerSample() const
{
return d->bitsPerSample;
}
long long DSF::AudioProperties::sampleCount() const
{
return d->sampleCount;
}
int DSF::AudioProperties::blockSizePerChannel() const
{
return d->blockSizePerChannel;
}
////////////////////////////////////////////////////////////////////////////////
// private members
////////////////////////////////////////////////////////////////////////////////
void DSF::AudioProperties::read(const ByteVector &data)
{
d->formatVersion = data.toUInt32LE(0);
d->formatID = data.toUInt32LE(4);
d->channelType = data.toUInt32LE(8);
d->channelNum = data.toUInt32LE(12);
d->samplingFrequency = data.toUInt32LE(16);
d->bitsPerSample = data.toUInt32LE(20);
d->sampleCount = data.toInt64LE(24);
d->blockSizePerChannel = data.toUInt32LE(32);
d->bitrate = static_cast<int>(d->samplingFrequency * d->bitsPerSample * d->channelNum / 1000.0 + 0.5);
if(d->samplingFrequency > 0)
d->length = static_cast<int>(d->sampleCount * 1000.0 / d->samplingFrequency + 0.5);
}

View File

@@ -1,91 +0,0 @@
/***************************************************************************
copyright : (C) 2013 by 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 "audioproperties.h"
namespace TagLib {
namespace DSF {
class File;
//! An implementation of audio property reading for DSF
/*!
* This reads the data from a DSF stream found in the AudioProperties
* API.
*/
class TAGLIB_EXPORT AudioProperties : public TagLib::AudioProperties
{
public:
/*!
* Create an instance of DSF::AudioProperties with the data read from the
* ByteVector \a data.
*/
AudioProperties(const ByteVector &data, ReadStyle style);
/*!
* Destroys this DSF::AudioProperties instance.
*/
virtual ~AudioProperties();
// Reimplementations.
virtual int length() const;
virtual int lengthInSeconds() const;
virtual int lengthInMilliseconds() const;
virtual int bitrate() const;
virtual int sampleRate() const;
virtual int channels() const;
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:
AudioProperties(const AudioProperties &);
AudioProperties &operator=(const AudioProperties &);
void read(const ByteVector &data);
class PropertiesPrivate;
PropertiesPrivate *d;
};
}
}
#endif

View File

@@ -1,58 +0,0 @@
/***************************************************************************
copyright : (C) 2013 by Sebastian Rachuj
email : rachus@web.de
***************************************************************************/
/***************************************************************************
* 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_EBML_CONSTANTS
#define TAGLIB_EBML_CONSTANTS
namespace TagLib {
namespace EBML {
//! Shorter representation of the type.
typedef unsigned long long int ulli;
//! The id of an EBML Void element that is just a placeholder.
const ulli Void = 0xecL;
//! The id of an EBML CRC32 element that contains a crc32 value.
const ulli CRC32 = 0xc3L;
//! A namespace containing the ids of the EBML header's elements.
namespace Header {
const ulli EBML = 0x1a45dfa3L;
const ulli EBMLVersion = 0x4286L;
const ulli EBMLReadVersion = 0x42f7L;
const ulli EBMLMaxIDWidth = 0x42f2L;
const ulli EBMLMaxSizeWidth = 0x42f3L;
const ulli DocType = 0x4282L;
const ulli DocTypeVersion = 0x4287L;
const ulli DocTypeReadVersion = 0x4285L;
}
}
}
#endif

View File

@@ -1,489 +0,0 @@
/***************************************************************************
copyright : (C) 2013 by Sebastian Rachuj
email : rachus@web.de
***************************************************************************/
/***************************************************************************
* 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 "tdebug.h"
#include "ebmlelement.h"
using namespace TagLib;
class EBML::Element::ElementPrivate
{
public:
// The id of this element.
ulli id;
// The position of the element, where the header begins.
long long position;
// The size of the element as read from the header. Note: Actually an ulli but
// due to the variable integer size limited, thus long long is ok.
long long size;
// The position of the element's data.
long long data;
// The element's children.
List<Element *> children;
// True: Treated this element as container and read children.
bool populated;
// The parent element. If NULL (0) this is the document root.
Element *parent;
// The file used to read and write.
File *document;
// Destructor: Clean up all children.
~ElementPrivate()
{
for(List<Element *>::Iterator i = children.begin(); i != children.end(); ++i) {
delete *i;
}
}
// Reads a variable length integer from the file at the given position
// and saves its value to result. If cutOne is true the first one of the
// binary representation of the result is removed (required for size). If
// cutOne is false the one will remain in the result (required for id).
// This method returns the position directly after the read integer.
long long readVInt(long long position, ulli *result, bool cutOne = true)
{
document->seek(position);
// Determine the length of the integer
char firstByte = document->readBlock(1)[0];
size_t byteSize = 1;
for(size_t i = 0; i < 8 && ((firstByte << i) & (1 << 7)) == 0; ++i)
++byteSize;
// Load the integer
document->seek(position);
ByteVector vint = document->readBlock(byteSize);
// Cut the one if requested
if(cutOne)
vint[0] = (vint[0] & (~(1 << (8 - byteSize))));
// Store the result and return the current position
if(result)
*result = static_cast<ulli>(vint.toInt64BE(0));
return position + byteSize;
}
// Returns a BytVector containing the given number in the variable integer
// format. Truncates numbers > 2^56 (^ means potency in this case).
// If addOne is true, the ByteVector will remain the One to determine the
// integer's length.
// If shortest is true, the ByteVector will be as short as possible (required
// for the id)
ByteVector createVInt(ulli number, bool addOne = true, bool shortest = true)
{
ByteVector vint = ByteVector::fromUInt64BE(number);
// Do we actually need to calculate the length of the variable length
// integer? If not, then prepend the 0b0000 0001 if necessary and return the
// vint.
if(!shortest) {
if(addOne)
vint[0] = 1;
return vint;
}
// Calculate the minimal length of the variable length integer
size_t byteSize = vint.size();
for(size_t i = 0; byteSize > 0 && vint[i] == 0; ++i)
--byteSize;
if(!addOne)
return ByteVector(vint.data() + vint.size() - byteSize, byteSize);
ulli firstByte = (1ULL << (vint.size() - byteSize));
// The most significant byte loses #bytSize bits for storing information.
// Therefore, we might need to increase byteSize.
if(number >= (firstByte << (8 * (byteSize - 1))) && byteSize < vint.size())
++byteSize;
// Add the one at the correct position
size_t firstBytePosition = vint.size() - byteSize;
vint[firstBytePosition] |= (1 << firstBytePosition);
return ByteVector(vint.data() + firstBytePosition, byteSize);
}
// Returns a void element within this element which is at least "least" in
// size. Uses best fit method. Returns a null pointer if no suitable element
// was found.
Element *searchVoid(long long least = 0L)
{
Element *currentBest = 0;
for(List<Element *>::Iterator i = children.begin(); i != children.end(); ++i) {
if((*i)->d->id == Void &&
// We need room for the header if we don't remove the element.
((((*i)->d->size + (*i)->d->data - (*i)->d->position) == least || ((*i)->d->size >= least)) &&
// best fit
(!currentBest || (*i)->d->size < currentBest->d->size))
) {
currentBest = *i;
}
}
return currentBest;
}
// Replaces this element by a Void element. Returns true on success and false
// on error.
bool makeVoid()
{
ulli realSize = size + data - position;
ByteVector header(createVInt(Void, false));
ulli leftSize = realSize - (header.size() + sizeof(ulli));
// Does not make sense to create a Void element
if (leftSize > realSize)
return false;
header.append(createVInt(leftSize, true, false));
// Write to file
document->seek(position);
document->writeBlock(header);
// Update data
data = position + header.size();
size = leftSize;
return true;
// XXX: We actually should merge Voids, if possible.
}
// Reading constructor: Reads all unknown information from the file.
ElementPrivate(File *p_document, Element *p_parent = 0, long long p_position = 0) :
id(0),
position(p_position),
data(0),
populated(false),
parent(p_parent),
document(p_document)
{
if(parent) {
ulli ssize;
data = readVInt(readVInt(position, &id, false), &ssize);
size = static_cast<long long>(ssize);
}
else {
document->seek(0, File::End);
size = document->tell();
}
}
// Writing constructor: Takes given information, calculates missing information
// and writes everything to the file.
// Tries to use void elements if available in the parent.
ElementPrivate(ulli p_id, File *p_document, Element *p_parent,
long long p_position, long long p_size) :
id(p_id),
position(p_position),
size(p_size),
populated(true), // It is a new element so we know, there are no children.
parent(p_parent),
document(p_document)
{
// header
ByteVector content(createVInt(id, false).append(createVInt(size, true, false)));
data = position + content.size();
// space for children
content.resize(static_cast<size_t>(data - position + size));
Element *freeSpace;
if (!(freeSpace = searchVoid(content.size()))) {
// We have to make room
document->insert(content, position);
// Update parents
for(Element *current = parent; current->d->parent; current = current->d->parent) {
current->d->size += content.size();
// Create new header and write it.
ByteVector parentHeader(createVInt(current->d->id, false).append(createVInt(current->d->size, true, false)));
size_t oldHeaderSize = static_cast<size_t>(current->d->data - current->d->position);
if(oldHeaderSize < parentHeader.size()) {
ByteVector secondHeader(createVInt(current->d->id, false).append(createVInt(current->d->size)));
if(oldHeaderSize == secondHeader.size()) {
// Write the header where the old one was.
document->seek(current->d->position);
document->writeBlock(secondHeader);
continue; // Very important here!
}
}
// Insert the new header
document->insert(parentHeader, current->d->position, oldHeaderSize);
current->d->data = current->d->position + parentHeader.size();
}
}
else {
document->seek(freeSpace->d->position);
if((freeSpace->d->size + freeSpace->d->data - freeSpace->d->position)
== static_cast<long long>(content.size())) {
// Write to file
document->writeBlock(content);
// Update parent
for(List<Element *>::Iterator i = parent->d->children.begin();
i != parent->d->children.end(); ++i) {
if(freeSpace == *i)
parent->d->children.erase(i);
}
delete freeSpace;
}
else {
ulli newSize = freeSpace->d->size - content.size();
ByteVector newVoid(createVInt(Void, false).append(createVInt(newSize, true, false)));
// Check if the original size of the size field was really 8 byte
if (static_cast<long long>(newVoid.size()) != (freeSpace->d->data - freeSpace->d->position))
newVoid = createVInt(Void, false).append(createVInt(newSize));
// Update freeSpace
freeSpace->d->size = newSize;
freeSpace->d->data = freeSpace->d->position + newVoid.size();
// Write to file
document->writeBlock(
newVoid.resize(static_cast<size_t>(newVoid.size() + newSize)).append(content));
}
}
}
};
////////////////////////////////////////////////////////////////////////////////
// public members
////////////////////////////////////////////////////////////////////////////////
EBML::Element::~Element()
{
delete d;
}
EBML::Element::Element(EBML::File *document)
: d(new EBML::Element::ElementPrivate(document))
{
}
EBML::Element *EBML::Element::getChild(EBML::ulli id)
{
populate();
for(List<Element *>::Iterator i = d->children.begin(); i != d->children.end();
++i) {
if ((*i)->d->id == id)
return *i;
}
return 0;
}
List<EBML::Element *> EBML::Element::getChildren(EBML::ulli id)
{
populate();
List<Element *> result;
for(List<Element *>::Iterator i = d->children.begin(); i != d->children.end();
++i) {
if ((*i)->d->id == id)
result.append(*i);
}
return result;
}
List<EBML::Element *> EBML::Element::getChildren()
{
populate();
return d->children;
}
EBML::Element *EBML::Element::getParent()
{
return d->parent;
}
ByteVector EBML::Element::getAsBinary()
{
d->document->seek(d->data);
return d->document->readBlock(static_cast<size_t>(d->size));
}
String EBML::Element::getAsString()
{
return String(getAsBinary(), String::UTF8);
}
long long EBML::Element::getAsInt()
{
// The debug note about returning 0 because of empty data is irrelevant. The
// behavior is as expected.
return getAsBinary().toInt64BE(0);
}
EBML::ulli EBML::Element::getAsUnsigned()
{
// The debug note about returning 0 because of empty data is irrelevant. The
// behavior is as expected.
return static_cast<ulli>(getAsBinary().toInt64BE(0));
}
long double EBML::Element::getAsFloat()
{
const ByteVector bin = getAsBinary();
switch (bin.size())
{
case 4:
return bin.toFloat32BE(0);
case 8:
return bin.toFloat64BE(0);
case 10:
return bin.toFloat80BE(0);
default:
debug("EBML::Element::getAsFloat() - Invalid data size. Returning 0.");
return 0.0;
}
}
EBML::Element *EBML::Element::addElement(EBML::ulli id)
{
Element *elem = new Element(
new ElementPrivate(id, d->document, this, d->data + d->size, 0)
);
d->children.append(elem);
return elem;
}
EBML::Element *EBML::Element::addElement(EBML::ulli id, const ByteVector &binary)
{
Element *elem = new Element(
new ElementPrivate(id, d->document, this, d->data + d->size, binary.size())
);
d->document->seek(elem->d->data);
d->document->writeBlock(binary);
d->children.append(elem);
return elem;
}
EBML::Element *EBML::Element::addElement(EBML::ulli id, const String &string)
{
return addElement(id, string.data(String::UTF8));
}
EBML::Element *EBML::Element::addElement(EBML::ulli id, signed long long number)
{
return addElement(id, ByteVector::fromUInt64BE(number));
}
EBML::Element *EBML::Element::addElement(EBML::ulli id, EBML::ulli number)
{
return addElement(id, ByteVector::fromUInt64BE(number));
}
EBML::Element *EBML::Element::addElement(EBML::ulli id, long double number)
{
// Probably, we will never need this method.
return 0;
}
bool EBML::Element::removeChildren(EBML::ulli id, bool useVoid)
{
bool result = false;
for(List<Element *>::Iterator i = d->children.begin(); i != d->children.end(); ++i)
if((*i)->d->id == id) {
removeChild(*i, useVoid);
result = true;
}
return result;
}
bool EBML::Element::removeChildren(bool useVoid)
{
// Maybe a better implementation, because we probably create a lot of voids
// in a row where a huge Void would be more appropriate.
if (d->children.isEmpty())
return false;
for(List<Element *>::Iterator i = d->children.begin(); i != d->children.end(); ++i)
removeChild(*i, useVoid);
return true;
}
bool EBML::Element::removeChild(Element *element, bool useVoid)
{
if (!d->children.contains(element))
return false;
if(!useVoid || !element->d->makeVoid()) {
d->document->removeBlock(element->d->position, static_cast<size_t>(element->d->size));
// Update parents
for(Element* current = this; current; current = current->d->parent)
current->d->size -= element->d->size;
// Update this element
for(List<Element *>::Iterator i = d->children.begin(); i != d->children.end(); ++i)
if(element == *i)
d->children.erase(i);
delete element;
}
return true;
}
void EBML::Element::setAsBinary(const ByteVector &binary)
{
// Maybe: Search for void element after this one
d->document->insert(binary, d->data, static_cast<size_t>(d->size));
}
void EBML::Element::setAsString(const String &string)
{
setAsBinary(string.data(String::UTF8));
}
void EBML::Element::setAsInt(signed long long number)
{
setAsBinary(ByteVector::fromUInt64BE(number));
}
void EBML::Element::setAsUnsigned(EBML::ulli number)
{
setAsBinary(ByteVector::fromUInt64BE(number));
}
void EBML::Element::setAsFloat(long double)
{
// Probably, we will never need this method.
}
////////////////////////////////////////////////////////////////////////////////
// private members
////////////////////////////////////////////////////////////////////////////////
void EBML::Element::populate()
{
if(!d->populated) {
d->populated = true;
long long end = d->data + d->size;
for(long long i = d->data; i < end;) {
Element *elem = new Element(
new ElementPrivate(d->document, this, i)
);
d->children.append(elem);
i = elem->d->data + elem->d->size;
}
}
}
EBML::Element::Element(EBML::Element::ElementPrivate *pe) : d(pe)
{}

View File

@@ -1,276 +0,0 @@
/***************************************************************************
copyright : (C) 2013 by Sebastian Rachuj
email : rachus@web.de
***************************************************************************/
/***************************************************************************
* 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
#include "tlist.h"
#include "tbytevector.h"
#include "tstring.h"
#include "ebmlfile.h"
namespace TagLib {
namespace EBML {
/*!
* Represents an element of the EBML. The only instance of this child, that
* is directly used is the root element. Every other element is accessed
* via pointers to the elements within the root element.
*
* Just create one root instance per file to prevent race conditions.
*
* Changes of the document tree will be directly written back to the file.
* Invalid values (exceeding the maximal value defined in the RFC) will be
* truncated.
*
* This class should not be used by library users since the proper file
* class should handle the internals.
*
* NOTE: Currently does not adjust CRC32 values.
*/
class TAGLIB_EXPORT Element
{
public:
//! Destroys the instance of the element.
~Element();
/*!
* Creates an root element using document.
*/
explicit Element(File *document);
/*!
* Returns the first found child element with the given id. Returns a null
* pointer if the child does not exist.
*
* Do not call this method if *this element is not a container element (see
* corresponding DTD)
*/
Element *getChild(const ulli id);
/*!
* Returns a list of all child elements with the given id. Returns an
* empty list if no such element exists.
*
* Do not call this method if *this element is not a container element (see
* corresponding DTD)
*/
List<Element *> getChildren(const ulli id);
/*!
* Returns a list of every child elements available. Returns an empty list
* if there are no children.
*
* Do not call this method if *this element is not a container element (see
* corresponding DTD)
*/
List<Element *> getChildren();
/*!
* Returns the parent element or null if no such element exists.
*/
Element *getParent();
/*!
* Returns the raw content of the element.
*/
ByteVector getAsBinary();
/*!
* Returns the content of this element interpreted as a string.
*/
String getAsString();
/*!
* Returns the content of this element interpreted as an signed integer.
*
* Do not call this method if *this element is not an INT element (see
* corresponding DTD)
*/
long long getAsInt();
/*!
* Returns the content of this element interpreted as an unsigned integer.
*
* Do not call this method if *this element is not an UINT element (see
* corresponding DTD)
*/
ulli getAsUnsigned();
/*!
* Returns the content of this element interpreted as a floating point
* type.
*
* Do not call this method if *this element is not an FLOAT element (see
* corresponding DTD)
*
* NOTE: There are 10 byte floats defined, therefore we might need a long
* double to store the value.
*/
long double getAsFloat();
/*!
* Adds an empty element with given id to this element. Returns a pointer
* to the new element.
*
* Do not call this method if *this element is not a container element (see
* corresponding DTD)
*/
Element *addElement(ulli id);
/*!
* Adds a new element, containing the given binary, to this element.
* Returns a pointer to the new element.
*
* Do not call this method if *this element is not a container element (see
* corresponding DTD)
*/
Element *addElement(ulli id, const ByteVector &binary);
/*!
* Adds a new element, containing the given string, to this element.
* Returns a pointer to the new element.
*
* Do not call this method if *this element is not a container element (see
* corresponding DTD)
*/
Element *addElement(ulli id, const String &string);
/*!
* Adds a new element, containing the given integer, to this element.
* Returns a pointer to the new element.
*
* Do not call this method if *this element is not a container element (see
* corresponding DTD)
*/
Element *addElement(ulli id, signed long long number);
/*!
* Adds a new element, containing the given unsigned integer, to this element.
* Returns a pointer to the new element.
*
* Do not call this method if *this element is not a container element (see
* corresponding DTD)
*/
Element *addElement(ulli id, ulli number);
/*!
* Adds a new element, containing the given floating point value, to this element.
* Returns a pointer to the new element.
*
* Do not call this method if *this element is not a container element (see
* corresponding DTD)
*
* This method is not implemented!
*/
Element *addElement(ulli id, long double number);
/*!
* Removes all children with the given id. Returns false if there was no
* such element.
* If useVoid is true, the element will be changed to a void element.
*
* Do not call this method if *this element is not a container element (see
* corresponding DTD)
*
* Every pointer to a removed element is invalidated.
*/
bool removeChildren(ulli id, bool useVoid = true);
/*!
* Removes all children. Returns false if this element had no children.
* If useVoid ist rue, the element will be changed to a void element.
*
* Do not call this method if *this element is not a container element (see
* corresponding DTD)
*
* Every pointer to a removed element is invalidated.
*/
bool removeChildren(bool useVoid = true);
/*!
* Removes the given element.
* If useVoid is true, the element will be changed to a void element.
*
* Do not call this method if *this element is not a container element (see
* corresponding DTD)
*
* The pointer to the given element is invalidated.
*/
bool removeChild(Element *element, bool useVoid = true);
/*!
* Writes the given binary to this element.
*/
void setAsBinary(const ByteVector &binary);
/*!
* Writes the given string to this element.
*/
void setAsString(const String &string);
/*!
* Writes the given integer to this element.
*/
void setAsInt(signed long long number);
/*!
* Writes the given unsigned integer to this element.
*/
void setAsUnsigned(ulli number);
/*!
* Writes the given floating point variable to this element.
*
* This method is not implemented!
*/
void setAsFloat(long double number);
private:
//! Non-copyable
Element(const Element &);
//! Non-copyable
Element &operator=(const File &);
//! Lazy parsing. This method will be triggered when trying to access
//! children.
void populate();
class ElementPrivate;
ElementPrivate *d;
//! Creates a new Element from an ElementPrivate. (The constructor takes
//! ownership of the pointer and will delete it when the element is
//! destroyed.
Element(ElementPrivate *pe);
};
}
}
#endif

View File

@@ -1,102 +0,0 @@
/***************************************************************************
copyright : (C) 2013 by Sebastian Rachuj
email : rachus@web.de
***************************************************************************/
/***************************************************************************
* 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"
using namespace TagLib;
class EBML::File::FilePrivate
{
public:
explicit FilePrivate(File *document) : root(document)
{
}
// Performs a few basic checks and creates the FilePrivate if they were
// successful.
static FilePrivate *checkAndCreate(File* document)
{
document->seek(0);
ByteVector magical = document->readBlock(4);
if(static_cast<ulli>(magical.toUInt32BE(0)) != Header::EBML)
return 0;
FilePrivate *d = new FilePrivate(document);
Element *head = d->root.getChild(Header::EBML);
Element *p;
if(!head ||
!((p = head->getChild(Header::EBMLVersion)) && p->getAsUnsigned() == 1L) ||
!((p = head->getChild(Header::EBMLReadVersion)) && p->getAsUnsigned() == 1L) ||
// Actually 4 is the current maximum of the EBML spec, but we support up to 8
!((p = head->getChild(Header::EBMLMaxIDWidth)) && p->getAsUnsigned() <= 8) ||
!((p = head->getChild(Header::EBMLMaxSizeWidth)) && p->getAsUnsigned() <= 8)
) {
delete d;
return 0;
}
return d;
}
Element root;
};
////////////////////////////////////////////////////////////////////////////////
// public members
////////////////////////////////////////////////////////////////////////////////
EBML::File::~File()
{
delete d;
}
EBML::Element *EBML::File::getDocumentRoot()
{
if(!d && isValid())
d = new FilePrivate(this);
return &d->root;
}
////////////////////////////////////////////////////////////////////////////////
// protected members
////////////////////////////////////////////////////////////////////////////////
EBML::File::File(FileName file) :
TagLib::File(file)
{
if(isOpen()) {
d = FilePrivate::checkAndCreate(this);
if(!d)
setValid(false);
}
}
EBML::File::File(IOStream *stream) :
TagLib::File(stream)
{
if(isOpen()) {
d = FilePrivate::checkAndCreate(this);
if(!d)
setValid(false);
}
}

View File

@@ -1,86 +0,0 @@
/***************************************************************************
copyright : (C) 2013 by Sebastian Rachuj
email : rachus@web.de
***************************************************************************/
/***************************************************************************
* 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_EBMLFILE_H
#define TAGLIB_EBMLFILE_H
#include "taglib_export.h"
#include "tfile.h"
#include "ebmlconstants.h"
namespace TagLib {
//! A namespace for the classes used by EBML-based metadata files
namespace EBML {
class Element;
/*!
* Represents an EBML file. It offers access to the root element which can
* be used to obtain the necessary information and to change the file
* according to changes.
*/
class TAGLIB_EXPORT File : public TagLib::File
{
public:
//! Destroys the instance of the file.
virtual ~File();
/*!
* Returns a pointer to the document root element of the EBML file.
*/
Element *getDocumentRoot();
protected:
/*!
* Constructs an instance of an EBML file from \a file.
*
* This constructor is protected since an object should be created
* through a specific subclass.
*/
explicit File(FileName file);
/*!
* Constructs an instance of an EBML file from an IOStream.
*
* This constructor is protected since an object should be created
* through a specific subclass.
*/
explicit File(IOStream *stream);
private:
//! Non-copyable
File(const File&);
File &operator=(const File &);
class FilePrivate;
FilePrivate *d;
};
}
}
#endif

View File

@@ -1,135 +0,0 @@
/***************************************************************************
copyright : (C) 2013 by Sebastian Rachuj
email : rachus@web.de
***************************************************************************/
/***************************************************************************
* 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 "ebmlmatroskaconstants.h"
#include "ebmlmatroskaaudio.h"
using namespace TagLib;
class EBML::Matroska::AudioProperties::AudioPropertiesPrivate
{
public:
// Constructor
AudioPropertiesPrivate() :
length(0),
bitrate(0),
channels(1),
samplerate(8000) {}
// The length of the file
int length;
// The bitrate
int bitrate;
// The amount of channels
int channels;
// The sample rate
int samplerate;
};
////////////////////////////////////////////////////////////////////////////////
// public members
////////////////////////////////////////////////////////////////////////////////
EBML::Matroska::AudioProperties::AudioProperties(File *document) :
d(new AudioPropertiesPrivate())
{
read(document);
}
EBML::Matroska::AudioProperties::~AudioProperties()
{
delete d;
}
int EBML::Matroska::AudioProperties::length() const
{
return lengthInSeconds();
}
int EBML::Matroska::AudioProperties::lengthInSeconds() const
{
return d->length / 1000;
}
int EBML::Matroska::AudioProperties::lengthInMilliseconds() const
{
return d->length;
}
int EBML::Matroska::AudioProperties::bitrate() const
{
return d->bitrate;
}
int EBML::Matroska::AudioProperties::channels() const
{
return d->channels;
}
int EBML::Matroska::AudioProperties::sampleRate() const
{
return d->samplerate;
}
////////////////////////////////////////////////////////////////////////////////
// private members
////////////////////////////////////////////////////////////////////////////////
void EBML::Matroska::AudioProperties::read(File *document)
{
Element *elem = document->getDocumentRoot()->getChild(Constants::Segment);
Element *info = elem->getChild(Constants::SegmentInfo);
Element *value;
if(info && (value = info->getChild(Constants::Duration))) {
const double length = value->getAsFloat() / 1000000.0;
if((value = info->getChild(Constants::TimecodeScale)))
d->length = static_cast<int>(length * value->getAsUnsigned() + 0.5);
else
d->length = static_cast<int>(length * 1000000 + 0.5);
}
info = elem->getChild(Constants::Tracks);
if(!info || !(info = info->getChild(Constants::TrackEntry)) ||
!(info = info->getChild(Constants::Audio))) {
return;
}
// Dirty bitrate:
if(d->length > 0)
d->bitrate = static_cast<int>(document->length() * 8.0 / d->length + 0.5);
if((value = info->getChild(Constants::Channels)))
d->channels = static_cast<int>(value->getAsUnsigned());
if((value = info->getChild(Constants::SamplingFrequency)))
d->samplerate = static_cast<int>(value->getAsFloat());
}

View File

@@ -1,92 +0,0 @@
/***************************************************************************
copyright : (C) 2013 by Sebastian Rachuj
email : rachus@web.de
***************************************************************************/
/***************************************************************************
* 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_EBMLMATROSKAAUDIO_H
#define TAGLIB_EBMLMATROSKAAUDIO_H
#include "ebmlmatroskafile.h"
#include "audioproperties.h"
namespace TagLib {
namespace EBML {
namespace Matroska {
/*!
* This class represents the audio properties of a matroska file.
* Currently all information are read from the container format and
* could be inexact.
*/
class TAGLIB_EXPORT AudioProperties : public TagLib::AudioProperties
{
public:
//! Destructor
virtual ~AudioProperties();
/*!
* Constructs an instance from a file.
*/
explicit AudioProperties(File *document);
/*!
* Returns the length of the file.
*/
virtual int length() const;
virtual int lengthInSeconds() const;
virtual int lengthInMilliseconds() const;
/*!
* Returns the bit rate of the file. Since the container format does not
* offer a proper value, it ist currently calculated by dividing the
* file size by the length.
*/
virtual int bitrate() const;
/*!
* Returns the amount of channels of the file.
*/
virtual int channels() const;
/*!
* Returns the sample rate of the file.
*/
virtual int sampleRate() const;
private:
void read(File *document);
class AudioPropertiesPrivate;
AudioPropertiesPrivate *d;
};
}
}
}
#endif

View File

@@ -1,140 +0,0 @@
/***************************************************************************
copyright : (C) 2013 by Sebastian Rachuj
email : rachus@web.de
***************************************************************************/
/***************************************************************************
* 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_EBMLMATROSKACONSTANTS_H
#define TAGLIB_EBMLMATROSKACONSTANTS_H
#include "ebmlconstants.h"
#include "tstring.h"
namespace TagLib {
namespace EBML {
namespace Matroska {
namespace Constants {
//! ID of an Matroska segment.
const ulli Segment = 0x18538067L;
//! ID of the tags element.
const ulli Tags = 0x1254c367L;
//! ID of the tag element.
const ulli Tag = 0x7373L;
//! ID of the targets element.
const ulli Targets = 0x63c0L;
//! ID of the target type value element.
const ulli TargetTypeValue = 0x68caL;
//! ID of the target type element.
const ulli TargetType = 0x63caL;
//! ID of a simple tag element.
const ulli SimpleTag = 0x67c8L;
//! ID of the tag name.
const ulli TagName = 0x45a3L;
//! ID of the tag content.
const ulli TagString = 0x4487L;
//! The DocType of a matroska file.
const String DocTypeMatroska = "matroska";
//! The DocType of a WebM file.
const String DocTypeWebM = "webm";
//! The TITLE entry
const String TITLE = "TITLE";
//! The ARTIST entry
const String ARTIST = "ARTIST";
//! The COMMENT entry
const String COMMENT = "COMMENT";
//! The GENRE entry
const String GENRE = "GENRE";
//! The DATE_RELEASE entry
const String DATE_RELEASE = "DATE_RELEASE";
//! The PART_NUMBER entry
const String PART_NUMBER = "PART_NUMBER";
//! The TargetTypeValue of the most common grouping level (e.g. album)
const ulli MostCommonGroupingValue = 50;
//! The TargetTypeValue of the most common parts of a group (e.g. track)
const ulli MostCommonPartValue = 30;
//! Name of the TargetType of an album.
const String ALBUM = "ALBUM";
//! Name of the TargetType of a track.
const String TRACK = "TRACK";
// For AudioProperties
//! ID of the Info block within the Segment.
const ulli SegmentInfo = 0x1549a966L;
//! ID of the duration element.
const ulli Duration = 0x4489L;
//! ID of TimecodeScale element.
const ulli TimecodeScale = 0x2ad7b1L;
//! ID of the Tracks container
const ulli Tracks = 0x1654ae6bL;
//! ID of a TrackEntry element.
const ulli TrackEntry = 0xaeL;
//! ID of the Audio container.
const ulli Audio = 0xe1L;
//! ID of the SamplingFrequency element.
const ulli SamplingFrequency = 0xb5L;
//! ID of the Channels element.
const ulli Channels = 0x9fL;
//! ID of the BitDepth element.
const ulli BitDepth = 0x6264L;
}
}
}
}
#endif

View File

@@ -1,563 +0,0 @@
/***************************************************************************
copyright : (C) 2013 by Sebastian Rachuj
email : rachus@web.de
***************************************************************************/
/***************************************************************************
* 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 "ebmlmatroskaconstants.h"
#include "ebmlmatroskaaudio.h"
#include "tpicturemap.h"
#include "tpropertymap.h"
using namespace TagLib;
class EBML::Matroska::File::FilePrivate
{
public:
// Returns true if simpleTag has a TagName and TagString child and writes
// their contents into name and value.
bool extractContent(Element *simpleTag, String &name, String &value)
{
Element *n = simpleTag->getChild(Constants::TagName);
Element *v = simpleTag->getChild(Constants::TagString);
if(!n || !v)
return false;
name = n->getAsString();
value = v->getAsString();
return true;
}
explicit FilePrivate(File *p_document) : tag(0), document(p_document)
{
// Just get the first segment, because "Typically a Matroska file is
// composed of 1 segment."
Element* elem = document->getDocumentRoot()->getChild(Constants::Segment);
// We take the first tags element (there shouldn't be more), if there is
// non such element, consider the file as not compatible.
if(!elem || !(elem = elem->getChild(Constants::Tags))) {
document->setValid(false);
return;
}
// Load all Tag entries
List<Element *> entries = elem->getChildren(Constants::Tag);
for(List<Element *>::Iterator i = entries.begin(); i != entries.end(); ++i) {
Element *target = (*i)->getChild(Constants::Targets);
ulli ttvalue = 50; // 50 is default (see spec.)
if(target && (target = target->getChild(Constants::TargetTypeValue)))
ttvalue = target->getAsUnsigned();
// Load all SimpleTags
PropertyMap tagEntries;
List<Element *> simpleTags = (*i)->getChildren(Constants::SimpleTag);
for(List<Element *>::Iterator j = simpleTags.begin(); j != simpleTags.end();
++j) {
String name, value;
if(!extractContent(*j, name, value))
continue;
tagEntries.insert(name, StringList(value));
}
tags.append(std::pair<PropertyMap, std::pair<Element *, ulli> >(tagEntries, std::pair<Element *, ulli>(*i, ttvalue)));
}
}
// Creates Tag and AudioProperties. Late creation because both require a fully
// functional FilePrivate (well AudioProperties doesn't...)
void lateCreate()
{
tag = new Tag(document);
audio = new AudioProperties(document);
}
// Checks the EBML header and creates the FilePrivate.
static FilePrivate *checkAndCreate(File *document)
{
Element *elem = document->getDocumentRoot()->getChild(Header::EBML);
Element *child = elem->getChild(Header::DocType);
if(child) {
String dt = child->getAsString();
if (dt == Constants::DocTypeMatroska || dt == Constants::DocTypeWebM) {
FilePrivate *fp = new FilePrivate(document);
return fp;
}
}
return 0;
}
// The tags with their Element and TargetTypeValue
List<std::pair<PropertyMap, std::pair<Element *, ulli> > > tags;
// The tag
Tag *tag;
// The audio properties
AudioProperties *audio;
// The corresponding file.
File *document;
};
////////////////////////////////////////////////////////////////////////////////
// public members
////////////////////////////////////////////////////////////////////////////////
EBML::Matroska::File::~File()
{
if (d) {
delete d->tag;
delete d->audio;
delete d;
}
}
EBML::Matroska::File::File(FileName file, bool, AudioProperties::ReadStyle) : EBML::File(file), d(0)
{
if(isValid() && isOpen()) {
d = FilePrivate::checkAndCreate(this);
if(!d)
setValid(false);
else
d->lateCreate();
}
}
EBML::Matroska::File::File(IOStream *stream, bool, AudioProperties::ReadStyle) : EBML::File(stream), d(0)
{
if(isValid() && isOpen()) {
d = FilePrivate::checkAndCreate(this);
if(!d)
setValid(false);
else
d->lateCreate();
}
}
Tag *EBML::Matroska::File::tag() const
{
return d->tag;
}
PropertyMap EBML::Matroska::File::properties() const
{
List<std::pair<PropertyMap, std::pair<Element *, ulli> > >::Iterator best = d->tags.end();
for(List<std::pair<PropertyMap, std::pair<Element *, ulli> > >::Iterator i = d->tags.begin();
i != d->tags.end(); ++i) {
if(best == d->tags.end() || best->second.second > i->second.second)
best = i;
}
return best->first;
}
PropertyMap EBML::Matroska::File::setProperties(const PropertyMap &properties)
{
List<std::pair<PropertyMap, std::pair<Element *, ulli> > >::Iterator best = d->tags.end();
for(List<std::pair<PropertyMap, std::pair<Element *, ulli> > >::Iterator i = d->tags.begin();
i != d->tags.end(); ++i) {
if(best == d->tags.end() || best->second.second > i->second.second)
best = i;
}
std::pair<PropertyMap, std::pair<Element *, ulli> > replace(properties, best->second);
d->tags.erase(best);
d->tags.prepend(replace);
return PropertyMap();
}
AudioProperties *EBML::Matroska::File::audioProperties() const
{
return d->audio;
}
bool EBML::Matroska::File::save()
{
if(readOnly())
return false;
// C++11 features would be nice: for(auto &i : d->tags) { /* ... */ }
// Well, here we just iterate each extracted element.
for(List<std::pair<PropertyMap, std::pair<Element *, ulli> > >::Iterator i = d->tags.begin();
i != d->tags.end(); ++i) {
for(PropertyMap::Iterator j = i->first.begin(); j != i->first.end(); ++j) {
// No element? Create it!
if(!i->second.first) {
// Should be save, since we already checked, when creating the object.
Element *container = d->document->getDocumentRoot()
->getChild(Constants::Segment)->getChild(Constants::Tags);
// Create Targets container
i->second.first = container->addElement(Constants::Tag);
Element *target = i->second.first->addElement(Constants::Targets);
if(i->second.second == Constants::MostCommonPartValue)
target->addElement(Constants::TargetType, Constants::TRACK);
else if(i->second.second == Constants::MostCommonGroupingValue)
target->addElement(Constants::TargetType, Constants::ALBUM);
target->addElement(Constants::TargetTypeValue, i->second.second);
}
// Find entries
List<Element *> simpleTags = i->second.first->getChildren(Constants::SimpleTag);
StringList::Iterator str = j->second.begin();
List<Element *>::Iterator k = simpleTags.begin();
for(; k != simpleTags.end(); ++k) {
String name, value;
if(!d->extractContent(*k, name, value))
continue;
// Write entry from StringList
if(name == j->first) {
if(str == j->second.end()) {
// We have all StringList elements but still found another element
// with the same name? Let's delete it!
i->second.first->removeChild(*k);
}
else {
if(value != *str) {
// extractContent already checked for availability
(*k)->getChild(Constants::TagString)->setAsString(*str);
}
++str;
}
}
}
// If we didn't write the complete StringList, we have to write the rest.
for(; str != j->second.end(); ++str) {
Element *stag = i->second.first->addElement(Constants::SimpleTag);
stag->addElement(Constants::TagName, j->first);
stag->addElement(Constants::TagString, *str);
}
}
// Finally, we have to find elements that are not in the PropertyMap and
// remove them.
List<Element *> simpleTags = i->second.first->getChildren(Constants::SimpleTag);
for(List<Element *>::Iterator j = simpleTags.begin(); j != simpleTags.end(); ++j) {
String name, value;
if(!d->extractContent(*j, name, value))
continue;
if(i->first.find(name) == i->first.end()){
i->second.first->removeChild(*j);}
}
}
return true;
}
////////////////////////////////////////////////////////////////////////////////
//
// Tag
//
////////////////////////////////////////////////////////////////////////////////
class EBML::Matroska::File::Tag::TagPrivate
{
public:
// Creates a TagPrivate instance
explicit TagPrivate(File *p_document) :
document(p_document),
title(document->d->tags.end()),
artist(document->d->tags.end()),
album(document->d->tags.end()),
comment(document->d->tags.end()),
genre(document->d->tags.end()),
year(document->d->tags.end()),
track(document->d->tags.end())
{
for(List<std::pair<PropertyMap, std::pair<Element *, ulli> > >::Iterator i =
document->d->tags.begin(); i != document->d->tags.end(); ++i) {
// Just save it, if the title is more specific, or there is no title yet.
if(i->first.find(Constants::TITLE) != i->first.end() &&
(title == document->d->tags.end() ||
title->second.second > i->second.second ||
i->second.second == Constants::MostCommonPartValue)) {
title = i;
}
// Same goes for artist.
if(i->first.find(Constants::ARTIST) != i->first.end() &&
(artist == document->d->tags.end() ||
artist->second.second > i->second.second ||
i->second.second == Constants::MostCommonPartValue)) {
artist = i;
}
// Here, we also look for a title (the album title), but since we
// specified the granularity, we have to search for it exactly.
// Therefore it is possible, that title and album are the same (if only
// the title of the album is given).
if(i->first.find(Constants::TITLE) != i->first.end() &&
i->second.second == Constants::MostCommonGroupingValue) {
album = i;
}
// Again the same as title and artist.
if(i->first.find(Constants::COMMENT) != i->first.end() &&
(comment == document->d->tags.end() ||
comment->second.second > i->second.second ||
i->second.second == Constants::MostCommonPartValue)) {
comment = i;
}
// Same goes for genre.
if(i->first.find(Constants::GENRE) != i->first.end() &&
(genre == document->d->tags.end() ||
genre->second.second > i->second.second ||
i->second.second == Constants::MostCommonPartValue)) {
genre = i;
}
// And year (in our case: DATE_REALEASE)
if(i->first.find(Constants::DATE_RELEASE) != i->first.end() &&
(year == document->d->tags.end() ||
year->second.second > i->second.second ||
i->second.second == Constants::MostCommonPartValue)) {
year = i;
}
// And track (in our case: PART_NUMBER)
if(i->first.find(Constants::PART_NUMBER) != i->first.end() &&
(track == document->d->tags.end() ||
track->second.second > i->second.second ||
i->second.second == Constants::MostCommonPartValue)) {
track = i;
}
}
}
// Searches for the Tag with given TargetTypeValue (returns the first one)
List<std::pair<PropertyMap, std::pair<Element *, ulli> > >::Iterator
find(ulli ttv)
{
for(List<std::pair<PropertyMap, std::pair<Element *, ulli> > >::Iterator i =
document->d->tags.begin(); i != document->d->tags.end(); ++i) {
if(i->second.second == ttv)
return i;
}
return document->d->tags.end();
}
// Updates the given information
void update(
List<std::pair<PropertyMap, std::pair<Element *, ulli> > >::Iterator t,
const String &tagname,
const String &s
)
{
t->first.find(tagname)->second.front() = s;
}
// Inserts a tag with given information
void insert(const String &tagname, const ulli ttv, const String &s)
{
for(List<std::pair<PropertyMap, std::pair<Element *, ulli> > >::Iterator i =
document->d->tags.begin(); i != document->d->tags.end(); ++i) {
if(i->second.second == ttv) {
i->first.insert(tagname, StringList(s));
return;
}
}
// Not found? Create new!
PropertyMap pm;
pm.insert(tagname, StringList(s));
document->d->tags.append(
std::pair<PropertyMap, std::pair<Element *, ulli> >(pm,
std::pair<Element *, ulli>(static_cast<Element *>(0), ttv)
)
);
}
// The PropertyMap from the Matroska::File
File *document;
// Iterators to the tags.
List<std::pair<PropertyMap, std::pair<Element *, ulli> > >::Iterator title;
List<std::pair<PropertyMap, std::pair<Element *, ulli> > >::Iterator artist;
List<std::pair<PropertyMap, std::pair<Element *, ulli> > >::Iterator album;
List<std::pair<PropertyMap, std::pair<Element *, ulli> > >::Iterator comment;
List<std::pair<PropertyMap, std::pair<Element *, ulli> > >::Iterator genre;
List<std::pair<PropertyMap, std::pair<Element *, ulli> > >::Iterator year;
List<std::pair<PropertyMap, std::pair<Element *, ulli> > >::Iterator track;
};
////////////////////////////////////////////////////////////////////////////////
// public members
////////////////////////////////////////////////////////////////////////////////
EBML::Matroska::File::Tag::~Tag()
{
delete e;
}
EBML::Matroska::File::Tag::Tag(EBML::Matroska::File *document) :
e(new EBML::Matroska::File::Tag::TagPrivate(document))
{
}
String EBML::Matroska::File::Tag::title() const
{
if(e->title != e->document->d->tags.end())
return e->title->first.find(Constants::TITLE)->second.front();
else
return String();
}
String EBML::Matroska::File::Tag::artist() const
{
if(e->artist != e->document->d->tags.end())
return e->artist->first.find(Constants::ARTIST)->second.front();
else
return String();
}
String EBML::Matroska::File::Tag::album() const
{
if(e->album != e->document->d->tags.end())
return e->album->first.find(Constants::TITLE)->second.front();
else
return String();
}
PictureMap EBML::Matroska::File::Tag::pictures() const
{
return PictureMap();
}
String EBML::Matroska::File::Tag::comment() const
{
if(e->comment != e->document->d->tags.end())
return e->comment->first.find(Constants::COMMENT)->second.front();
else
return String();
}
String EBML::Matroska::File::Tag::genre() const
{
if(e->genre != e->document->d->tags.end())
return e->genre->first.find(Constants::GENRE)->second.front();
else
return String();
}
unsigned int EBML::Matroska::File::Tag::year() const
{
if(e->year != e->document->d->tags.end())
return e->year->first.find(Constants::DATE_RELEASE)->second.front().toInt();
else
return 0;
}
unsigned int EBML::Matroska::File::Tag::track() const
{
if(e->track != e->document->d->tags.end())
return e->track->first.find(Constants::PART_NUMBER)->second.front().toInt();
else
return 0;
}
void EBML::Matroska::File::Tag::setTitle(const String &s)
{
if(e->title != e->document->d->tags.end())
e->update(e->title, Constants::TITLE, s);
else
e->insert(Constants::TITLE, Constants::MostCommonPartValue, s);
}
void EBML::Matroska::File::Tag::setArtist(const String &s)
{
if(e->artist != e->document->d->tags.end())
e->update(e->artist, Constants::ARTIST, s);
else
e->insert(Constants::ARTIST, Constants::MostCommonPartValue, s);
}
void EBML::Matroska::File::Tag::setAlbum(const String &s)
{
if(e->album != e->document->d->tags.end())
e->update(e->album, Constants::TITLE, s);
else
e->insert(Constants::TITLE, Constants::MostCommonGroupingValue, s);
}
void EBML::Matroska::File::Tag::setPictures(const PictureMap& p )
{
(void)p; // avoid warning for unused variable
}
void EBML::Matroska::File::Tag::setComment(const String &s)
{
if(e->comment != e->document->d->tags.end())
e->update(e->comment, Constants::COMMENT, s);
else
e->insert(Constants::COMMENT, Constants::MostCommonPartValue, s);
}
void EBML::Matroska::File::Tag::setGenre(const String &s)
{
if(e->genre != e->document->d->tags.end())
e->update(e->genre, Constants::GENRE, s);
else
e->insert(Constants::GENRE, Constants::MostCommonPartValue, s);
}
void EBML::Matroska::File::Tag::setYear(unsigned int i)
{
String s = String::number(i);
if(e->year != e->document->d->tags.end())
e->update(e->year, Constants::DATE_RELEASE, s);
else
e->insert(Constants::DATE_RELEASE, Constants::MostCommonPartValue, s);
}
void EBML::Matroska::File::Tag::setTrack(unsigned int i)
{
String s = String::number(i);
if(e->track != e->document->d->tags.end())
e->update(e->track, Constants::PART_NUMBER, s);
else
e->insert(Constants::PART_NUMBER, Constants::MostCommonPartValue, s);
}

View File

@@ -1,228 +0,0 @@
/***************************************************************************
copyright : (C) 2013 by Sebastian Rachuj
email : rachus@web.de
***************************************************************************/
/***************************************************************************
* 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_EBMLMATROSKAFILE_H
#define TAGLIB_EBMLMATROSKAFILE_H
#include "ebmlelement.h"
#include "audioproperties.h"
namespace TagLib {
namespace EBML {
//! Implementation for reading Matroska tags.
namespace Matroska {
/*!
* Implements the TagLib::File API and offers access to the tags of the
* matroska file.
*/
class TAGLIB_EXPORT File : public EBML::File
{
public:
//! Destroys the instance of the file.
virtual ~File();
/*!
* Constructs a Matroska file from \a file. If \a readProperties is true the
* file's audio properties will also be read using \a propertiesStyle. If
* false, \a propertiesStyle is ignored.
*
* \note In the current implementation, both \a readProperties and
* \a propertiesStyle are ignored. The audio properties are always
* read.
*/
explicit File(FileName file, bool readProperties = true,
AudioProperties::ReadStyle propertiesStyle = AudioProperties::Average);
/*!
* Constructs a Matroska file from \a stream. If \a readProperties is true the
* file's audio properties will also be read using \a propertiesStyle. If
* false, \a propertiesStyle is ignored.
*
* \note In the current implementation, both \a readProperties and
* \a propertiesStyle are ignored. The audio properties are always
* read.
*/
explicit File(IOStream *stream, bool readproperties = true,
AudioProperties::ReadStyle propertiesStyle = AudioProperties::Average);
/*!
* Returns the pointer to a tag that allow access on common tags.
*/
virtual TagLib::Tag *tag() const;
/*!
* Exports the tags to a PropertyMap. Due to the diversity of the
* Matroska format (e.g. multiple media streams in one file, each with
* its own tags), only the best fitting tags are taken into account.
* There are no unsupported tags.
*/
virtual PropertyMap properties() const;
/*!
* Sets the tags of the file to those specified in properties. The
* returned PropertyMap is always empty.
* Note: Only the best fitting tags are taken into account.
*/
virtual PropertyMap setProperties(const PropertyMap &properties);
/*!
* Returns a pointer to this file's audio properties.
*
* I'm glad about not having a setAudioProperties method ;)
*/
virtual AudioProperties *audioProperties() const;
/*!
* Saves the file. Returns true on success.
*/
bool save();
/*!
* Offers access to a few common tag entries.
*/
class Tag : public TagLib::Tag
{
public:
//! Destroys the tag.
~Tag();
/*!
* Creates a new Tag for Matroska files. The given properties are gained
* by the Matroska::File.
*/
explicit Tag(File *document);
/*!
* Returns the track name; if no track name is present in the tag
* String::null will be returned.
*/
virtual String title() const;
/*!
* Returns the artist name; if no artist name is present in the tag
* String::null will be returned.
*/
virtual String artist() const;
/*!
* Returns the album name; if no album name is present in the tag
* String::null will be returned.
*/
virtual String album() const;
/*!
* Returns a list of pictures; if no picture is present in the tag
* an empty PictureMap is returned
*/
virtual PictureMap pictures() const;
/*!
* Returns the track comment; if no comment is present in the tag
* String::null will be returned.
*/
virtual String comment() const;
/*!
* Returns the genre name; if no genre is present in the tag String::null
* will be returned.
*/
virtual String genre() const;
/*!
* Returns the year; if there is no year set, this will return 0.
*/
virtual unsigned int year() const;
/*!
* Returns the track number; if there is no track number set, this will
* return 0.
*/
virtual unsigned int track() const;
/*!
* Sets the title to s. If s is String::null then this value will be
* cleared.
*/
virtual void setTitle(const String &s);
/*!
* Sets the artist to s. If s is String::null then this value will be
* cleared.
*/
virtual void setArtist(const String &s);
/*!
* Sets the album to s. If s is String::null then this value will be
* cleared.
*/
virtual void setAlbum(const String &s);
/*!
* Sets the picture map to p. If p is empty then this value will be
* cleared
*/
virtual void setPictures(const PictureMap& p );
/*!
* Sets the comment to s. If s is String::null then this value will be
* cleared.
*/
virtual void setComment(const String &s);
/*!
* Sets the genre to s. If s is String::null then this value will be
* cleared.
*/
virtual void setGenre(const String &s);
/*!
* Sets the year to i. If s is 0 then this value will be cleared.
*/
virtual void setYear(unsigned int i);
/*!
* Sets the track to i. If s is 0 then this value will be cleared.
*/
virtual void setTrack(unsigned int i);
private:
class TagPrivate;
TagPrivate *e;
};
private:
class FilePrivate;
FilePrivate *d;
};
}
}
}
#endif

View File

@@ -27,11 +27,13 @@
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#include <cstring>
#include <tfile.h>
#include <tfilestream.h>
#include <tstring.h>
#include <tdebug.h>
#include <tsmartptr.h>
#include <trefcounter.h>
#include "fileref.h"
#include "asffile.h"
@@ -52,9 +54,6 @@
#include "s3mfile.h"
#include "itfile.h"
#include "xmfile.h"
#include "dsffile.h"
#include "dsdifffile.h"
#include "ebmlmatroskafile.h"
using namespace TagLib;
@@ -68,6 +67,13 @@ namespace
File *detectByResolvers(FileName fileName, bool readAudioProperties,
AudioProperties::ReadStyle audioPropertiesStyle)
{
#ifdef _WIN32
if(::strlen(fileName) == 0 && ::wcslen(fileName) == 0)
return 0;
#else
if(::strlen(fileName) == 0)
return 0;
#endif
ResolverList::ConstIterator it = fileTypeResolvers.begin();
for(; it != fileTypeResolvers.end(); ++it) {
File *file = (*it)->createFile(fileName, readAudioProperties, audioPropertiesStyle);
@@ -78,20 +84,36 @@ namespace
return 0;
}
File *detectByResolvers(IOStream* stream, bool readAudioProperties,
AudioProperties::ReadStyle audioPropertiesStyle)
{
for(ResolverList::ConstIterator it = fileTypeResolvers.begin();
it != fileTypeResolvers.end(); ++it) {
if(const FileRef::StreamTypeResolver *streamResolver =
dynamic_cast<const FileRef::StreamTypeResolver*>(*it)) {
if(File *file = streamResolver->createFileFromStream(
stream, readAudioProperties, audioPropertiesStyle))
return file;
}
}
return 0;
}
// Detect the file type based on the file extension.
File* detectByExtension(IOStream *stream, bool readAudioProperties,
AudioProperties::ReadStyle audioPropertiesStyle)
{
#ifdef _WIN32
const String s(stream->name().wstr());
const String s = stream->name().toString();
#else
const String s(stream->name());
#endif
String ext;
const size_t pos = s.rfind(".");
if(pos != String::npos())
const int pos = s.rfind(".");
if(pos != -1)
ext = s.substr(pos + 1).upper();
// If this list is updated, the method defaultFileExtensions() should also be
@@ -103,47 +125,50 @@ namespace
// .oga can be any audio in the Ogg container. So leave it to content-based detection.
File *file = 0;
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, 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);
file = new MPEG::File(stream, ID3v2::FrameFactory::instance(), readAudioProperties, audioPropertiesStyle);
else if(ext == "OGG")
file = new Ogg::Vorbis::File(stream, readAudioProperties, audioPropertiesStyle);
else if(ext == "FLAC")
file = new FLAC::File(stream, ID3v2::FrameFactory::instance(), readAudioProperties, audioPropertiesStyle);
else if(ext == "MPC")
file = new MPC::File(stream, readAudioProperties, audioPropertiesStyle);
else if(ext == "WV")
file = new WavPack::File(stream, readAudioProperties, audioPropertiesStyle);
else if(ext == "SPX")
file = new Ogg::Speex::File(stream, readAudioProperties, audioPropertiesStyle);
else if(ext == "OPUS")
file = new Ogg::Opus::File(stream, readAudioProperties, audioPropertiesStyle);
else if(ext == "TTA")
file = new TrueAudio::File(stream, readAudioProperties, audioPropertiesStyle);
else if(ext == "M4A" || ext == "M4R" || ext == "M4B" || ext == "M4P" || ext == "MP4" || ext == "3G2" || ext == "M4V")
file = new MP4::File(stream, readAudioProperties, audioPropertiesStyle);
else if(ext == "WMA" || ext == "ASF")
file = new ASF::File(stream, readAudioProperties, audioPropertiesStyle);
else if(ext == "AIF" || ext == "AIFF" || ext == "AFC" || ext == "AIFC")
file = new RIFF::AIFF::File(stream, readAudioProperties, audioPropertiesStyle);
else if(ext == "WAV")
file = new RIFF::WAV::File(stream, readAudioProperties, audioPropertiesStyle);
else if(ext == "APE")
file = new APE::File(stream, readAudioProperties, audioPropertiesStyle);
// 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);
if(ext == "DFF" || ext == "DSDIFF")
return new DSDIFF::File(stream, readAudioProperties, audioPropertiesStyle);
if(ext == "DSF")
return new DSF::File(stream, readAudioProperties, audioPropertiesStyle);
if (ext == "MKA" || ext == "MKV") {
return new EBML::Matroska::File(stream, readAudioProperties, audioPropertiesStyle);
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);
// if file is not valid, leave it to content-based detection.
if(file) {
if(file->isValid())
return file;
delete file;
}
return 0;
@@ -163,7 +188,7 @@ namespace
else if(Ogg::FLAC::File::isSupported(stream))
file = new Ogg::FLAC::File(stream, readAudioProperties, audioPropertiesStyle);
else if(FLAC::File::isSupported(stream))
file = new FLAC::File(stream, readAudioProperties, audioPropertiesStyle);
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))
@@ -184,46 +209,105 @@ namespace
file = new RIFF::WAV::File(stream, readAudioProperties, audioPropertiesStyle);
else if(APE::File::isSupported(stream))
file = new APE::File(stream, readAudioProperties, audioPropertiesStyle);
else if(DSDIFF::File::isSupported(stream))
file = new DSDIFF::File(stream, readAudioProperties, audioPropertiesStyle);
else if(DSF::File::isSupported(stream))
file = new DSF::File(stream, readAudioProperties, audioPropertiesStyle);
// isSupported() only does a quick check, so double check the file here.
if(file) {
if(file->isValid())
return file;
else
delete file;
delete file;
}
return 0;
}
struct FileRefData
// 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)
{
FileRefData() :
file(0),
stream(0) {}
File *file = detectByResolvers(fileName, readAudioProperties, audioPropertiesStyle);
if(file)
return file;
~FileRefData() {
#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;
delete stream;
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);
File *file;
IOStream *stream;
};
}
return 0;
}
} // namespace
class FileRef::FileRefPrivate
class FileRef::FileRefPrivate : public RefCounter
{
public:
FileRefPrivate() :
data(new FileRefData()) {}
file(0),
stream(0) {}
SHARED_PTR<FileRefData> data;
~FileRefPrivate() {
delete file;
delete stream;
}
File *file;
IOStream *stream;
};
////////////////////////////////////////////////////////////////////////////////
@@ -251,17 +335,19 @@ FileRef::FileRef(IOStream* stream, bool readAudioProperties, AudioProperties::Re
FileRef::FileRef(File *file) :
d(new FileRefPrivate())
{
d->data->file = file;
d->file = file;
}
FileRef::FileRef(const FileRef &ref) :
d(new FileRefPrivate(*ref.d))
d(ref.d)
{
d->ref();
}
FileRef::~FileRef()
{
delete d;
if(d->deref())
delete d;
}
Tag *FileRef::tag() const
@@ -270,37 +356,7 @@ Tag *FileRef::tag() const
debug("FileRef::tag() - Called without a valid file.");
return 0;
}
return d->data->file->tag();
}
PropertyMap FileRef::properties() const
{
if(isNull()) {
debug("FileRef::properties() - Called without a valid file.");
return PropertyMap();
}
return d->data->file->properties();
}
void FileRef::removeUnsupportedProperties(const StringList& properties)
{
if(isNull()) {
debug("FileRef::removeUnsupportedProperties() - Called without a valid file.");
return;
}
d->data->file->removeUnsupportedProperties(properties);
}
PropertyMap FileRef::setProperties(const PropertyMap &properties)
{
if(isNull()) {
debug("FileRef::setProperties() - Called without a valid file.");
return PropertyMap();
}
return d->data->file->setProperties(properties);
return d->file->tag();
}
AudioProperties *FileRef::audioProperties() const
@@ -309,12 +365,12 @@ AudioProperties *FileRef::audioProperties() const
debug("FileRef::audioProperties() - Called without a valid file.");
return 0;
}
return d->data->file->audioProperties();
return d->file->audioProperties();
}
File *FileRef::file() const
{
return d->data->file;
return d->file;
}
bool FileRef::save()
@@ -323,7 +379,7 @@ bool FileRef::save()
debug("FileRef::save() - Called without a valid file.");
return false;
}
return d->data->file->save();
return d->file->save();
}
const FileRef::FileTypeResolver *FileRef::addFileTypeResolver(const FileRef::FileTypeResolver *resolver) // static
@@ -339,6 +395,7 @@ StringList FileRef::defaultFileExtensions()
l.append("ogg");
l.append("flac");
l.append("oga");
l.append("opus");
l.append("mp3");
l.append("mpc");
l.append("wv");
@@ -355,6 +412,8 @@ StringList FileRef::defaultFileExtensions()
l.append("asf");
l.append("aif");
l.append("aiff");
l.append("afc");
l.append("aifc");
l.append("wav");
l.append("ape");
l.append("mod");
@@ -364,23 +423,13 @@ StringList FileRef::defaultFileExtensions()
l.append("s3m");
l.append("it");
l.append("xm");
l.append("dsf");
l.append("dff");
l.append("dsdiff"); // alias for "dff"
l.append("mka");
l.append("mkv");
return l;
}
bool FileRef::isValid() const
{
return (d->data->file && d->data->file->isValid());
}
bool FileRef::isNull() const
{
return !isValid();
return (!d->file || !d->file->isValid());
}
FileRef &FileRef::operator=(const FileRef &ref)
@@ -398,12 +447,18 @@ void FileRef::swap(FileRef &ref)
bool FileRef::operator==(const FileRef &ref) const
{
return (ref.d->data == d->data);
return (ref.d->file == d->file);
}
bool FileRef::operator!=(const FileRef &ref) const
{
return (ref.d->data != d->data);
return (ref.d->file != d->file);
}
File *FileRef::create(FileName fileName, bool readAudioProperties,
AudioProperties::ReadStyle audioPropertiesStyle) // static
{
return createInternal(fileName, readAudioProperties, audioPropertiesStyle);
}
////////////////////////////////////////////////////////////////////////////////
@@ -415,41 +470,51 @@ void FileRef::parse(FileName fileName, bool readAudioProperties,
{
// Try user-defined resolvers.
d->data->file = detectByResolvers(fileName, readAudioProperties, audioPropertiesStyle);
if(d->data->file)
d->file = detectByResolvers(fileName, readAudioProperties, audioPropertiesStyle);
if(d->file)
return;
// Try to resolve file types based on the file extension.
d->data->stream = new FileStream(fileName);
d->data->file = detectByExtension(d->data->stream, readAudioProperties, audioPropertiesStyle);
if(d->data->file)
d->stream = new FileStream(fileName);
d->file = detectByExtension(d->stream, readAudioProperties, audioPropertiesStyle);
if(d->file)
return;
// At last, try to resolve file types based on the actual content.
d->data->file = detectByContent(d->data->stream, readAudioProperties, audioPropertiesStyle);
if(d->data->file)
d->file = detectByContent(d->stream, readAudioProperties, audioPropertiesStyle);
if(d->file)
return;
// Stream have to be closed here if failed to resolve file types.
delete d->data->stream;
d->data->stream = 0;
delete d->stream;
d->stream = 0;
}
void FileRef::parse(IOStream *stream, bool readAudioProperties,
AudioProperties::ReadStyle audioPropertiesStyle)
{
// User-defined resolvers won't work with a stream.
// Try user-defined stream resolvers.
d->file = detectByResolvers(stream, readAudioProperties, audioPropertiesStyle);
if(d->file)
return;
// Try user-defined resolvers.
d->file = detectByResolvers(stream->name(), readAudioProperties, audioPropertiesStyle);
if(d->file)
return;
// Try to resolve file types based on the file extension.
d->data->file = detectByExtension(stream, readAudioProperties, audioPropertiesStyle);
if(d->data->file)
d->file = detectByExtension(stream, readAudioProperties, audioPropertiesStyle);
if(d->file)
return;
// At last, try to resolve file types based on the actual content of the file.
d->data->file = detectByContent(stream, readAudioProperties, audioPropertiesStyle);
d->file = detectByContent(stream, readAudioProperties, audioPropertiesStyle);
}

View File

@@ -26,10 +26,10 @@
#ifndef TAGLIB_FILEREF_H
#define TAGLIB_FILEREF_H
#include "taglib_export.h"
#include "tfile.h"
#include "tstringlist.h"
#include "tpropertymap.h"
#include "taglib_export.h"
#include "audioproperties.h"
namespace TagLib {
@@ -91,9 +91,8 @@ namespace TagLib {
class TAGLIB_EXPORT FileTypeResolver
{
TAGLIB_IGNORE_MISSING_DESTRUCTOR
public:
virtual ~FileTypeResolver() {}
/*!
* This method must be overridden to provide an additional file type
* resolver. If the resolver is able to determine the file type it should
@@ -109,6 +108,16 @@ namespace TagLib {
audioPropertiesStyle = AudioProperties::Average) const = 0;
};
class TAGLIB_EXPORT StreamTypeResolver : public FileTypeResolver
{
TAGLIB_IGNORE_MISSING_DESTRUCTOR
public:
virtual File *createFileFromStream(IOStream *stream,
bool readAudioProperties = true,
AudioProperties::ReadStyle
audioPropertiesStyle = AudioProperties::Average) const = 0;
};
/*!
* Creates a null FileRef.
*/
@@ -167,48 +176,13 @@ namespace TagLib {
* \warning This pointer will become invalid when this FileRef and all
* copies pass out of scope.
*
* \warning Do not cast it to any subclasses of \class Tag.
* Use tag returning methods of appropriate subclasses of \class File instead.
* \warning Do not cast it to any subclasses of Tag.
* Use tag returning methods of appropriate subclasses of File instead.
*
* \see File::tag()
*/
Tag *tag() const;
/*!
* Exports the tags of the file as dictionary mapping (human readable) tag
* names (uppercase Strings) to StringLists of tag values. Calls the according
* specialization in the File subclasses.
* For each metadata object of the file that could not be parsed into the PropertyMap
* format, the returend 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 this File to those specified in \a properties. Calls the
* according specialization method in the subclasses of File to do the translation
* into the format-specific details.
* If some value(s) could not be written imported to the specific metadata format,
* the returned PropertyMap will contain those value(s). Otherwise it will be empty,
* indicating that no problems occured.
* 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);
/*!
* Returns the audio properties for this FileRef. If no audio properties
* were read then this will returns a null pointer.
@@ -269,17 +243,8 @@ namespace TagLib {
*/
static StringList defaultFileExtensions();
/*!
* Returns true if the file is open and readable.
*
* \note Just a negative of isNull().
*/
bool isValid() const;
/*!
* Returns true if the file (and as such other pointers) are null.
*
* \note Just a negative of isValid().
*/
bool isNull() const;
@@ -304,6 +269,21 @@ namespace TagLib {
*/
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);

View File

@@ -30,7 +30,6 @@
#include <tagunion.h>
#include <tpropertymap.h>
#include <tagutils.h>
#include <tsmartptr.h>
#include <id3v2header.h>
#include <id3v2tag.h>
@@ -46,61 +45,53 @@ using namespace TagLib;
namespace
{
typedef List<SHARED_PTR<FLAC::MetadataBlock> > BlockList;
typedef List<FLAC::MetadataBlock *> BlockList;
typedef BlockList::Iterator BlockIterator;
typedef BlockList::Iterator BlockConstIterator;
enum { FlacXiphIndex = 0, FlacID3v2Index = 1, FlacID3v1Index = 2 };
const long long MinPaddingLength = 4096;
const long long MaxPaddingLegnth = 1024 * 1024;
const long MinPaddingLength = 4096;
const long MaxPaddingLegnth = 1024 * 1024;
const char LastBlockFlag = '\x80';
}
namespace TagLib
{
namespace FLAC
{
// Enables BlockList::find() to take raw pointers.
bool operator==(SHARED_PTR<MetadataBlock> lhs, MetadataBlock *rhs)
{
return lhs.get() == rhs;
}
}
}
} // namespace
class FLAC::File::FilePrivate
{
public:
explicit FilePrivate(const ID3v2::FrameFactory *frameFactory) :
ID3v2FrameFactory(ID3v2::FrameFactory::instance()),
FilePrivate(const ID3v2::FrameFactory *frameFactory = ID3v2::FrameFactory::instance()) :
ID3v2FrameFactory(frameFactory),
ID3v2Location(-1),
ID3v2OriginalSize(0),
ID3v1Location(-1),
properties(0),
flacStart(0),
streamStart(0),
scanned(false)
{
if(frameFactory)
ID3v2FrameFactory = frameFactory;
blocks.setAutoDelete(true);
}
~FilePrivate()
{
delete properties;
}
const ID3v2::FrameFactory *ID3v2FrameFactory;
long long ID3v2Location;
long long ID3v2OriginalSize;
long ID3v2Location;
long ID3v2OriginalSize;
long long ID3v1Location;
long ID3v1Location;
TripleTagUnion tag;
TagUnion tag;
SCOPED_PTR<AudioProperties> properties;
Properties *properties;
ByteVector xiphCommentData;
BlockList blocks;
long long flacStart;
long long streamStart;
long flacStart;
long streamStart;
bool scanned;
};
@@ -113,15 +104,23 @@ 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") != ByteVector::npos());
return (buffer.find("fLaC") >= 0);
}
////////////////////////////////////////////////////////////////////////////////
// public members
////////////////////////////////////////////////////////////////////////////////
FLAC::File::File(FileName file, bool readProperties, AudioProperties::ReadStyle,
ID3v2::FrameFactory *frameFactory) :
FLAC::File::File(FileName file, bool readProperties, Properties::ReadStyle) :
TagLib::File(file),
d(new FilePrivate())
{
if(isOpen())
read(readProperties);
}
FLAC::File::File(FileName file, ID3v2::FrameFactory *frameFactory,
bool readProperties, Properties::ReadStyle) :
TagLib::File(file),
d(new FilePrivate(frameFactory))
{
@@ -129,8 +128,8 @@ FLAC::File::File(FileName file, bool readProperties, AudioProperties::ReadStyle,
read(readProperties);
}
FLAC::File::File(IOStream *stream, bool readProperties, AudioProperties::ReadStyle,
ID3v2::FrameFactory *frameFactory) :
FLAC::File::File(IOStream *stream, ID3v2::FrameFactory *frameFactory,
bool readProperties, Properties::ReadStyle) :
TagLib::File(stream),
d(new FilePrivate(frameFactory))
{
@@ -148,14 +147,24 @@ TagLib::Tag *FLAC::File::tag() const
return &d->tag;
}
PropertyMap FLAC::File::properties() const
{
return d->tag.properties();
}
void FLAC::File::removeUnsupportedProperties(const StringList &unsupported)
{
d->tag.removeUnsupportedProperties(unsupported);
}
PropertyMap FLAC::File::setProperties(const PropertyMap &properties)
{
return xiphComment(true)->setProperties(properties);
}
FLAC::AudioProperties *FLAC::File::audioProperties() const
FLAC::Properties *FLAC::File::audioProperties() const
{
return d->properties.get();
return d->properties;
}
bool FLAC::File::save()
@@ -178,23 +187,31 @@ bool FLAC::File::save()
// Replace metadata blocks
for(BlockIterator it = d->blocks.begin(); it != d->blocks.end(); ++it) {
MetadataBlock *commentBlock =
new UnknownMetadataBlock(MetadataBlock::VorbisComment, d->xiphCommentData);
for(BlockIterator it = d->blocks.begin(); it != d->blocks.end();) {
if((*it)->code() == MetadataBlock::VorbisComment) {
// Set the new Vorbis Comment block
d->blocks.erase(it);
break;
// Remove the old Vorbis Comment block
delete *it;
it = d->blocks.erase(it);
continue;
}
if(commentBlock && (*it)->code() == MetadataBlock::Picture) {
// Set the new Vorbis Comment block before the first picture block
d->blocks.insert(it, commentBlock);
commentBlock = 0;
}
++it;
}
d->blocks.append(SHARED_PTR<MetadataBlock>(
new UnknownMetadataBlock(MetadataBlock::VorbisComment, d->xiphCommentData)));
if(commentBlock)
d->blocks.append(commentBlock);
// Render data for the metadata blocks
ByteVector data;
for(BlockConstIterator it = d->blocks.begin(); it != d->blocks.end(); ++it) {
ByteVector blockData = (*it)->render();
ByteVector blockHeader = ByteVector::fromUInt32BE(blockData.size());
ByteVector blockHeader = ByteVector::fromUInt(blockData.size());
blockHeader[0] = (*it)->code();
data.append(blockHeader);
data.append(blockData);
@@ -202,8 +219,8 @@ bool FLAC::File::save()
// Compute the amount of padding, and append that to data.
long long originalLength = d->streamStart - d->flacStart;
long long paddingLength = originalLength - data.size() - 4;
long originalLength = d->streamStart - d->flacStart;
long paddingLength = originalLength - data.size() - 4;
if(paddingLength <= 0) {
paddingLength = MinPaddingLength;
@@ -211,7 +228,7 @@ bool FLAC::File::save()
else {
// Padding won't increase beyond 1% of the file size or 1MB.
long long threshold = length() / 100;
long threshold = length() / 100;
threshold = std::max(threshold, MinPaddingLength);
threshold = std::min(threshold, MaxPaddingLegnth);
@@ -219,14 +236,14 @@ bool FLAC::File::save()
paddingLength = MinPaddingLength;
}
ByteVector paddingHeader = ByteVector::fromUInt32BE(static_cast<unsigned int>(paddingLength));
ByteVector paddingHeader = ByteVector::fromUInt(paddingLength);
paddingHeader[0] = static_cast<char>(MetadataBlock::Padding | LastBlockFlag);
data.append(paddingHeader);
data.resize(static_cast<size_t>(data.size() + paddingLength));
data.resize(static_cast<unsigned int>(data.size() + paddingLength));
// Write the data to the file
insert(data, d->flacStart, static_cast<size_t>(originalLength));
insert(data, d->flacStart, originalLength);
d->streamStart += (static_cast<long>(data.size()) - originalLength);
@@ -243,7 +260,7 @@ bool FLAC::File::save()
d->ID3v2Location = 0;
data = ID3v2Tag()->render();
insert(data, d->ID3v2Location, static_cast<size_t>(d->ID3v2OriginalSize));
insert(data, d->ID3v2Location, d->ID3v2OriginalSize);
d->flacStart += (static_cast<long>(data.size()) - d->ID3v2OriginalSize);
d->streamStart += (static_cast<long>(data.size()) - d->ID3v2OriginalSize);
@@ -258,7 +275,7 @@ bool FLAC::File::save()
// ID3v2 tag is empty. Remove the old one.
if(d->ID3v2Location >= 0) {
removeBlock(d->ID3v2Location, static_cast<size_t>(d->ID3v2OriginalSize));
removeBlock(d->ID3v2Location, d->ID3v2OriginalSize);
d->flacStart -= d->ID3v2OriginalSize;
d->streamStart -= d->ID3v2OriginalSize;
@@ -313,11 +330,28 @@ 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->get());
Picture *picture = dynamic_cast<Picture *>(*it);
if(picture) {
pictures.append(picture);
}
@@ -327,20 +361,24 @@ List<FLAC::Picture *> FLAC::File::pictureList()
void FLAC::File::addPicture(Picture *picture)
{
d->blocks.append(SHARED_PTR<Picture>(picture));
d->blocks.append(picture);
}
void FLAC::File::removePicture(Picture *picture)
void FLAC::File::removePicture(Picture *picture, bool del)
{
BlockIterator it = d->blocks.find(picture);
if(it != d->blocks.end())
d->blocks.erase(it);
if(del)
delete picture;
}
void FLAC::File::removePictures()
{
for(BlockIterator it = d->blocks.begin(); it != d->blocks.end(); ) {
if(dynamic_cast<Picture *>(it->get())) {
if(dynamic_cast<Picture *>(*it)) {
delete *it;
it = d->blocks.erase(it);
}
else {
@@ -418,14 +456,14 @@ void FLAC::File::read(bool readProperties)
const ByteVector infoData = d->blocks.front()->render();
long long streamLength;
long streamLength;
if(d->ID3v1Location >= 0)
streamLength = d->ID3v1Location - d->streamStart;
else
streamLength = length() - d->streamStart;
d->properties.reset(new AudioProperties(infoData, streamLength));
d->properties = new Properties(infoData, streamLength);
}
}
@@ -439,7 +477,7 @@ void FLAC::File::scan()
if(!isValid())
return;
long long nextBlockOffset;
long nextBlockOffset;
if(d->ID3v2Location >= 0)
nextBlockOffset = find("fLaC", d->ID3v2Location + d->ID3v2OriginalSize);
@@ -459,6 +497,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
@@ -474,7 +517,7 @@ void FLAC::File::scan()
const char blockType = header[0] & ~LastBlockFlag;
const bool isLastBlock = (header[0] & LastBlockFlag) != 0;
const size_t blockLength = header.toUInt24BE(1);
const unsigned int blockLength = header.toUInt(1U, 3U);
// First block should be the stream_info metadata
@@ -499,32 +542,33 @@ void FLAC::File::scan()
return;
}
SHARED_PTR<MetadataBlock> block;
MetadataBlock *block = 0;
// Found the vorbis-comment
if(blockType == MetadataBlock::VorbisComment) {
if(d->xiphCommentData.isEmpty()) {
d->xiphCommentData = data;
block.reset(new UnknownMetadataBlock(MetadataBlock::VorbisComment, data));
block = new UnknownMetadataBlock(MetadataBlock::VorbisComment, data);
}
else {
debug("FLAC::File::scan() -- multiple Vorbis Comment blocks found, discarding");
}
}
else if(blockType == MetadataBlock::Picture) {
SHARED_PTR<FLAC::Picture> picture(new FLAC::Picture());
FLAC::Picture *picture = new FLAC::Picture();
if(picture->parse(data)) {
block = picture;
}
else {
debug("FLAC::File::scan() -- invalid picture found, discarding");
delete picture;
}
}
else if(blockType == MetadataBlock::Padding) {
// Skip all padding blocks.
}
else {
block.reset(new UnknownMetadataBlock(blockType, data));
block = new UnknownMetadataBlock(blockType, data);
}
if(block)

View File

@@ -83,6 +83,18 @@ namespace TagLib {
AllTags = 0xffff
};
/*!
* Constructs a FLAC file from \a file. If \a readProperties is 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.
*/
File(FileName file, bool readProperties = true,
Properties::ReadStyle propertiesStyle = Properties::Average);
/*!
* Constructs an FLAC file from \a file. If \a readProperties is true the
* file's audio properties will also be read.
@@ -92,27 +104,27 @@ namespace TagLib {
*
* \note In the current implementation, \a propertiesStyle is ignored.
*/
File(FileName file,
// BIC: merge with the above constructor
File(FileName file, ID3v2::FrameFactory *frameFactory,
bool readProperties = true,
AudioProperties::ReadStyle propertiesStyle = AudioProperties::Average,
ID3v2::FrameFactory *frameFactory = 0);
Properties::ReadStyle propertiesStyle = Properties::Average);
/*!
* Constructs a FLAC file from \a file. If \a readProperties is true the
* Constructs a FLAC file from \a stream. If \a readProperties is 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.
*
* \note In the current implementation, \a propertiesStyle is ignored.
*
* \note TagLib will *not* take ownership of the stream, the caller is
* responsible for deleting it after the File object.
*/
File(IOStream *stream,
// BIC: merge with the above constructor
File(IOStream *stream, ID3v2::FrameFactory *frameFactory,
bool readProperties = true,
AudioProperties::ReadStyle propertiesStyle = AudioProperties::Average,
ID3v2::FrameFactory *frameFactory = 0);
Properties::ReadStyle propertiesStyle = Properties::Average);
/*!
* Destroys this instance of the File.
@@ -129,6 +141,16 @@ namespace TagLib {
*/
virtual TagLib::Tag *tag() const;
/*!
* Implements the unified property interface -- export function.
* If the file contains more than one tag (e.g. XiphComment and ID3v1),
* only the first one (in the order XiphComment, ID3v2, ID3v1) will be
* converted to the PropertyMap.
*/
PropertyMap properties() const;
void removeUnsupportedProperties(const StringList &);
/*!
* Implements the unified property interface -- import function.
* This always creates a Xiph comment, if none exists. The return value
@@ -136,13 +158,13 @@ namespace TagLib {
* Ignores any changes to ID3v1 or ID3v2 comments since they are not allowed
* in the FLAC specification.
*/
virtual PropertyMap setProperties(const PropertyMap &);
PropertyMap setProperties(const PropertyMap &);
/*!
* Returns the FLAC::Properties for this file. If no audio properties
* were read then this will return a null pointer.
*/
virtual AudioProperties *audioProperties() const;
virtual Properties *audioProperties() const;
/*!
* Save the file. This will primarily save the XiphComment, but
@@ -210,15 +232,42 @@ 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. The picture's memory will be freed.
* 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.
*/
void removePicture(Picture *picture);
void removePicture(Picture *picture, bool del = true);
/*!
* Remove all attached images.

View File

@@ -78,10 +78,10 @@ bool FLAC::Picture::parse(const ByteVector &data)
return false;
}
size_t pos = 0;
d->type = FLAC::Picture::Type(data.toUInt32BE(pos));
unsigned int pos = 0;
d->type = FLAC::Picture::Type(data.toUInt(pos));
pos += 4;
const unsigned int mimeTypeLength = data.toUInt32BE(pos);
unsigned int mimeTypeLength = data.toUInt(pos);
pos += 4;
if(pos + mimeTypeLength + 24 > data.size()) {
debug("Invalid picture block.");
@@ -89,7 +89,7 @@ bool FLAC::Picture::parse(const ByteVector &data)
}
d->mimeType = String(data.mid(pos, mimeTypeLength), String::UTF8);
pos += mimeTypeLength;
const unsigned int descriptionLength = data.toUInt32BE(pos);
unsigned int descriptionLength = data.toUInt(pos);
pos += 4;
if(pos + descriptionLength + 20 > data.size()) {
debug("Invalid picture block.");
@@ -97,15 +97,15 @@ bool FLAC::Picture::parse(const ByteVector &data)
}
d->description = String(data.mid(pos, descriptionLength), String::UTF8);
pos += descriptionLength;
d->width = data.toUInt32BE(pos);
d->width = data.toUInt(pos);
pos += 4;
d->height = data.toUInt32BE(pos);
d->height = data.toUInt(pos);
pos += 4;
d->colorDepth = data.toUInt32BE(pos);
d->colorDepth = data.toUInt(pos);
pos += 4;
d->numColors = data.toUInt32BE(pos);
d->numColors = data.toUInt(pos);
pos += 4;
const unsigned int dataLength = data.toUInt32BE(pos);
unsigned int dataLength = data.toUInt(pos);
pos += 4;
if(pos + dataLength > data.size()) {
debug("Invalid picture block.");
@@ -119,18 +119,18 @@ bool FLAC::Picture::parse(const ByteVector &data)
ByteVector FLAC::Picture::render() const
{
ByteVector result;
result.append(ByteVector::fromUInt32BE(d->type));
result.append(ByteVector::fromUInt(d->type));
ByteVector mimeTypeData = d->mimeType.data(String::UTF8);
result.append(ByteVector::fromUInt32BE(mimeTypeData.size()));
result.append(ByteVector::fromUInt(mimeTypeData.size()));
result.append(mimeTypeData);
ByteVector descriptionData = d->description.data(String::UTF8);
result.append(ByteVector::fromUInt32BE(descriptionData.size()));
result.append(ByteVector::fromUInt(descriptionData.size()));
result.append(descriptionData);
result.append(ByteVector::fromUInt32BE(d->width));
result.append(ByteVector::fromUInt32BE(d->height));
result.append(ByteVector::fromUInt32BE(d->colorDepth));
result.append(ByteVector::fromUInt32BE(d->numColors));
result.append(ByteVector::fromUInt32BE(d->data.size()));
result.append(ByteVector::fromUInt(d->width));
result.append(ByteVector::fromUInt(d->height));
result.append(ByteVector::fromUInt(d->colorDepth));
result.append(ByteVector::fromUInt(d->numColors));
result.append(ByteVector::fromUInt(d->data.size()));
result.append(d->data);
return result;
}

View File

@@ -89,7 +89,7 @@ namespace TagLib {
};
Picture();
explicit Picture(const ByteVector &data);
Picture(const ByteVector &data);
~Picture();
/*!
@@ -199,6 +199,8 @@ namespace TagLib {
PicturePrivate *d;
};
typedef List<Picture> PictureList;
}
}

View File

@@ -31,7 +31,7 @@
using namespace TagLib;
class FLAC::AudioProperties::PropertiesPrivate
class FLAC::Properties::PropertiesPrivate
{
public:
PropertiesPrivate() :
@@ -55,64 +55,71 @@ public:
// public members
////////////////////////////////////////////////////////////////////////////////
FLAC::AudioProperties::AudioProperties(const ByteVector &data, long long streamLength, ReadStyle) :
TagLib::AudioProperties(),
FLAC::Properties::Properties(ByteVector data, long streamLength, ReadStyle style) :
AudioProperties(style),
d(new PropertiesPrivate())
{
read(data, streamLength);
}
FLAC::AudioProperties::~AudioProperties()
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::AudioProperties::length() const
int FLAC::Properties::length() const
{
return lengthInSeconds();
}
int FLAC::AudioProperties::lengthInSeconds() const
int FLAC::Properties::lengthInSeconds() const
{
return d->length / 1000;
}
int FLAC::AudioProperties::lengthInMilliseconds() const
int FLAC::Properties::lengthInMilliseconds() const
{
return d->length;
}
int FLAC::AudioProperties::bitrate() const
int FLAC::Properties::bitrate() const
{
return d->bitrate;
}
int FLAC::AudioProperties::sampleRate() const
int FLAC::Properties::sampleRate() const
{
return d->sampleRate;
}
int FLAC::AudioProperties::bitsPerSample() const
int FLAC::Properties::bitsPerSample() const
{
return d->bitsPerSample;
}
int FLAC::AudioProperties::sampleWidth() const
int FLAC::Properties::sampleWidth() const
{
return bitsPerSample();
}
int FLAC::AudioProperties::channels() const
int FLAC::Properties::channels() const
{
return d->channels;
}
unsigned long long FLAC::AudioProperties::sampleFrames() const
unsigned long long FLAC::Properties::sampleFrames() const
{
return d->sampleFrames;
}
ByteVector FLAC::AudioProperties::signature() const
ByteVector FLAC::Properties::signature() const
{
return d->signature;
}
@@ -121,14 +128,14 @@ ByteVector FLAC::AudioProperties::signature() const
// private members
////////////////////////////////////////////////////////////////////////////////
void FLAC::AudioProperties::read(const ByteVector &data, long long streamLength)
void FLAC::Properties::read(const ByteVector &data, long streamLength)
{
if(data.size() < 18) {
debug("FLAC::AudioProperties::read() - FLAC properties must contain at least 18 bytes.");
debug("FLAC::Properties::read() - FLAC properties must contain at least 18 bytes.");
return;
}
size_t pos = 0;
unsigned int pos = 0;
// Minimum block size (in samples)
pos += 2;
@@ -142,7 +149,7 @@ void FLAC::AudioProperties::read(const ByteVector &data, long long streamLength)
// Maximum frame size (in bytes)
pos += 3;
const unsigned int flags = data.toUInt32BE(pos);
const unsigned int flags = data.toUInt(pos, true);
pos += 4;
d->sampleRate = flags >> 12;
@@ -153,7 +160,7 @@ void FLAC::AudioProperties::read(const ByteVector &data, long long streamLength)
// stream length in samples. (Audio files measured in days)
const unsigned long long hi = flags & 0xf;
const unsigned long long lo = data.toUInt32BE(pos);
const unsigned long long lo = data.toUInt(pos, true);
pos += 4;
d->sampleFrames = (hi << 32) | lo;

View File

@@ -42,19 +42,27 @@ namespace TagLib {
* API.
*/
class TAGLIB_EXPORT AudioProperties : public TagLib::AudioProperties
class TAGLIB_EXPORT Properties : public AudioProperties
{
public:
/*!
* Creates an instance of FLAC::AudioProperties with the data read from
* the ByteVector \a data.
* Create an instance of FLAC::Properties with the data read from the
* ByteVector \a data.
*/
AudioProperties(const ByteVector &data, long long streamLength, ReadStyle style = Average);
// BIC: switch to const reference
Properties(ByteVector data, long streamLength, ReadStyle style = Average);
/*!
* Destroys this FLAC::AudioProperties instance.
* Create an instance of FLAC::Properties with the data read from the
* FLAC::File \a file.
*/
virtual ~AudioProperties();
// BIC: remove
Properties(File *file, ReadStyle style = Average);
/*!
* Destroys this FLAC::Properties instance.
*/
virtual ~Properties();
/*!
* Returns the length of the file in seconds. The length is rounded down to
@@ -64,7 +72,7 @@ namespace TagLib {
*
* \deprecated
*/
virtual int length() const;
TAGLIB_DEPRECATED virtual int length() const;
/*!
* Returns the length of the file in seconds. The length is rounded down to
@@ -72,14 +80,16 @@ namespace TagLib {
*
* \see lengthInMilliseconds()
*/
virtual int lengthInSeconds() const;
// BIC: make virtual
int lengthInSeconds() const;
/*!
* Returns the length of the file in milliseconds.
*
* \see lengthInSeconds()
*/
virtual int lengthInMilliseconds() const;
// BIC: make virtual
int lengthInMilliseconds() const;
/*!
* Returns the average bit rate of the file in kb/s.
@@ -110,7 +120,7 @@ namespace TagLib {
*
* \deprecated
*/
int sampleWidth() const;
TAGLIB_DEPRECATED int sampleWidth() const;
/*!
* Return the number of sample frames.
@@ -124,7 +134,10 @@ namespace TagLib {
ByteVector signature() const;
private:
void read(const ByteVector &data, long long streamLength);
Properties(const Properties &);
Properties &operator=(const Properties &);
void read(const ByteVector &data, long streamLength);
class PropertiesPrivate;
PropertiesPrivate *d;

View File

@@ -36,13 +36,13 @@ using namespace IT;
class IT::File::FilePrivate
{
public:
explicit FilePrivate(AudioProperties::ReadStyle propertiesStyle)
: tag(), properties(propertiesStyle)
FilePrivate(AudioProperties::ReadStyle propertiesStyle)
: properties(propertiesStyle)
{
}
Mod::Tag tag;
IT::AudioProperties properties;
IT::Properties properties;
};
IT::File::File(FileName file, bool readProperties,
@@ -73,7 +73,17 @@ Mod::Tag *IT::File::tag() const
return &d->tag;
}
IT::AudioProperties *IT::File::audioProperties() const
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;
}
@@ -103,8 +113,8 @@ 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));
unsigned int instrumentOffset = 0;
seek(192L + length + (static_cast<long>(i) << 2));
unsigned long instrumentOffset = 0;
if(!readU32L(instrumentOffset))
return false;
@@ -118,14 +128,14 @@ bool IT::File::save()
}
for(unsigned short i = 0; i < sampleCount; ++ i) {
seek(192L + length + ((long)instrumentCount << 2) + ((long)i << 2));
unsigned int sampleOffset = 0;
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);
@@ -142,18 +152,18 @@ 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;
unsigned int messageOffset = 0;
unsigned long messageOffset = 0;
seek(46);
if(!readU16L(special))
return false;
unsigned int fileSize = static_cast<unsigned int>(File::length());
if(special & AudioProperties::MessageAttached) {
unsigned long fileSize = File::length();
if(special & Properties::MessageAttached) {
seek(54);
if(!readU16L(messageLength) || !readU32L(messageOffset))
return false;
@@ -171,7 +181,7 @@ bool IT::File::save()
if(messageOffset + messageLength >= fileSize) {
// append new message
seek(54);
writeU16L(static_cast<unsigned short>(message.size()));
writeU16L(message.size());
writeU32L(messageOffset);
seek(messageOffset);
writeBlock(message);
@@ -223,14 +233,14 @@ void IT::File::read(bool)
// sample/instrument names are abused as comments so
// I just add all together.
String message;
if(special & AudioProperties::MessageAttached) {
if(special & Properties::MessageAttached) {
READ_U16L_AS(messageLength);
READ_U32L_AS(messageOffset);
seek(messageOffset);
ByteVector messageBytes = readBlock(messageLength);
READ_ASSERT(messageBytes.size() == messageLength);
const size_t index = messageBytes.find((char) 0);
if(index != ByteVector::npos())
int index = messageBytes.find(static_cast<char>(0));
if(index > -1)
messageBytes.resize(index, 0);
messageBytes.replace('\r', '\n');
message = messageBytes;
@@ -248,7 +258,7 @@ void IT::File::read(bool)
// 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)
if(static_cast<unsigned char>(pannings[i]) < 128 && volumes[i] > 0)
++channels;
}
d->properties.setChannels(channels);
@@ -267,10 +277,10 @@ void IT::File::read(bool)
// in the instrument/sample names and more characters
// afterwards. The spec does not mention such a case.
// Currently I just discard anything after a nil, but
// e.g. VLC seems to interprete a nil as a space. I
// e.g. VLC seems to interpret a nil as a space. I
// don't know what is the proper behaviour.
for(unsigned short i = 0; i < instrumentCount; ++ i) {
seek(192L + length + ((long)i << 2));
seek(192L + length + (static_cast<long>(i) << 2));
READ_U32L_AS(instrumentOffset);
seek(instrumentOffset);
@@ -286,7 +296,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);

View File

@@ -67,11 +67,23 @@ namespace TagLib {
Mod::Tag *tag() const;
/*!
* 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 &);
/*!
* Returns the IT::Properties for this file. If no audio properties
* were read then this will return a null pointer.
*/
IT::AudioProperties *audioProperties() const;
IT::Properties *audioProperties() const;
/*!
* Save the file.

View File

@@ -1,4 +1,4 @@
/***************************************************************************
/***************************************************************************
copyright :(C) 2011 by Mathias Panzenböck
email : grosser.meister.morti@gmx.net
***************************************************************************/
@@ -29,7 +29,7 @@
using namespace TagLib;
using namespace IT;
class IT::AudioProperties::PropertiesPrivate
class IT::Properties::PropertiesPrivate
{
public:
PropertiesPrivate() :
@@ -47,7 +47,9 @@ public:
tempo(0),
bpmSpeed(0),
panningSeparation(0),
pitchWheelDepth(0) {}
pitchWheelDepth(0)
{
}
int channels;
unsigned short lengthInPatterns;
@@ -66,201 +68,193 @@ public:
unsigned char pitchWheelDepth;
};
////////////////////////////////////////////////////////////////////////////////
// public members
////////////////////////////////////////////////////////////////////////////////
IT::AudioProperties::AudioProperties(AudioProperties::ReadStyle) :
TagLib::AudioProperties(),
IT::Properties::Properties(AudioProperties::ReadStyle propertiesStyle) :
AudioProperties(propertiesStyle),
d(new PropertiesPrivate())
{
}
IT::AudioProperties::~AudioProperties()
IT::Properties::~Properties()
{
delete d;
}
int IT::AudioProperties::length() const
int IT::Properties::length() const
{
return 0;
}
int IT::AudioProperties::lengthInSeconds() const
int IT::Properties::lengthInSeconds() const
{
return 0;
}
int IT::AudioProperties::lengthInMilliseconds() const
int IT::Properties::lengthInMilliseconds() const
{
return 0;
}
int IT::AudioProperties::bitrate() const
int IT::Properties::bitrate() const
{
return 0;
}
int IT::AudioProperties::sampleRate() const
int IT::Properties::sampleRate() const
{
return 0;
}
int IT::AudioProperties::channels() const
int IT::Properties::channels() const
{
return d->channels;
}
unsigned short IT::AudioProperties::lengthInPatterns() const
unsigned short IT::Properties::lengthInPatterns() const
{
return d->lengthInPatterns;
}
bool IT::AudioProperties::stereo() const
bool IT::Properties::stereo() const
{
return d->flags & Stereo;
}
unsigned short IT::AudioProperties::instrumentCount() const
unsigned short IT::Properties::instrumentCount() const
{
return d->instrumentCount;
}
unsigned short IT::AudioProperties::sampleCount() const
unsigned short IT::Properties::sampleCount() const
{
return d->sampleCount;
}
unsigned short IT::AudioProperties::patternCount() const
unsigned short IT::Properties::patternCount() const
{
return d->patternCount;
}
unsigned short IT::AudioProperties::version() const
unsigned short IT::Properties::version() const
{
return d->version;
}
unsigned short IT::AudioProperties::compatibleVersion() const
unsigned short IT::Properties::compatibleVersion() const
{
return d->compatibleVersion;
}
unsigned short IT::AudioProperties::flags() const
unsigned short IT::Properties::flags() const
{
return d->flags;
}
unsigned short IT::AudioProperties::special() const
unsigned short IT::Properties::special() const
{
return d->special;
}
unsigned char IT::AudioProperties::globalVolume() const
unsigned char IT::Properties::globalVolume() const
{
return d->globalVolume;
}
unsigned char IT::AudioProperties::mixVolume() const
unsigned char IT::Properties::mixVolume() const
{
return d->mixVolume;
}
unsigned char IT::AudioProperties::tempo() const
unsigned char IT::Properties::tempo() const
{
return d->tempo;
}
unsigned char IT::AudioProperties::bpmSpeed() const
unsigned char IT::Properties::bpmSpeed() const
{
return d->bpmSpeed;
}
unsigned char IT::AudioProperties::panningSeparation() const
unsigned char IT::Properties::panningSeparation() const
{
return d->panningSeparation;
}
unsigned char IT::AudioProperties::pitchWheelDepth() const
unsigned char IT::Properties::pitchWheelDepth() const
{
return d->pitchWheelDepth;
}
////////////////////////////////////////////////////////////////////////////////
// private members
////////////////////////////////////////////////////////////////////////////////
void IT::AudioProperties::setChannels(int channels)
void IT::Properties::setChannels(int channels)
{
d->channels = channels;
}
void IT::AudioProperties::setLengthInPatterns(unsigned short lengthInPatterns)
void IT::Properties::setLengthInPatterns(unsigned short lengthInPatterns)
{
d->lengthInPatterns = lengthInPatterns;
}
void IT::AudioProperties::setInstrumentCount(unsigned short instrumentCount)
void IT::Properties::setInstrumentCount(unsigned short instrumentCount)
{
d->instrumentCount = instrumentCount;
}
void IT::AudioProperties::setSampleCount(unsigned short sampleCount)
void IT::Properties::setSampleCount(unsigned short sampleCount)
{
d->sampleCount = sampleCount;
}
void IT::AudioProperties::setPatternCount(unsigned short patternCount)
void IT::Properties::setPatternCount(unsigned short patternCount)
{
d->patternCount = patternCount;
}
void IT::AudioProperties::setFlags(unsigned short flags)
void IT::Properties::setFlags(unsigned short flags)
{
d->flags = flags;
}
void IT::AudioProperties::setSpecial(unsigned short special)
void IT::Properties::setSpecial(unsigned short special)
{
d->special = special;
}
void IT::AudioProperties::setCompatibleVersion(unsigned short compatibleVersion)
void IT::Properties::setCompatibleVersion(unsigned short compatibleVersion)
{
d->compatibleVersion = compatibleVersion;
}
void IT::AudioProperties::setVersion(unsigned short version)
void IT::Properties::setVersion(unsigned short version)
{
d->version = version;
}
void IT::AudioProperties::setGlobalVolume(unsigned char globalVolume)
void IT::Properties::setGlobalVolume(unsigned char globalVolume)
{
d->globalVolume = globalVolume;
}
void IT::AudioProperties::setMixVolume(unsigned char mixVolume)
void IT::Properties::setMixVolume(unsigned char mixVolume)
{
d->mixVolume = mixVolume;
}
void IT::AudioProperties::setTempo(unsigned char tempo)
void IT::Properties::setTempo(unsigned char tempo)
{
d->tempo = tempo;
}
void IT::AudioProperties::setBpmSpeed(unsigned char bpmSpeed)
void IT::Properties::setBpmSpeed(unsigned char bpmSpeed)
{
d->bpmSpeed = bpmSpeed;
}
void IT::AudioProperties::setPanningSeparation(unsigned char panningSeparation)
void IT::Properties::setPanningSeparation(unsigned char panningSeparation)
{
d->panningSeparation = panningSeparation;
}
void IT::AudioProperties::setPitchWheelDepth(unsigned char pitchWheelDepth)
void IT::Properties::setPitchWheelDepth(unsigned char pitchWheelDepth)
{
d->pitchWheelDepth = pitchWheelDepth;
}

View File

@@ -30,15 +30,9 @@
#include "audioproperties.h"
namespace TagLib {
namespace IT {
class File;
class TAGLIB_EXPORT AudioProperties : public TagLib::AudioProperties
{
class TAGLIB_EXPORT Properties : public AudioProperties {
friend class File;
public:
/*! Flag bits. */
enum {
@@ -58,8 +52,8 @@ namespace TagLib {
MidiConfEmbedded = 8
};
explicit AudioProperties(AudioProperties::ReadStyle propertiesStyle);
virtual ~AudioProperties();
Properties(AudioProperties::ReadStyle propertiesStyle);
virtual ~Properties();
int length() const;
int lengthInSeconds() const;
@@ -84,7 +78,6 @@ namespace TagLib {
unsigned char panningSeparation() const;
unsigned char pitchWheelDepth() const;
private:
void setChannels(int channels);
void setLengthInPatterns(unsigned short lengthInPatterns);
void setInstrumentCount(unsigned short instrumentCount);
@@ -101,6 +94,10 @@ namespace TagLib {
void setPanningSeparation(unsigned char panningSeparation);
void setPitchWheelDepth (unsigned char pitchWheelDepth);
private:
Properties(const Properties&);
Properties &operator=(const Properties&);
class PropertiesPrivate;
PropertiesPrivate *d;
};

View File

@@ -36,13 +36,13 @@ using namespace Mod;
class Mod::File::FilePrivate
{
public:
explicit FilePrivate(AudioProperties::ReadStyle propertiesStyle)
FilePrivate(AudioProperties::ReadStyle propertiesStyle)
: properties(propertiesStyle)
{
}
Mod::Tag tag;
Mod::AudioProperties properties;
Mod::Properties properties;
};
Mod::File::File(FileName file, bool readProperties,
@@ -73,11 +73,21 @@ Mod::Tag *Mod::File::tag() const
return &d->tag;
}
Mod::AudioProperties *Mod::File::audioProperties() const
Mod::Properties *Mod::File::audioProperties() const
{
return &d->properties;
}
PropertyMap Mod::File::properties() const
{
return d->tag.properties();
}
PropertyMap Mod::File::setProperties(const PropertyMap &properties)
{
return d->tag.setProperties(properties);
}
bool Mod::File::save()
{
if(readOnly()) {
@@ -87,13 +97,13 @@ bool Mod::File::save()
seek(0);
writeString(d->tag.title(), 20);
StringList lines = d->tag.comment().split("\n");
size_t n = std::min<size_t>(lines.size(), d->properties.instrumentCount());
for(size_t i = 0; i < n; ++ i) {
unsigned int n = std::min(lines.size(), d->properties.instrumentCount());
for(unsigned int i = 0; i < n; ++ i) {
writeString(lines[i], 22);
seek(8, Current);
}
for(size_t i = n; i < d->properties.instrumentCount(); ++ i) {
for(unsigned int i = n; i < d->properties.instrumentCount(); ++ i) {
writeString(String(), 22);
seek(8, Current);
}

View File

@@ -72,11 +72,22 @@ namespace TagLib {
Mod::Tag *tag() const;
/*!
* Implements the unified property interface -- export function.
* Forwards to Mod::Tag::properties().
*/
PropertyMap properties() const;
/*!
* Implements the unified property interface -- import function.
* Forwards to Mod::Tag::setProperties().
*/
PropertyMap setProperties(const PropertyMap &);
/*!
* Returns the Mod::Properties for this file. If no audio properties
* were read then this will return a null pointer.
*/
Mod::AudioProperties *audioProperties() const;
Mod::Properties *audioProperties() const;
/*!
* Save the file.

View File

@@ -38,19 +38,20 @@ Mod::FileBase::FileBase(IOStream *stream) : TagLib::File(stream)
{
}
void Mod::FileBase::writeString(const String &s, unsigned int size, char padding)
void Mod::FileBase::writeString(const String &s, unsigned long size, char padding)
{
ByteVector data(s.data(String::Latin1));
data.resize(size, padding);
writeBlock(data);
}
bool Mod::FileBase::readString(String &s, unsigned int size)
bool Mod::FileBase::readString(String &s, unsigned long size)
{
ByteVector data(readBlock(size));
if(data.size() < size) return false;
const size_t index = data.find((char) 0);
if(index != ByteVector::npos()) {
int index = data.find(static_cast<char>(0));
if(index > -1)
{
data.resize(index);
}
data.replace('\xff', ' ');
@@ -67,22 +68,22 @@ void Mod::FileBase::writeByte(unsigned char byte)
void Mod::FileBase::writeU16L(unsigned short number)
{
writeBlock(ByteVector::fromUInt16LE(number));
writeBlock(ByteVector::fromShort(number, false));
}
void Mod::FileBase::writeU32L(unsigned int number)
void Mod::FileBase::writeU32L(unsigned long number)
{
writeBlock(ByteVector::fromUInt32LE(number));
writeBlock(ByteVector::fromUInt(number, false));
}
void Mod::FileBase::writeU16B(unsigned short number)
{
writeBlock(ByteVector::fromUInt16BE(number));
writeBlock(ByteVector::fromShort(number, true));
}
void Mod::FileBase::writeU32B(unsigned int number)
void Mod::FileBase::writeU32B(unsigned long number)
{
writeBlock(ByteVector::fromUInt32BE(number));
writeBlock(ByteVector::fromUInt(number, true));
}
bool Mod::FileBase::readByte(unsigned char &byte)
@@ -97,14 +98,14 @@ bool Mod::FileBase::readU16L(unsigned short &number)
{
ByteVector data(readBlock(2));
if(data.size() < 2) return false;
number = data.toUInt16LE(0);
number = data.toUShort(false);
return true;
}
bool Mod::FileBase::readU32L(unsigned int &number) {
bool Mod::FileBase::readU32L(unsigned long &number) {
ByteVector data(readBlock(4));
if(data.size() < 4) return false;
number = data.toUInt32LE(0);
number = data.toUInt(false);
return true;
}
@@ -112,13 +113,13 @@ bool Mod::FileBase::readU16B(unsigned short &number)
{
ByteVector data(readBlock(2));
if(data.size() < 2) return false;
number = data.toUInt16BE(0);
number = data.toUShort(true);
return true;
}
bool Mod::FileBase::readU32B(unsigned int &number) {
bool Mod::FileBase::readU32B(unsigned long &number) {
ByteVector data(readBlock(4));
if(data.size() < 4) return false;
number = data.toUInt32BE(0);
number = data.toUInt(true);
return true;
}

View File

@@ -41,22 +41,22 @@ namespace TagLib {
class TAGLIB_EXPORT FileBase : public TagLib::File
{
protected:
explicit FileBase(FileName file);
explicit FileBase(IOStream *stream);
FileBase(FileName file);
FileBase(IOStream *stream);
void writeString(const String &s, unsigned int size, char padding = 0);
void writeString(const String &s, unsigned long size, char padding = 0);
void writeByte(unsigned char byte);
void writeU16L(unsigned short number);
void writeU32L(unsigned int number);
void writeU32L(unsigned long number);
void writeU16B(unsigned short number);
void writeU32B(unsigned int number);
void writeU32B(unsigned long number);
bool readString(String &s, unsigned int size);
bool readString(String &s, unsigned long size);
bool readByte(unsigned char &byte);
bool readU16L(unsigned short &number);
bool readU32L(unsigned int &number);
bool readU32L(unsigned long &number);
bool readU16B(unsigned short &number);
bool readU32B(unsigned int &number);
bool readU32B(unsigned long &number);
};
}

View File

@@ -39,9 +39,9 @@
#define READ_BYTE(setter) READ(setter,unsigned char,readByte)
#define READ_U16L(setter) READ(setter,unsigned short,readU16L)
#define READ_U32L(setter) READ(setter,unsigned int,readU32L)
#define READ_U32L(setter) READ(setter,unsigned long,readU32L)
#define READ_U16B(setter) READ(setter,unsigned short,readU16B)
#define READ_U32B(setter) READ(setter,unsigned int,readU32B)
#define READ_U32B(setter) READ(setter,unsigned long,readU32B)
#define READ_STRING(setter,size) \
{ \
@@ -56,9 +56,9 @@
#define READ_BYTE_AS(name) READ_AS(unsigned char,name,readByte)
#define READ_U16L_AS(name) READ_AS(unsigned short,name,readU16L)
#define READ_U32L_AS(name) READ_AS(unsigned int,name,readU32L)
#define READ_U32L_AS(name) READ_AS(unsigned long,name,readU32L)
#define READ_U16B_AS(name) READ_AS(unsigned short,name,readU16B)
#define READ_U32B_AS(name) READ_AS(unsigned int,name,readU32B)
#define READ_U32B_AS(name) READ_AS(unsigned long,name,readU32B)
#define READ_STRING_AS(name,size) \
String name; \

View File

@@ -1,4 +1,4 @@
/***************************************************************************
/***************************************************************************
copyright : (C) 2011 by Mathias Panzenböck
email : grosser.meister.morti@gmx.net
***************************************************************************/
@@ -29,89 +29,83 @@
using namespace TagLib;
using namespace Mod;
class Mod::AudioProperties::PropertiesPrivate
class Mod::Properties::PropertiesPrivate
{
public:
PropertiesPrivate() :
channels(0),
instrumentCount(0),
lengthInPatterns(0) {}
lengthInPatterns(0)
{
}
int channels;
unsigned int instrumentCount;
unsigned char lengthInPatterns;
};
////////////////////////////////////////////////////////////////////////////////
// public members
////////////////////////////////////////////////////////////////////////////////
Mod::AudioProperties::AudioProperties(AudioProperties::ReadStyle) :
TagLib::AudioProperties(),
Mod::Properties::Properties(AudioProperties::ReadStyle propertiesStyle) :
AudioProperties(propertiesStyle),
d(new PropertiesPrivate())
{
}
Mod::AudioProperties::~AudioProperties()
Mod::Properties::~Properties()
{
delete d;
}
int Mod::AudioProperties::length() const
int Mod::Properties::length() const
{
return 0;
}
int Mod::AudioProperties::lengthInSeconds() const
int Mod::Properties::lengthInSeconds() const
{
return 0;
}
int Mod::AudioProperties::lengthInMilliseconds() const
int Mod::Properties::lengthInMilliseconds() const
{
return 0;
}
int Mod::AudioProperties::bitrate() const
int Mod::Properties::bitrate() const
{
return 0;
}
int Mod::AudioProperties::sampleRate() const
int Mod::Properties::sampleRate() const
{
return 0;
}
int Mod::AudioProperties::channels() const
int Mod::Properties::channels() const
{
return d->channels;
}
unsigned int Mod::AudioProperties::instrumentCount() const
unsigned int Mod::Properties::instrumentCount() const
{
return d->instrumentCount;
}
unsigned char Mod::AudioProperties::lengthInPatterns() const
unsigned char Mod::Properties::lengthInPatterns() const
{
return d->lengthInPatterns;
}
////////////////////////////////////////////////////////////////////////////////
// private members
////////////////////////////////////////////////////////////////////////////////
void Mod::AudioProperties::setChannels(int channels)
void Mod::Properties::setChannels(int channels)
{
d->channels = channels;
}
void Mod::AudioProperties::setInstrumentCount(unsigned int instrumentCount)
void Mod::Properties::setInstrumentCount(unsigned int instrumentCount)
{
d->instrumentCount = instrumentCount;
}
void Mod::AudioProperties::setLengthInPatterns(unsigned char lengthInPatterns)
void Mod::Properties::setLengthInPatterns(unsigned char lengthInPatterns)
{
d->lengthInPatterns = lengthInPatterns;
}

View File

@@ -33,15 +33,11 @@ namespace TagLib {
namespace Mod {
class File;
class TAGLIB_EXPORT AudioProperties : public TagLib::AudioProperties
class TAGLIB_EXPORT Properties : public AudioProperties
{
friend class File;
public:
explicit AudioProperties(AudioProperties::ReadStyle propertiesStyle);
virtual ~AudioProperties();
Properties(AudioProperties::ReadStyle propertiesStyle);
virtual ~Properties();
int length() const;
int lengthInSeconds() const;
@@ -53,12 +49,17 @@ namespace TagLib {
unsigned int instrumentCount() const;
unsigned char lengthInPatterns() const;
private:
void setChannels(int channels);
void setInstrumentCount(unsigned int sampleCount);
void setLengthInPatterns(unsigned char lengthInPatterns);
private:
friend class File;
Properties(const Properties&);
Properties &operator=(const Properties&);
class PropertiesPrivate;
PropertiesPrivate *d;
};

View File

@@ -27,7 +27,6 @@
#include "modtag.h"
#include "tstringlist.h"
#include "tpropertymap.h"
#include "tpicturemap.h"
using namespace TagLib;
using namespace Mod;
@@ -45,7 +44,6 @@ public:
};
Mod::Tag::Tag() :
TagLib::Tag(),
d(new TagPrivate())
{
}
@@ -90,11 +88,6 @@ unsigned int Mod::Tag::track() const
return 0;
}
TagLib::PictureMap Mod::Tag::pictures() const
{
return PictureMap();
}
String Mod::Tag::trackerName() const
{
return d->trackerName;
@@ -130,10 +123,6 @@ void Mod::Tag::setTrack(unsigned int)
{
}
void Mod::Tag::setPictures(const PictureMap &l)
{
}
void Mod::Tag::setTrackerName(const String &trackerName)
{
d->trackerName = trackerName;

View File

@@ -88,8 +88,6 @@ namespace TagLib {
*/
virtual unsigned int track() const;
PictureMap pictures() const;
/*!
* Returns the name of the tracker used to create/edit the module file.
* Only XM files store this tag to the file as such, for other formats
@@ -153,8 +151,6 @@ namespace TagLib {
*/
virtual void setTrack(unsigned int track);
void setPictures(const PictureMap &l);
/*!
* Sets the tracker name to \a trackerName. If \a trackerName is
* String::null then this value will be cleared.

View File

@@ -23,17 +23,18 @@
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#include <climits>
#include <tdebug.h>
#include <tstring.h>
#include "mp4atom.h"
using namespace TagLib;
const char *MP4::Atom::containers[11] = {
"moov", "udta", "mdia", "meta", "ilst",
"stbl", "minf", "moof", "traf", "trak",
"stsd"
const char *const MP4::Atom::containers[11] = {
"moov", "udta", "mdia", "meta", "ilst",
"stbl", "minf", "moof", "traf", "trak",
"stsd"
};
MP4::Atom::Atom(File *file)
@@ -51,7 +52,7 @@ MP4::Atom::Atom(File *file)
return;
}
length = header.toUInt32BE(0);
length = header.toUInt();
if(length == 0) {
// The last atom which extends to the end of the file.
@@ -59,7 +60,17 @@ MP4::Atom::Atom(File *file)
}
else if(length == 1) {
// The atom has a 64-bit length.
length = file->readBlock(8).toInt64BE(0);
const long long longLength = file->readBlock(8).toLongLong();
if(longLength <= LONG_MAX) {
// The actual length fits in long. That's always the case if long is 64-bit.
length = static_cast<long>(longLength);
}
else {
debug("MP4: 64-bit atoms are not supported");
length = 0;
file->seek(0, File::End);
return;
}
}
if(length < 8) {
@@ -74,7 +85,25 @@ MP4::Atom::Atom(File *file)
for(int i = 0; i < numContainers; i++) {
if(name == containers[i]) {
if(name == "meta") {
file->seek(4, File::Current);
long posAfterMeta = file->tell();
ByteVector nextSize = file->readBlock(8).mid(4, 4);
static const char *const metaChildrenNames[] = {
"hdlr", "ilst", "mhdr", "ctry", "lang"
};
bool metaIsFullAtom = true;
for(size_t j = 0;
j < sizeof(metaChildrenNames) / sizeof(metaChildrenNames[0]);
++j) {
if(nextSize == metaChildrenNames[j]) {
// meta is not a full atom (i.e. not followed by version, flags). It
// is followed by the size and type of the first child atom.
metaIsFullAtom = false;
break;
}
}
// Only skip next four bytes, which contain version and flags, if meta
// is a full atom.
file->seek(posAfterMeta + (metaIsFullAtom ? 4 : 0));
}
else if(name == "stsd") {
file->seek(8, File::Current);
@@ -145,7 +174,7 @@ MP4::Atoms::Atoms(File *file)
atoms.setAutoDelete(true);
file->seek(0, File::End);
long long end = file->tell();
long end = file->tell();
file->seek(0);
while(file->tell() + 8 <= end) {
MP4::Atom *atom = new MP4::Atom(file);

View File

@@ -74,28 +74,28 @@ namespace TagLib {
typedef TagLib::List<AtomData> AtomDataList;
class Atom
class TAGLIB_EXPORT Atom
{
public:
explicit Atom(File *file);
Atom(File *file);
~Atom();
Atom *find(const char *name1, const char *name2 = 0, const char *name3 = 0, const char *name4 = 0);
bool path(AtomList &path, const char *name1, const char *name2 = 0, const char *name3 = 0);
AtomList findall(const char *name, bool recursive = false);
long long offset;
long long length;
long offset;
long length;
TagLib::ByteVector name;
AtomList children;
private:
static const int numContainers = 11;
static const char *containers[11];
static const char *const containers[11];
};
//! Root-level atoms
class Atoms
class TAGLIB_EXPORT Atoms
{
public:
explicit Atoms(File *file);
Atoms(File *file);
~Atoms();
Atom *find(const char *name1, const char *name2 = 0, const char *name3 = 0, const char *name4 = 0);
AtomList path(const char *name1, const char *name2 = 0, const char *name3 = 0, const char *name4 = 0);

View File

@@ -23,33 +23,21 @@
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#include "taglib.h"
#include "tdebug.h"
#include "tsmartptr.h"
#include <taglib.h>
#include <tdebug.h>
#include "trefcounter.h"
#include "mp4coverart.h"
using namespace TagLib;
namespace
{
struct CoverArtData
{
MP4::CoverArt::Format format;
ByteVector data;
};
}
class MP4::CoverArt::CoverArtPrivate
class MP4::CoverArt::CoverArtPrivate : public RefCounter
{
public:
CoverArtPrivate(Format f, const ByteVector &v) :
data(new CoverArtData())
{
data->format = f;
data->data = v;
}
CoverArtPrivate() :
format(MP4::CoverArt::JPEG) {}
SHARED_PTR<CoverArtData> data;
Format format;
ByteVector data;
};
////////////////////////////////////////////////////////////////////////////////
@@ -57,13 +45,16 @@ public:
////////////////////////////////////////////////////////////////////////////////
MP4::CoverArt::CoverArt(Format format, const ByteVector &data) :
d(new CoverArtPrivate(format, data))
d(new CoverArtPrivate())
{
d->format = format;
d->data = data;
}
MP4::CoverArt::CoverArt(const CoverArt &item) :
d(new CoverArtPrivate(*item.d))
d(item.d)
{
d->ref();
}
MP4::CoverArt &
@@ -83,17 +74,19 @@ MP4::CoverArt::swap(CoverArt &item)
MP4::CoverArt::~CoverArt()
{
delete d;
if(d->deref()) {
delete d;
}
}
MP4::CoverArt::Format
MP4::CoverArt::format() const
{
return d->data->format;
return d->format;
}
ByteVector
MP4::CoverArt::data() const
{
return d->data->data;
return d->data;
}

View File

@@ -76,7 +76,9 @@ namespace TagLib {
};
typedef List<CoverArt> CoverArtList;
}
}
#endif

View File

@@ -49,7 +49,7 @@ namespace
return true;
}
}
} // namespace
class MP4::File::FilePrivate
{
@@ -66,9 +66,9 @@ public:
delete properties;
}
MP4::Tag *tag;
MP4::Atoms *atoms;
MP4::AudioProperties *properties;
MP4::Tag *tag;
MP4::Atoms *atoms;
MP4::Properties *properties;
};
////////////////////////////////////////////////////////////////////////////////
@@ -114,7 +114,22 @@ MP4::File::tag() const
return d->tag;
}
MP4::AudioProperties *
PropertyMap MP4::File::properties() const
{
return d->tag->properties();
}
void MP4::File::removeUnsupportedProperties(const StringList &properties)
{
d->tag->removeUnsupportedProperties(properties);
}
PropertyMap MP4::File::setProperties(const PropertyMap &properties)
{
return d->tag->setProperties(properties);
}
MP4::Properties *
MP4::File::audioProperties() const
{
return d->properties;
@@ -140,7 +155,7 @@ MP4::File::read(bool readProperties)
d->tag = new Tag(this, d->atoms);
if(readProperties) {
d->properties = new AudioProperties(this, d->atoms);
d->properties = new Properties(this, d->atoms);
}
}
@@ -160,6 +175,26 @@ MP4::File::save()
return d->tag->save();
}
bool
MP4::File::strip(int tags)
{
if(readOnly()) {
debug("MP4::File::strip() - Cannot strip tags from a read only file.");
return false;
}
if(!isValid()) {
debug("MP4::File::strip() -- Cannot strip tags from an invalid file.");
return false;
}
if(tags & MP4) {
return d->tag->strip();
}
return true;
}
bool
MP4::File::hasMP4Tag() const
{

View File

@@ -49,25 +49,38 @@ namespace TagLib {
{
public:
/*!
* Constructs an MP4 file from \a file. If \a readProperties is true the
* file's audio properties will also be read.
*
* \note In the current implementation, \a propertiesStyle is ignored.
* This set of flags is used for strip() and is suitable for
* being OR-ed together.
*/
File(FileName file, bool readProperties = true,
AudioProperties::ReadStyle audioPropertiesStyle = AudioProperties::Average);
enum TagTypes {
//! Empty set. Matches no tag types.
NoTags = 0x0000,
//! Matches MP4 tags.
MP4 = 0x0001,
//! Matches all tag types.
AllTags = 0xffff
};
/*!
* Constructs an MP4 file from \a file. If \a readProperties is true the
* file's audio properties will also be read.
*
* \note In the current implementation, \a propertiesStyle is ignored.
*/
File(FileName file, bool readProperties = true,
Properties::ReadStyle audioPropertiesStyle = Properties::Average);
/*!
* Constructs an MP4 file from \a stream. If \a readProperties is 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.
*
* \note In the current implementation, \a propertiesStyle is ignored.
*/
File(IOStream *stream, bool readProperties = true,
AudioProperties::ReadStyle audioPropertiesStyle = AudioProperties::Average);
Properties::ReadStyle audioPropertiesStyle = Properties::Average);
/*!
* Destroys this instance of the File.
@@ -86,10 +99,26 @@ namespace TagLib {
*/
Tag *tag() const;
/*!
* Implements the unified property interface -- export function.
*/
PropertyMap properties() const;
/*!
* Removes unsupported properties. Forwards to the actual Tag's
* removeUnsupportedProperties() function.
*/
void removeUnsupportedProperties(const StringList &properties);
/*!
* Implements the unified property interface -- import function.
*/
PropertyMap setProperties(const PropertyMap &);
/*!
* Returns the MP4 audio properties for this file.
*/
AudioProperties *audioProperties() const;
Properties *audioProperties() const;
/*!
* Save the file.
@@ -98,6 +127,15 @@ namespace TagLib {
*/
bool save();
/*!
* This will strip the tags that match the OR-ed together TagTypes from the
* file. By default it strips all tags. It returns true if the tags are
* successfully stripped.
*
* \note This will update the file immediately.
*/
bool strip(int tags = AllTags);
/*!
* Returns whether or not the file on disk actually has an MP4 tag, or the
* file has a Metadata Item List (ilst) atom.

View File

@@ -23,58 +23,45 @@
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#include "taglib.h"
#include "tdebug.h"
#include "tsmartptr.h"
#include <taglib.h>
#include <tdebug.h>
#include "trefcounter.h"
#include "mp4item.h"
#include "tutils.h"
using namespace TagLib;
namespace
{
struct ItemData
{
bool valid;
MP4::AtomDataType atomDataType;
MP4::Item::ItemType type;
union {
bool m_bool;
int m_int;
MP4::Item::IntPair m_intPair;
unsigned char m_byte;
unsigned int m_uint;
long long m_longlong;
};
StringList m_stringList;
ByteVectorList m_byteVectorList;
MP4::CoverArtList m_coverArtList;
};
}
class MP4::Item::ItemPrivate
class MP4::Item::ItemPrivate : public RefCounter
{
public:
ItemPrivate() :
data(new ItemData())
{
data->valid = true;
data->atomDataType = MP4::TypeUndefined;
data->type = MP4::Item::TypeUndefined;
}
valid(true),
atomDataType(TypeUndefined) {}
SHARED_PTR<ItemData> data;
bool valid;
AtomDataType atomDataType;
union {
bool m_bool;
int m_int;
IntPair m_intPair;
unsigned char m_byte;
unsigned int m_uint;
long long m_longlong;
};
StringList m_stringList;
ByteVectorList m_byteVectorList;
MP4::CoverArtList m_coverArtList;
};
MP4::Item::Item() :
d(new ItemPrivate())
{
d->data->valid = false;
d->valid = false;
}
MP4::Item::Item(const Item &item) :
d(new ItemPrivate(*item.d))
d(item.d)
{
d->ref();
}
MP4::Item &
@@ -94,176 +81,131 @@ MP4::Item::swap(Item &item)
MP4::Item::~Item()
{
delete d;
if(d->deref())
delete d;
}
MP4::Item::Item(bool value) :
d(new ItemPrivate())
{
d->data->m_bool = value;
d->data->type = TypeBool;
d->m_bool = value;
}
MP4::Item::Item(int value) :
d(new ItemPrivate())
{
d->data->m_int = value;
d->data->type = TypeInt;
d->m_int = value;
}
MP4::Item::Item(unsigned char value) :
d(new ItemPrivate())
{
d->data->m_byte = value;
d->data->type = TypeByte;
d->m_byte = value;
}
MP4::Item::Item(unsigned int value) :
d(new ItemPrivate())
{
d->data->m_uint = value;
d->data->type = TypeUInt;
d->m_uint = value;
}
MP4::Item::Item(long long value) :
d(new ItemPrivate())
{
d->data->m_longlong = value;
d->data->type = TypeLongLong;
d->m_longlong = value;
}
MP4::Item::Item(int value1, int value2) :
d(new ItemPrivate())
{
d->data->m_intPair.first = value1;
d->data->m_intPair.second = value2;
d->data->type = TypeIntPair;
d->m_intPair.first = value1;
d->m_intPair.second = value2;
}
MP4::Item::Item(const ByteVectorList &value) :
d(new ItemPrivate())
{
d->data->m_byteVectorList = value;
d->data->type = TypeByteVectorList;
d->m_byteVectorList = value;
}
MP4::Item::Item(const StringList &value) :
d(new ItemPrivate())
{
d->data->m_stringList = value;
d->data->type = TypeStringList;
d->m_stringList = value;
}
MP4::Item::Item(const MP4::CoverArtList &value) :
d(new ItemPrivate())
{
d->data->m_coverArtList = value;
d->data->type = TypeCoverArtList;
d->m_coverArtList = value;
}
void MP4::Item::setAtomDataType(MP4::AtomDataType type)
{
d->data->atomDataType = type;
d->atomDataType = type;
}
MP4::AtomDataType MP4::Item::atomDataType() const
{
return d->data->atomDataType;
return d->atomDataType;
}
bool
MP4::Item::toBool() const
{
return d->data->m_bool;
return d->m_bool;
}
int
MP4::Item::toInt() const
{
return d->data->m_int;
return d->m_int;
}
unsigned char
MP4::Item::toByte() const
{
return d->data->m_byte;
return d->m_byte;
}
unsigned int
MP4::Item::toUInt() const
{
return d->data->m_uint;
return d->m_uint;
}
long long
MP4::Item::toLongLong() const
{
return d->data->m_longlong;
return d->m_longlong;
}
MP4::Item::IntPair
MP4::Item::toIntPair() const
{
return d->data->m_intPair;
return d->m_intPair;
}
StringList
MP4::Item::toStringList() const
{
return d->data->m_stringList;
return d->m_stringList;
}
ByteVectorList
MP4::Item::toByteVectorList() const
{
return d->data->m_byteVectorList;
return d->m_byteVectorList;
}
MP4::CoverArtList
MP4::Item::toCoverArtList() const
{
return d->data->m_coverArtList;
return d->m_coverArtList;
}
bool
MP4::Item::isValid() const
{
return d->data->valid;
}
String
MP4::Item::toString() const
{
StringList desc;
switch (d->data->type) {
case TypeBool:
return d->data->m_bool ? "true" : "false";
case TypeInt:
return Utils::formatString("%d", d->data->m_int);
case TypeIntPair:
return Utils::formatString("%d/%d", d->data->m_intPair.first, d->data->m_intPair.second);
case TypeByte:
return Utils::formatString("%d", d->data->m_byte);
case TypeUInt:
return Utils::formatString("%u", d->data->m_uint);
case TypeLongLong:
return Utils::formatString("%lld", d->data->m_longlong);
case TypeStringList:
return d->data->m_stringList.toString(" / ");
case TypeByteVectorList:
for(size_t i = 0; i < d->data->m_byteVectorList.size(); i++) {
desc.append(Utils::formatString(
"[%d bytes of data]", static_cast<int>(d->data->m_byteVectorList[i].size())));
}
return desc.toString(", ");
case TypeCoverArtList:
for(size_t i = 0; i < d->data->m_coverArtList.size(); i++) {
desc.append(Utils::formatString(
"[%d bytes of data]", static_cast<int>(d->data->m_coverArtList[i].data().size())));
}
return desc.toString(", ");
case TypeUndefined:
return "[unknown]";
}
return String();
return d->valid;
}

View File

@@ -41,19 +41,6 @@ namespace TagLib {
int first, second;
};
enum ItemType {
TypeUndefined = 0,
TypeBool,
TypeInt,
TypeIntPair,
TypeByte,
TypeUInt,
TypeLongLong,
TypeStringList,
TypeByteVectorList,
TypeCoverArtList,
};
Item();
Item(const Item &item);
@@ -92,12 +79,8 @@ namespace TagLib {
ByteVectorList toByteVectorList() const;
CoverArtList toCoverArtList() const;
ItemType type() const;
bool isValid() const;
String toString() const;
private:
class ItemPrivate;
ItemPrivate *d;

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