767 Commits

Author SHA1 Message Date
Tsuda Kageyu
e36a9cabb9 Update NEWS. 2016-10-24 12:03:23 +09:00
Tsuda Kageyu
597dcde72a Update the version to v1.11.1. 2016-10-22 02:45:52 +09:00
Tsuda Kageyu
6a96a6426a Replace a possibly non-free file in the test suite. 2016-10-22 02:11:16 +09:00
Tsuda Kageyu
69c65284a5 Update NEWS. 2016-10-22 01:06:57 +09:00
Tsuda Kageyu
97aaa0f979 Restore the ABI breakage by bringing back a removed private static variable. 2016-10-19 15:57:28 +09:00
Tsuda Kageyu
0dac721ce2 Update NEWS. 2016-09-26 17:48:44 +09:00
Stephen F. Booth
bbeeca6fdb Merge pull request #754 from hyperquantum/master
Fix defect in ByteVectorStream::seek when Position==End.
2016-09-15 21:11:08 -04:00
Kevin André
7e90313690 Fix defect in ByteVectorStream::seek when Position==End. 2016-09-15 16:30:16 +02:00
Tsuda Kageyu
1d3c95f692 Merge pull request #752 from evpobr/cmake-ver
Move cmake_minimum_required at the top
2016-09-12 15:56:45 +09:00
Tsuda Kageyu
8c3801f18d Merge pull request #753 from FestusHagen/fh1.m_AddBuildSharedLibsOption
Add BUILD_SHARED_LIBS option for CMake GUI.
2016-09-12 15:56:33 +09:00
Festus Hagen
c9bdd416ef Add BUILD_SHARED_LIBS option for CMake GUI. 2016-08-25 15:03:44 -04:00
evpobr
9f28e037fe Move cmake_minimum_required at the top 2016-08-20 22:37:53 +05:00
Stephen F. Booth
92c070ba9e Merge pull request #749 from jwelton/fix-typo
Fix Typo
2016-08-14 17:10:18 -04:00
Jake Welton
75e3ec73aa Change string to end offset 2016-08-14 22:02:33 +01:00
Tsuda Kageyu
3e47a036fb Update NEWS. 2016-05-14 10:46:42 +09:00
Tsuda Kageyu
9b995544e4 Fix reading table of contents frames with a lot of children. 2016-05-14 09:58:19 +09:00
Tsuda Kageyu
d8e5077961 Update NEWS. 2016-04-29 17:26:33 +09:00
Tsuda Kageyu
6422054540 Merge pull request #739 from frgm/master
c: fix a typo
2016-04-23 06:54:59 +09:00
Svyatoslav Mishyn
1b878102f0 c: fix a typo
readble => readable
2016-04-22 16:46:15 +03:00
Tsuda Kageyu
0a85f9b227 Revert "Hide a private static variable."
This reverts commit 25ffbcb4b9.

# Conflicts:
#	taglib/mpeg/id3v2/id3v2framefactory.cpp
#	taglib/mpeg/id3v2/id3v2framefactory.h
2016-04-22 09:27:04 +09:00
Stephen F. Booth
31f3109b47 Merge pull request #732 from dirkvdb/master
Also ignore the virtual destructor warning when compiling with clang
2016-04-02 13:21:39 -04:00
Dirk Vanden Boer
76f8ff388f Also ignore the virtual destructor warning when compiling with clang 2016-03-20 20:33:04 +01:00
Tsuda Kageyu
7627ae48ed Change some static_casts to dynamic_casts in test_id3v2.cpp.
static_casts are unsafe and some of following tests doesn't work well with them.
2016-03-17 22:50:34 +09:00
Tsuda Kageyu
b2a6768704 Don't stop parsing an ID3v2 SYLT frame when its description is empty. 2016-03-14 20:35:09 +09:00
Tsuda Kageyu
7d270a7e20 Update NEWS. 2016-03-10 21:38:08 +09:00
Tsuda Kageyu
bf53dc6131 Merge pull request #726 from amethystAnt/ape-keys-bug
Fixed bug #725 - changed the maximum length of an APE key to 255
2016-03-07 06:41:21 +09:00
Karel Patlejch
ff8b6a91e7 Fixed bug #725 - changed the maximum length of an APE key to 255 2016-03-06 21:15:18 +01:00
Tsuda Kageyu
1a82419872 Add myself to maintainers. 2016-03-03 10:06:51 +09:00
Tsuda Kageyu
5a3265d031 Update NEWS for v1.11 BETA 2. 2016-03-03 05:02:14 +09:00
Tsuda Kageyu
a27199b772 Support Boost iostreams library to decode compressed ID3v2 frames in additiion to zlib.
This will help Windows users build TagLib without zlib source.
2016-03-02 11:14:11 +09:00
Tsuda Kageyu
6c27a32de8 Skip a useless debug message. 2016-02-24 21:32:25 +09:00
Tsuda Kageyu
11abffc0f6 Update NEWS. 2016-02-22 23:27:46 +09:00
Tsuda Kageyu
2aea23aed2 Add some debug messages to RIFF::File, just in case. 2016-02-22 23:17:17 +09:00
Tsuda Kageyu
e8ef0e0a4b Improve the padding handling of RIFF files a bit. 2016-02-22 23:12:34 +09:00
Tsuda Kageyu
520da50bc5 Always update the global RIFF size when updating RIFF files. 2016-02-22 22:27:18 +09:00
Tsuda Kageyu
c0a909b709 Fix a broken indent. 2016-02-22 01:39:40 +09:00
Tsuda Kageyu
8d09f12847 Remove some #ifdefs and #includes no longer used. 2016-02-22 00:35:11 +09:00
Tsuda Kageyu
710166e32d Separate zlib related code rather than having several #ifdef blocks. 2016-02-21 23:16:48 +09:00
Tsuda Kageyu
01054009ac Make the code in ByteVector::append() a bit more clearer. 2016-02-21 00:43:33 +09:00
Tsuda Kageyu
7d8aa7b8bd ByteVector::append() can't take the vector itself. 2016-02-20 19:42:46 +09:00
Tsuda Kageyu
98a57744c3 Add some tests for ByteVector::replace(). 2016-02-20 11:50:47 +09:00
Tsuda Kageyu
8c4b484e67 Fix a possible out-of-bounds access in SynchData::decode().
This also avoids an extra memory copy.
2016-02-19 20:58:09 +09:00
Tsuda Kageyu
2166d703e0 Small fix in style. 2016-02-18 14:34:16 +09:00
Tsuda Kageyu
887f3b28e5 Add a TODO comment in ByteVector::replace(). 2016-02-18 14:29:17 +09:00
Tsuda Kageyu
6a9db50c03 Revert "Remove more useless inline specifiers."
This reverts commit dadfe79799.
2016-02-18 03:51:42 +09:00
Tsuda Kageyu
07d95e0dc0 Add an overload of ByteVector::replace() which takes chars.
Currently, this is only way of using ByteVector::replace().
2016-02-18 03:47:02 +09:00
Tsuda Kageyu
a9acca5d81 Decode unsynchronized ID3v2 frames efficiently.
It makes a great difference when decoding huge unsynchronized ID3v2 frames.
2016-02-18 03:07:38 +09:00
Tsuda Kageyu
dadfe79799 Remove more useless inline specifiers.
They are no longer needed since the unnamed namespaces have solved the ODR violation.
2016-02-17 22:06:36 +09:00
Tsuda Kageyu
552185a8b8 Cast the parameters of boost::endian::endian_reverse(), just in case. 2016-02-16 20:35:27 +09:00
Tsuda Kageyu
46eacaeba4 Inline functions had better have internal linkages.
This also removes useless inline specifiers.
2016-02-15 20:53:27 +09:00
Tsuda Kageyu
455e827e1e Explicitly set a false flag in ConfigureChecks.cmake. 2016-02-15 02:18:42 +09:00
Tsuda Kageyu
c08e0b1357 Reorganize some redundant or missing tests. 2016-02-15 01:49:23 +09:00
Tsuda Kageyu
f3016c0892 Enable Boost Endian library again.
It should be detected properly now.
2016-02-15 01:36:17 +09:00
Tsuda Kageyu
f8f7cb0904 Update NEWS. 2016-02-15 00:26:41 +09:00
Tsuda Kageyu
d037b8c908 Make sure we don't write UTF8 or UTF16BE to ID3v2.3 tags. 2016-02-15 00:22:18 +09:00
Tsuda Kageyu
c349ba3a31 Add OS X settings to travis.yml. 2016-02-14 03:29:03 +09:00
Tsuda Kageyu
bb006e41d7 Disable Boost Endian library for now.
It's not detected properly depending on the environment.
2016-02-14 03:17:37 +09:00
Tsuda Kageyu
ab2267f9aa Find Boost properly.
check_cxx_source_compiles() depends on the Visual Studio settings.
2016-02-14 01:53:02 +09:00
Tsuda Kageyu
73662037eb Merge branch 'master' of https://github.com/taglib/taglib 2016-02-11 20:53:22 +09:00
Tsuda Kageyu
aa339eba87 Fix a wrong test for base64 decoding. 2016-02-11 20:47:55 +09:00
Scott Wheeler
45a3bdb695 Properly mark functions as virtual 2016-02-10 11:08:46 +00:00
Tsuda Kageyu
5a155ef4ce Fix memory leaks when saving Ogg files. 2016-02-07 04:46:40 +09:00
Tsuda Kageyu
184a151d41 Add some missing FileRef tests. 2016-02-06 21:56:51 +09:00
Tsuda Kageyu
cc2ccbc20c Test if FileRef chooses a correct File type. 2016-02-06 21:32:03 +09:00
Tsuda Kageyu
fa46185ca4 Add a supplementary note to a comment. 2016-02-06 21:14:12 +09:00
Tsuda Kageyu
fffff35ca5 Correct the license information. 2016-02-06 01:27:36 +09:00
Tsuda Kageyu
31894f47f6 Fix another typo in NEWS. 2016-02-05 19:57:09 +09:00
Tsuda Kageyu
c1e3d65b68 Fix a typo in NEWS 2016-02-05 10:53:56 +09:00
Tsuda Kageyu
a8ecbbaef4 Update NEWS. 2016-02-03 20:33:13 +09:00
Tsuda Kageyu
24575aab23 Remove strnlen() since some compilers lack it. 2016-02-03 20:21:04 +09:00
Tsuda Kageyu
c04b24a2f5 More efficient handling of broken APE item keys.
This also improves the performance when handling intact APE items.
2016-02-03 01:08:16 +09:00
Tsuda Kageyu
013fbbf22c APE::Tag::addValue() may append a string to non-text items. 2016-02-02 00:42:08 +09:00
Tsuda Kageyu
5350bc8501 Oops! We already have a function to check APE item keys. 2016-02-01 22:46:08 +09:00
Tsuda Kageyu
92a1a00624 APE item keys should be ASCII between 0x20 and 0x7E, not UTF-8. 2016-02-01 22:19:43 +09:00
Tsuda Kageyu
8afbf6c92a Update NEWS. 2016-01-30 11:13:32 +09:00
Tsuda Kageyu
9976155aa9 Ignore 'fact' chunk of WAV files if their format is PCM.
TagLib reports wrong length of some PCM files with a 'fact' chunk.
2016-01-30 00:51:28 +09:00
Tsuda Kageyu
c2cb9ab8b0 Update NEWS with the specific release date. 2016-01-28 13:31:22 +09:00
Tsuda Kageyu
758b7e39ce Update the version to v1.11. 2016-01-28 13:17:56 +09:00
Tsuda Kageyu
4ba7bb5a8a Reorganize NEWS to put new features first. 2016-01-28 12:13:18 +09:00
Tsuda Kageyu
0b62ba1530 Remove the body of deprecated function Ogg::Page::getCopyWithNewPageSequenceNumber(). 2016-01-28 12:00:27 +09:00
Tsuda Kageyu
fe92f3dffe Add a test for broken MPEG audio frames. 2016-01-09 13:30:00 +09:00
Tsuda Kageyu
b8e82a7775 Amend an outdated comment. 2016-01-09 10:15:42 +09:00
Tsuda Kageyu
3dc873b7d0 Check if two consecutive MPEG audio frames are consistent.
This fixes reading the audio properties of some MP3 files reported by a Kodi user.
This is basically the same check as FFmpeg does.
2016-01-08 20:08:04 +09:00
Tsuda Kageyu
1254534ed4 Another workaround for broken MPEG headers. 2016-01-08 02:30:17 +09:00
Tsuda Kageyu
7a5fb7d672 Revert some unnecessary changes. 2016-01-08 02:22:44 +09:00
Tsuda Kageyu
5b7ee5bc1a Initialize all the private data members. 2016-01-08 01:57:18 +09:00
Tsuda Kageyu
815d1583e9 Avoid repeating insert() operations in Ogg::File. 2016-01-06 17:57:37 +09:00
Tsuda Kageyu
6c183a7df8 Update NEWS. 2016-01-06 17:43:32 +09:00
Tsuda Kageyu
8d5f821b16 Merge pull request #689 from TsudaKageyu/save-ogg
Fix a segfault when saving an Ogg file repeatedly.
2016-01-06 09:46:35 +09:00
Tsuda Kageyu
0611744171 Merge pull request #706 from TwoFX/master
Remove "FORCE" in CMake path specifications to allow installation to a non-standard directory structure
2016-01-05 13:20:28 +09:00
Markus Himmel
c25e0c6ead Remove "FORCE" in CMake path specifications to allow installation to a
non-standard directory structure.
2015-12-29 10:29:58 +01:00
Tsuda Kageyu
45762d20f1 Fix a silly mistake. 2015-12-25 16:52:26 +09:00
Tsuda Kageyu
67434aa7b4 Merge pull request #705 from TsudaKageyu/mpeg-invalid-frame
More robust checks for invalid MPEG frame headers. (again)
2015-12-25 08:58:06 +09:00
Tsuda Kageyu
cb23f91c98 Merge branch 'master' of https://github.com/dukeyin/taglib into dukeyin-master
# Conflicts:
#	tests/test_id3v2.cpp
2015-12-24 13:47:55 +09:00
Tsuda Kageyu
37c4d0b11d Update NEWS. 2015-12-24 09:43:21 +09:00
Tsuda Kageyu
626d13467e Merge pull request #701 from TsudaKageyu/flac-strip
Enable FLAC::File to remove non-standard tags.
2015-12-24 09:41:48 +09:00
Tsuda Kageyu
02eac18d00 Fix a segfault when saving an Ogg file repeatedly.
This also reduces memory usage when reading/writing Ogg files.
Especially, it stops holding an entire file when renumbering Ogg pages.
2015-12-24 08:58:56 +09:00
Tsuda Kageyu
4064b34eff A bit more tolerant check for the MPEG frame length. 2015-12-22 20:39:58 +09:00
Tsuda Kageyu
22708a0af6 Add some supplementary comments. 2015-12-22 17:06:40 +09:00
Tsuda Kageyu
12da0ebd6d Amend a vague comment and debug message. 2015-12-22 15:18:53 +09:00
Tsuda Kageyu
d13a3e3e16 Update NEWS. 2015-12-22 15:11:21 +09:00
Tsuda Kageyu
e8b28a0466 Merge pull request #703 from inizan-yannick/master
Fix .pc file configuration.
2015-12-22 15:07:27 +09:00
Tsuda Kageyu
081d6eaf76 More robust checks for invalid MPEG frame headers. (again) 2015-12-22 14:57:23 +09:00
Tsuda Kageyu
ac361c7692 Unify some duplicate internal functions. 2015-12-22 11:49:55 +09:00
Yannick Inizan
d7de3983d9 Fix .pc file configuration. 2015-12-21 22:25:49 +01:00
Tsuda Kageyu
1aca1f64fe Enable FLAC::File to remove non-standard tags. 2015-12-21 15:42:41 +09:00
Tsuda Kageyu
5a1f44d166 Avoid the risk of subtracting between signed and unsigned types. 2015-12-21 14:29:59 +09:00
Tsuda Kageyu
130654316b Backport some comments from tablib2 branch. 2015-12-21 13:54:02 +09:00
Tsuda Kageyu
5cd139a578 Small fix in style. 2015-12-21 13:34:48 +09:00
Tsuda Kageyu
dd2ed47703 Avoid an implicit const cast. 2015-12-21 13:06:43 +09:00
Tsuda Kageyu
d0238ba82f Avoid the risk of subtracting between signed and unsigned types. 2015-12-21 11:44:25 +09:00
Tsuda Kageyu
cf5d431d77 Remove an unused private data member. 2015-12-21 10:49:46 +09:00
Tsuda Kageyu
4ec264b6a1 Update NEWS. 2015-12-21 10:05:05 +09:00
Tsuda Kageyu
ef92ee223f Merge pull request #699 from TsudaKageyu/flac-duplicate-comments
Remove duplicate Vorbis comment blocks when saving a FLAC file.
2015-12-21 10:03:14 +09:00
Tsuda Kageyu
8b6bcf411a Update NEWS. 2015-12-18 13:54:47 +09:00
Tsuda Kageyu
aa66b0651d Merge pull request #688 from TsudaKageyu/save-wv
Fix saving WavPack files.
2015-12-18 13:52:54 +09:00
Tsuda Kageyu
7c5d1e02de Update NEWS. 2015-12-18 09:03:31 +09:00
Tsuda Kageyu
6d27a1013d Merge pull request #687 from TsudaKageyu/save-mpc
Fix saving MPC files.
2015-12-18 09:00:45 +09:00
Tsuda Kageyu
50711f4758 Update NEWS. 2015-12-17 16:59:28 +09:00
Tsuda Kageyu
6998be94f6 Merge pull request #686 from TsudaKageyu/save-ape
Fix saving APE files.
2015-12-17 16:57:24 +09:00
Tsuda Kageyu
862952bdcc Remove unnecessary private data members from TrueAudio::File. 2015-12-17 11:43:11 +09:00
Tsuda Kageyu
f0b0b736e3 Update NEWS. 2015-12-17 11:26:45 +09:00
Tsuda Kageyu
546870d83a Merge pull request #684 from TsudaKageyu/save-mpeg
Fix saving MPEG files.
2015-12-17 11:24:25 +09:00
Tsuda Kageyu
581e58585a Amend a comment that refers to a deprecated function. 2015-12-16 13:39:22 +09:00
Tsuda Kageyu
36b905670c Replace TagLib::uint with unsinged int. 2015-12-16 12:59:43 +09:00
Tsuda Kageyu
42459fe457 Update NEWS. 2015-12-16 11:49:55 +09:00
Tsuda Kageyu
fc571e5150 Merge pull request #683 from TsudaKageyu/save-mp4
Fix saving MP4 files.
2015-12-16 11:48:52 +09:00
Tsuda Kageyu
5480458dfc Change a method name of XiphComment to make it consistent with other method. 2015-12-16 11:00:13 +09:00
Tsuda Kageyu
9950fca3c2 Remove duplicate Vorbis comment blocks when saving a FLAC file. 2015-12-16 10:00:08 +09:00
Tsuda Kageyu
63e838f831 Make use of List iterators and setAutoDelete() in FLAC::File. 2015-12-15 15:31:33 +09:00
Tsuda Kageyu
94f28b4fa1 Update NEWS. 2015-12-15 15:18:07 +09:00
Tsuda Kageyu
680784c7b7 Merge pull request #682 from TsudaKageyu/save-flac
Fix saving FLAC files.
2015-12-15 15:08:14 +09:00
Tsuda Kageyu
5d517127ae Merge pull request #698 from TsudaKageyu/mpeg-header-check
More robust checks for invalid MPEG frame headers.
2015-12-15 10:11:13 +09:00
Tsuda Kageyu
be9b5cc93a More robust checks for invalid MPEG frame headers. 2015-12-08 11:20:51 +09:00
Tsuda Kageyu
847eec23cf Fix careless copy-and-paste code. 2015-12-04 14:29:26 +09:00
Tsuda Kageyu
9dfb3fe452 Update NEWS. 2015-12-04 14:15:51 +09:00
Tsuda Kageyu
a013c7c4c2 Merge pull request #694 from FestusHagen/fh1.m_NoSharedLibTests
Disable tests with a shared library.
2015-12-04 11:41:44 +09:00
Festus Hagen
62ec63b039 Disable tests with a shared library. 2015-12-03 15:04:37 -05:00
Tsuda Kageyu
52d3122e9e Remove some unused private data members. 2015-12-03 16:55:05 +09:00
Tsuda Kageyu
24f220224d Move a test to the proper place.
The test is actually for XiphComment, not Vorbis::File.
2015-12-03 13:05:18 +09:00
Tsuda Kageyu
a1ac23530e Simpler conversion from bits to bytes. 2015-12-03 12:34:54 +09:00
Tsuda Kageyu
e99910dd74 Revert some ABI breaking changes. 2015-12-03 10:06:04 +09:00
Tsuda Kageyu
88ad2843f4 Small cosmetic fixes. 2015-12-03 02:25:16 +09:00
Tsuda Kageyu
a0b8683656 Use a standard type rather than TagLib::uint.
This won't break the ABI compatibility.
2015-12-03 02:15:41 +09:00
Tsuda Kageyu
085a0ef298 Use a standard type rather than TagLib::ulong.
This won't break the ABI compatibility.
2015-12-03 00:03:06 +09:00
Tsuda Kageyu
363c471a2d Small cosmetic fixes. 2015-12-02 23:21:23 +09:00
Tsuda Kageyu
7f8efec4d4 Merge pull request #545 from FestusHagen/fh1.m_TDRC
Append TIME to TDRC, adjusted test_id3v2.cpp as appropriate.
2015-12-02 21:11:53 +09:00
Tsuda Kageyu
dcc0fe553c Use a standard type rather than TagLib::ushort.
This won't break the ABI compatibility.
2015-12-02 18:59:45 +09:00
Tsuda Kageyu
8bdddaabce Use a standard type rather than TagLib::uchar.
This won't break the ABI compatibility.
2015-12-02 18:11:52 +09:00
Tsuda Kageyu
2cb89b85c9 Make use of List iterators and setAutoDelete() in XiphComment. 2015-12-02 17:33:11 +09:00
Tsuda Kageyu
e6e9ba6094 Fix saving FLAC files.
This fixes all the issues reported at #622.
2015-12-02 17:21:12 +09:00
Tsuda Kageyu
7b854c5434 Update NEWS. 2015-12-02 15:01:10 +09:00
Tsuda Kageyu
1529af7a12 Merge branch 'xiph-picture' of https://github.com/gogglesmm/taglib into gogglesmm-xiph-picture
# Conflicts:
#	tests/test_bytevector.cpp
#	tests/test_ogg.cpp
2015-12-02 14:59:50 +09:00
Tsuda Kageyu
e475e31e0d Update NEWS. 2015-12-02 14:28:41 +09:00
Tsuda Kageyu
ea55c8b5c1 Merge branch 'cmake-allow-BUILD_SHARED_LIBS-to-drive-ENABLE_STATIC' of https://github.com/tSed/taglib into tSed-cmake-allow-BUILD_SHARED_LIBS-to-drive-ENABLE_STATIC
# Conflicts:
#	CMakeLists.txt
#	examples/CMakeLists.txt
2015-12-02 14:21:58 +09:00
Tsuda Kageyu
bc8b60ea46 Remove an unreachable statement. 2015-12-02 14:08:20 +09:00
Tsuda Kageyu
83ffc87598 Surround some internal stuff by anonymous namespaces. 2015-12-02 12:00:43 +09:00
Tsuda Kageyu
d97292c593 Amend some comments refer to the 'offset_t' type. It no longer exists. 2015-12-02 11:36:54 +09:00
Tsuda Kageyu
060a50ab11 Use a standard type rather than TagLib::wchar.
This won't break the ABI compatibility.
2015-12-02 11:30:29 +09:00
Tsuda Kageyu
6966be4292 Use a standard type rather than TagLib::ulonglong.
TagLib::ulonglong is not used in the public headers, so the changes are trivial.
2015-12-02 09:13:10 +09:00
Lukáš Lalinský
f38749aacf Merge pull request #690 from stefansaraev/pkgconfig
fix pkgconfig file for cross compiling
2015-12-01 16:47:47 +01:00
Tsuda Kageyu
b4b16fd7e0 Merge pull request #691 from FestusHagen/fh1.m_ushortAmbiguities
Silence ushort ambiguity errors
2015-12-01 00:02:25 +09:00
Festus Hagen
b1c67fc5dc Silence ushort ambiguity errors 2015-11-30 08:09:08 -05:00
Stefan Saraev
90f1f03163 fix pkgconfig file for cross compiling 2015-11-30 14:14:10 +02:00
Tsuda Kageyu
101a330d55 The new test wasn't called. 2015-11-30 18:32:49 +09:00
Tsuda Kageyu
59088096e6 Add some tests for M4V files. 2015-11-30 17:18:50 +09:00
Tsuda Kageyu
d1ceffd1f0 Merge branch 'm4v' of https://github.com/videolabs/taglib into videolabs-m4v
# Conflicts:
#	taglib/fileref.cpp
2015-11-30 17:12:03 +09:00
Tsuda Kageyu
eb6542bc8b Unify some duplicate internal functions. 2015-11-30 16:35:37 +09:00
Tsuda Kageyu
bccd57a470 Add const to an unchanged data member. 2015-11-30 16:21:58 +09:00
Tsuda Kageyu
a6701ddcda Remove some private data members not needed to belong to private classes. 2015-11-30 15:43:18 +09:00
Tsuda Kageyu
31d1f11969 Use a const pointer to initialize a const pointer. 2015-11-30 15:00:32 +09:00
Tsuda Kageyu
f9e558eef5 Avoid trying to remove tag chunks when an AIFF file doesn't have an ID3v2 tag. 2015-11-30 14:17:26 +09:00
Tsuda Kageyu
fcdf7c2ae2 Add some tests to check if the internal flags are updated when writing WAV files. 2015-11-30 13:46:43 +09:00
Tsuda Kageyu
8c424badad Avoid writing an empty ID3v2 tag in an AIFF file. 2015-11-30 13:42:25 +09:00
Tsuda Kageyu
f1b683b582 Remove some private data members not needed to be carried. 2015-11-30 13:09:11 +09:00
Tsuda Kageyu
5e37f0101d Remove some private data members not needed to be carried. 2015-11-30 12:53:52 +09:00
Tsuda Kageyu
b541ec8b68 Remove some private data members not needed to be carried. 2015-11-30 12:35:29 +09:00
Tsuda Kageyu
1fa677e2ef Add some tests about splitting/merging Ogg pages. 2015-11-29 01:57:15 +09:00
Festus Hagen
463e8f313a Append TIME to TDRC, adjusted test_id3v2.cpp as appropriate. 2015-11-27 17:54:20 -05:00
Tsuda Kageyu
8130b30397 Close temporary files regardless if the tests are successful or not.
delete statements are skipped when assertions fail.
2015-11-27 15:20:16 +09:00
Tsuda Kageyu
6b836c6ba7 Add ENABLE_CCACHE build option to allow users opt in to use ccache. 2015-11-27 12:15:03 +09:00
Tsuda Kageyu
f5a3f2b6c1 Update NEWS. 2015-11-27 09:37:29 +09:00
Tsuda Kageyu
4b8e39e8f0 Merge branch 'fh1.m_AddBuildBindingsOption' of https://github.com/FestusHagen/taglib into FestusHagen-fh1.m_AddBuildBindingsOption
# Conflicts:
#	CMakeLists.txt
2015-11-27 09:35:13 +09:00
Tsuda Kageyu
905bf89e66 Fix saving WavPack files.
This fixes the issue reported at #624.
2015-11-27 09:30:03 +09:00
Tsuda Kageyu
67cbf2c0a8 Silence a GCC warning in tests about ignoring a return value. 2015-11-27 02:45:33 +09:00
Tsuda Kageyu
3f84621d58 Small cleanups in CMakeLists.txt. 2015-11-27 02:27:24 +09:00
Tsuda Kageyu
083eadec60 Fix saving MPC files.
This fixes the issue reported at #624.
2015-11-27 00:53:03 +09:00
Tsuda Kageyu
f180445be5 Fix saving APE files.
This fixes the issue reported at #624.
2015-11-27 00:22:07 +09:00
Tsuda Kageyu
cbc279b899 Merge pull request #685 from g-coder/patch-2
Added ccache support for faster compilation
2015-11-26 20:03:17 +09:00
g-coder
e1bcb101f0 Update CMakeLists.txt 2015-11-26 15:51:42 +05:30
g-coder
14f3b6c928 Added ccache support for faster compilation
ccache is a compiler cache. It speeds up recompilation by caching previous compilations and detecting when the same compilation is being done again. It provides support for C/C++ languages.
It makes compilation fast if it is available on the machine.
https://ccache.samba.org/
2015-11-26 15:12:23 +05:30
Tsuda Kageyu
b6361ddde5 Fix saving MPEG files.
This fixes all the issues reported at #618.
2015-11-25 01:56:07 +09:00
Tsuda Kageyu
66dd0b8a6f Small cosmetic fixes. 2015-11-25 00:12:18 +09:00
Tsuda Kageyu
94b7828990 Fix saving MP4 files.
This fixes the issue reported at #619.
2015-11-24 23:41:10 +09:00
Tsuda Kageyu
fa4289e044 Fix instance references to a static member function. 2015-11-24 16:36:50 +09:00
Tsuda Kageyu
3612c2cc24 Small cosmetic fix. 2015-11-24 15:45:38 +09:00
Tsuda Kageyu
477aba9465 Merge pull request #676 from TsudaKageyu/string-hide-private
Hide private things from a public header.
2015-11-23 17:48:06 +09:00
Tsuda Kageyu
a640074a21 Hide some private functions from a public header. 2015-11-23 03:26:38 +09:00
Tsuda Kageyu
25ffbcb4b9 Hide a private static variable.
This is so-called Scott Mayers' singleton pattern.
2015-11-23 01:32:12 +09:00
Tsuda Kageyu
e44de9f37f Remove duplicate tags when saving AIFF files.
Just the same way as WAV already does.
2015-11-22 21:03:33 +09:00
Tsuda Kageyu
2b7d6fef47 Reduce redundant ref()/deref() operations. 2015-11-22 20:11:08 +09:00
Tsuda Kageyu
ae633105d6 Fix an instance reference to a static data member. 2015-11-22 19:43:17 +09:00
Tsuda Kageyu
6978131d22 Silence some GCC warnings about no return statement. 2015-11-22 18:42:49 +09:00
Tsuda Kageyu
83a0bc3710 Avoid using obsolete XiphComment::removeField(). 2015-11-22 18:30:56 +09:00
Tsuda Kageyu
6477132301 Update NEWS. 2015-11-22 16:23:53 +09:00
Tsuda Kageyu
559c6b28c9 Merge pull request #681 from TsudaKageyu/xiph-remove-fields
Add alternative methods to XiphComment::removeField() that may cause …
2015-11-22 16:19:15 +09:00
Tsuda Kageyu
e9f70a59b2 Merge branch 'genversion' of https://github.com/ufleisch/taglib into ufleisch-genversion
# Conflicts:
#	CMakeLists.txt
2015-11-22 10:38:35 +09:00
Tsuda Kageyu
f34d73d319 Make FileRef::FileTypeResolver work properly. 2015-11-21 18:29:41 +09:00
Tsuda Kageyu
ef3ce1e38a Style fixes in fileref.cpp. 2015-11-21 17:06:26 +09:00
Tsuda Kageyu
8724184d56 Fix a typo in a comment. 2015-11-21 10:32:32 +09:00
Tsuda Kageyu
61dabe61a7 Update NEWS. 2015-11-21 10:30:48 +09:00
Tsuda Kageyu
de0fc83066 Style fixes in fileref.cpp. 2015-11-21 10:29:37 +09:00
Tsuda Kageyu
c3807e59cd Merge pull request #528 from chouquette/master
FileRef: Allow an IOStream to be used
2015-11-21 09:51:03 +09:00
Tsuda Kageyu
b592f78238 Unify common functions for finding tags.
Several classes have exactly identical functions for finding tags.
This also hides the functions from public headers.
2015-11-21 09:30:04 +09:00
Tsuda Kageyu
84e3582332 Add alternative methods to XiphComment::removeField().
Using XiphComment::removeField() may lead to a linkage error, however we can't fix the method itself without breaking the ABI or changing its behavior.
So we added some alternative method and marked the old one deprecated.
2015-11-20 23:28:32 +09:00
Tsuda Kageyu
ce1c03faa3 Update NEWS. 2015-11-20 23:08:43 +09:00
Tsuda Kageyu
94c941928a Merge pull request #654 from TsudaKageyu/xiph-setcomment
Fix XiphComment::setComment() for the case that a Vorbis comment has …
2015-11-20 23:02:51 +09:00
Tsuda Kageyu
8c6fe45453 Avoid using String::null where an empty string is required.
String::null is not necessarily be empty or remains the same instance.
Using it in a public header may lead to a linkage error.
2015-11-20 22:21:47 +09:00
Tsuda Kageyu
c4fe65787c Avoid using String::isNull() where it is considered to be confused with isEmpty(). 2015-11-20 20:59:13 +09:00
Tsuda Kageyu
29b0568dec Revert "Add a test about handing "COMMENT" and "DESCIPRION" fields in XiphComment."
This reverts commit 8f147034d6.
2015-11-20 14:18:40 +09:00
Tsuda Kageyu
7d0a651f3e Revert "Use always "COMMENT" field when updating XiphComment."
This reverts commit ba5137bf2d.
2015-11-20 14:18:23 +09:00
Tsuda Kageyu
224f133d65 Merge pull request #643 from TsudaKageyu/save-asf
Avoid writing duplicate tags when saving ASF files.
2015-11-20 13:52:33 +09:00
Tsuda Kageyu
f956c89141 Remove a warning from a comment and update NEWS. 2015-11-20 13:48:51 +09:00
Tsuda Kageyu
6dc8d701a8 Avoid writing duplicate tags when saving ASF files.
Reduce memory reallocations and copies when saving ASF files.
2015-11-20 13:48:25 +09:00
Tsuda Kageyu
779f904940 Update NEWS. 2015-11-20 13:35:07 +09:00
Tsuda Kageyu
4161746d89 Update NEWS. 2015-11-20 13:25:28 +09:00
Tsuda Kageyu
6d5654028a Merge pull request #642 from TsudaKageyu/strip-and-properties
Fix segfaults when calling File::properties() after strip().
2015-11-20 13:24:01 +09:00
Tsuda Kageyu
93d55dafd6 Update NEWS. 2015-11-20 10:50:21 +09:00
Tsuda Kageyu
e1e1b6c60c Merge pull request #623 from TsudaKageyu/erase-duplicate-id3v2
Skip duplicate ID3v2 tags and treat them as an extra blank of the first one.
2015-11-20 10:49:11 +09:00
Tsuda Kageyu
1cc3e4cc57 Consistent rounding when calculating the MP4 audio length. 2015-11-19 18:24:20 +09:00
Tsuda Kageyu
f310b1810d Update NEWS. 2015-11-19 17:55:18 +09:00
Tsuda Kageyu
e10684312e Efficient lookup for an ID3v2 tag in a MPEG file.
An ID3v2 tag or MPEG frame is most likely be at the beginning of the file.
2015-11-19 17:31:51 +09:00
Tsuda Kageyu
a3564d8c68 Efficient lookup for the MP4/ASF field name and ID3v1 genre tables.
Linear lookup is much faster and memory efficient when an array is very small.
2015-11-19 16:35:55 +09:00
Tsuda Kageyu
95ef0e7882 Add some tests for String::toInt() with too large values. 2015-11-19 14:52:02 +09:00
Tsuda Kageyu
13a258d9ed Backport shorter versions of some functions from taglib2. 2015-11-19 14:45:36 +09:00
Tsuda Kageyu
72a807def1 Fix a string conversion bug in tag_c.cpp. 2015-11-19 11:50:39 +09:00
Tsuda Kageyu
539d951277 Avoid using ByteVector::null where an empty vector is required.
ByteVector::null is not necessarily be empty or remains the same instance.
Using it in a public header may lead to a linkage error.
2015-11-19 10:52:46 +09:00
Tsuda Kageyu
21788f4a26 Efficient lookup for the ID3v2 frame ID table.
Linear lookup is much faster and memory efficient when an array is very small.
2015-11-19 10:07:10 +09:00
Tsuda Kageyu
74cb6f3fc7 Merge pull request #493 from TsudaKageyu/nested-refcounter
Simplify overly complicated ByteVector::mid() implementation.
2015-11-19 09:37:40 +09:00
Tsuda Kageyu
e0f1151c5c Resolve some conflicts before merging. 2015-11-19 09:27:15 +09:00
Tsuda Kageyu
10e1fcd686 Consistent notations between ByteVector::data() and at(). 2015-11-19 09:23:20 +09:00
Tsuda Kageyu
3bce77a359 Use linear search instead of the Knuth-Morris-Pratt algorithm in ByteVector::find().
In almost all cases, it handles too small data for KMP to work effectively.
2015-11-19 09:23:19 +09:00
Tsuda Kageyu
0ffd2e8ab9 Replace DATA macro with more straightforward notations. 2015-11-19 09:23:19 +09:00
Tsuda Kageyu
7e85d9b202 Simplify overly complicated ByteVector::mid() implementation.
Especially remove the useless nested RefCounters.
2015-11-19 09:23:19 +09:00
Tsuda Kageyu
c6443dabc6 Use the same type name between a List and its iterator. 2015-11-18 17:58:13 +09:00
Tsuda Kageyu
c5db39fbf4 Merge pull request #661 from ufleisch/podcast-frames
Support for Apple podcast frames
2015-11-18 17:48:14 +09:00
Tsuda Kageyu
b6469bda1a Merge pull request #628 from RyanLucchese/rl-ConstReverseIterator-solaris-bug
return const correct reverse iterator for newer Sun compiler
2015-11-18 17:23:16 +09:00
Tsuda Kageyu
978a5f0a27 Merge pull request #629 from RyanLucchese/rl-fix-class-instantiation-solstudio
check SUNPRO_CC version before using Map workaround
2015-11-18 17:22:25 +09:00
Tsuda Kageyu
8203ccf04c Merge pull request #608 from TsudaKageyu/mp4-has-tags
Add a method to check if an MP4 file on disk actually has a tag.
2015-11-18 17:14:26 +09:00
Tsuda Kageyu
47860c20f4 Make use of List iterators. 2015-11-18 17:06:49 +09:00
Tsuda Kageyu
6e6e11f21a Use List::isEmpty() rather than size() to check if the list is empty.
std::list::empty() is guaranteed to be an O(1) operation.
2015-11-18 16:57:41 +09:00
Tsuda Kageyu
288e97ad44 Make use of List iterators. 2015-11-18 16:38:30 +09:00
Tsuda Kageyu
07b26ab3e4 Make use of List iterators and setAutoDelete(). 2015-11-18 15:29:04 +09:00
Tsuda Kageyu
0f00dfc778 Add a comment to List::isEmpty(). 2015-11-18 15:14:50 +09:00
Tsuda Kageyu
b01fecd280 Separate some tests for MP4::File::hasMP4Tag(). 2015-11-18 14:25:22 +09:00
Tsuda Kageyu
69921b8090 Amend the comment on MP4::File::hasMP4Tag(). 2015-11-18 14:17:29 +09:00
Tsuda Kageyu
1caaa8a020 Reduce duplicate code between String::ctor() and operator=().
It's safer to have only one pair of ref()/deref() than to have a several pairs.
2015-11-18 14:03:45 +09:00
Tsuda Kageyu
4f3844114a Mark some variables and functions deprecated.
ByteVector::null, ByteVector::isNull(), String::null, String::isNull() have got marked deprecated.
It's dangerous to use them, and they will be removed in taglib2.
2015-11-18 10:29:25 +09:00
Tsuda Kageyu
76de4234a1 Add a test for the CRC checksum of Ogg pages. 2015-11-17 15:05:43 +09:00
Tsuda Kageyu
4bac99e3da Add some notes about ByteVector::isNull() and ByteVector::null. 2015-11-17 13:06:03 +09:00
Tsuda Kageyu
6b7a2c4cf7 Add some notes about String::isNull() and String::null. 2015-11-17 11:41:13 +09:00
Tsuda Kageyu
1a942627bf Add String::clear() method to clear the string. 2015-11-17 11:29:52 +09:00
Tsuda Kageyu
3128f425b8 vsnprintf()/vsprintf() does not necessarily return -1 when failed. 2015-11-17 11:06:19 +09:00
Tsuda Kageyu
3f968933f4 Use std::wstring::empty() rather than size() == 0.
Depending on the implementation, empty() can be more efficient than size().
2015-11-17 10:49:30 +09:00
Tsuda Kageyu
67f44071cd Fix the usage of boost::endian::endian_reverse(). 2015-11-14 14:49:59 +09:00
Tsuda Kageyu
11fbf394a3 Skip duplicate ID3v2 tags and treat them as an extra blank of the first one.
This enables all the file formats to handle duplicate ID3v2 tags properly and erase them automatically.
2015-11-13 11:55:56 +09:00
Tsuda Kageyu
a25e1e9f90 Correct the ID3v2 padding size calculation. 2015-11-13 11:44:12 +09:00
Tsuda Kageyu
091ab9dee0 Reduce memory reallocation when rendering an ID3v2 tag.
Prevent an ID3v2 padding from being ridiculously large.
2015-11-13 11:35:37 +09:00
Tsuda Kageyu
c353a71ce5 Remove an unused private data member. 2015-11-13 11:23:27 +09:00
Tsuda Kageyu
762581fe3f Add a method to check if an MP4 file on disk actually has a tag. 2015-11-13 11:14:12 +09:00
Tsuda Kageyu
8f147034d6 Add a test about handing "COMMENT" and "DESCIPRION" fields in XiphComment. 2015-11-13 11:07:50 +09:00
Tsuda Kageyu
6775cef651 Make use of the Boost Endian library for byte swapping.
It's likely to be better at choosing the most efficient method than our CMake tests.
2015-11-13 10:58:23 +09:00
Tsuda Kageyu
86c7e905ba Silence some MSVC security warnings by replacing strdup() with _strdup().
Backported from taglib2.
2015-11-13 10:06:01 +09:00
Sander Jansen
2184aa476f xiph: preserve any picture data we can't decode in the text comments 2015-11-12 08:50:34 -06:00
Sander Jansen
3e8eff16b8 another style fix 2015-11-12 08:50:34 -06:00
Sander Jansen
41a44f4ab2 Support reading deprecated COVERART xiphcomment 2015-11-12 08:50:34 -06:00
Sander Jansen
70e471f1d1 Check if saved picture can be read back correctly 2015-11-12 08:50:34 -06:00
Sander Jansen
f836cb4dea Two more spaces 2015-11-12 08:50:34 -06:00
Sander Jansen
47bd01ecda Minor style fixes 2015-11-12 08:50:34 -06:00
Sander Jansen
90ad17b4c5 Additional unit tests for base64 encoder/decoder 2015-11-12 08:50:34 -06:00
Sander Jansen
f4c1beb161 Make fromBase64 more strict and update code to follow taglib coding style 2015-11-12 08:50:34 -06:00
Sander Jansen
c963d3ba04 correctly filter out invalid base64 characters 2015-11-12 08:50:34 -06:00
Sander Jansen
c4a02a1799 remove redundant size specificier in mid usage 2015-11-12 08:50:34 -06:00
Sander Jansen
59a1b7a491 add forgotten const cast 2015-11-12 08:50:34 -06:00
Sander Jansen
62bb3c95c8 Make ByteVector::fromBase64 as static member function 2015-11-12 08:50:34 -06:00
Sander Jansen
d3351a0012 Delete pictures in XiphComment destructor 2015-11-12 08:50:34 -06:00
Sander Jansen
0c14bd0ed0 Add FLAC::Picture support to Xiph Comment 2015-11-12 08:50:34 -06:00
Sander Jansen
7bbf5a2e79 add base64 encoder/decoder to bytevector 2015-11-12 08:48:00 -06:00
Tsuda Kageyu
ba5137bf2d Use always "COMMENT" field when updating XiphComment.
The recommended field name for additional comments is "COMMENT".
It's the same behavior as "DATE" or "TRACKNUMBER" field.
2015-11-12 17:37:22 +09:00
Tsuda Kageyu
b3cea6cca7 Fix XiphComment::setComment() for the case that a Vorbis comment has the "COMMENT" field. 2015-11-12 17:16:47 +09:00
Tsuda Kageyu
a533b9c665 Hide private things from a public header.
Some functions and a variable in tstring.h are not needed to be exposed in a public header.
2015-11-12 15:36:02 +09:00
Tsuda Kageyu
ec8e611909 Fix an instance reference to a static data member. 2015-11-12 14:48:24 +09:00
Tsuda Kageyu
c66e6b27d9 Small cleanups in CMakeLists.txt. 2015-11-12 14:41:15 +09:00
Tsuda Kageyu
1d379fdb2f Small cleanups in audioproperties.cpp. 2015-11-12 14:24:04 +09:00
Tsuda Kageyu
47813c5a7f A bit more accurate calculation of the AIFF audio length.
Actually, it's unlikely to improve the accuracy, but prevents a useless round-trip conversion between double and int.
2015-11-12 14:10:09 +09:00
Tsuda Kageyu
94ff9124c7 Skip both ID3v1 and APE tags when seeking the last MPEG frame. 2015-11-12 13:52:46 +09:00
Lukáš Lalinský
9f697fce8e 1.10 2015-11-11 22:41:59 +01:00
Tsuda Kageyu
320d0f5ad7 Use List::isEmpty() than size() > 0.
Small revision of pokowaka's fix. isEmpty() is a little better than size() > 0, since std::list::empty() is guaranteed to be an O(1) operation.
2015-11-06 16:13:43 +09:00
Tsuda Kageyu
77087cf865 Merge pull request #675 from garima-g/patch-1
Add self-assignment check in operator=
2015-11-05 15:43:03 +09:00
garima-g
8b4a27beb4 Add self-assignment check in operator=
Method 'operator=' should check its argument with 'this' pointer.
2015-11-05 11:19:44 +05:30
garima-g
998ebf4ce6 Add self-assignment check in operator=
Method 'operator=' should check its argument with 'this' pointer.
2015-11-05 11:16:34 +05:30
garima-g
ccaf650214 Add self-assignment check in operator=
Method 'operator=' should check its argument with 'this' pointer.
2015-11-05 11:12:24 +05:30
garima-g
9fad0b28a5 Add self-assignment check in operator=
Method 'operator=' should check its argument with 'this' pointer.
2015-11-05 11:09:20 +05:30
Lukáš Lalinský
153820bf12 Merge pull request #669 from pokowaka/bad_access
Fixes access violation
2015-10-12 14:30:00 +02:00
Erwin Jansen
29be00dc59 Fixes access violation
- Fixes access violation when setting empty stringlist on integer
  properties in mp4 tag
- Add a unit test that validates the fix.
2015-10-09 22:11:27 -07:00
Stephen F. Booth
ab30ec3a6f Merge pull request #664 from pbhd/master
add options R, I, D for replace/insert/delete of arbitrary tags
2015-09-23 23:09:55 -04:00
Peter Bauer
0a90687805 add options R, I, D for replace/insert/delete of arbitrary tags 2015-09-23 14:11:40 +02:00
Hugo Beauzée-Luyssen
e750cb491d FileRef: Allow an IOStream to be used 2015-09-15 15:01:40 +02:00
Steve Lhomme
1f7715a5da Add support for M4V tag parsing 2015-09-15 13:15:51 +02:00
Urs Fleisch
8e7504a66f Add support for ID2v2 PCST frames (podcast). 2015-09-08 20:42:30 +02:00
Hasso Tepper
9ad5bb1d62 Support for proprietary frames Apple iTunes uses to tag podcast files. 2015-09-07 20:25:34 +02:00
Tsuda Kageyu
5ca4cd2f52 Revert "Small cleanups in audioproperties.cpp."
This reverts commit b49e3e5620.
2015-08-31 16:29:13 +09:00
Tsuda Kageyu
70f8fb1bae Revert "Add and fix some comments in ConfigureChecks.cmake."
This reverts commit fa6f33e552.
2015-08-31 16:28:24 +09:00
Tsuda Kageyu
030e177640 Revert "Small cleanups in CMakeLists.txt."
This reverts commit 021c539002.
2015-08-31 16:28:12 +09:00
Tsuda Kageyu
3bb0b11bff Revert "Fix an instance reference to a static data member."
This reverts commit 9666b64f28.
2015-08-31 16:21:26 +09:00
Tsuda Kageyu
60558d6a4a Revert "A bit more accurate calculation of the AIFF audio length."
This reverts commit 15a1505880.
2015-08-31 16:20:58 +09:00
Tsuda Kageyu
12f59d774e Revert "Hide an internal variable from a public header."
This reverts commit fbd3b71690.
2015-08-31 16:20:41 +09:00
Tsuda Kageyu
e2466a72f8 Revert "Skip both ID3v1 and APE tags when seeking the last MPEG frame."
This reverts commit 6d925da75e.
2015-08-31 16:20:15 +09:00
Tsuda Kageyu
9666b64f28 Fix an instance reference to a static data member. 2015-08-31 10:34:12 +09:00
Tsuda Kageyu
15a1505880 A bit more accurate calculation of the AIFF audio length. 2015-08-31 01:48:29 +09:00
Tsuda Kageyu
fbd3b71690 Hide an internal variable from a public header. 2015-08-30 01:01:34 +09:00
Tsuda Kageyu
6a76f491f8 Merge pull request #579 from TsudaKageyu/frameoffset2
Skip both ID3v1 and APE tags when seeking the last MPEG frame.
2015-08-29 00:09:14 +09:00
Tsuda Kageyu
6d925da75e Skip both ID3v1 and APE tags when seeking the last MPEG frame. 2015-08-28 17:04:15 +09:00
Tsuda Kageyu
e178875b40 Mention that String::toWString()/toCWString() doesn't return UTF-32 string. 2015-08-28 13:38:22 +09:00
Tsuda Kageyu
bf45cfd84a Check if QT_VERSION macro is defined. 2015-08-28 01:38:09 +09:00
Tsuda Kageyu
b49e3e5620 Small cleanups in audioproperties.cpp. 2015-08-27 11:29:40 +09:00
Tsuda Kageyu
fa6f33e552 Add and fix some comments in ConfigureChecks.cmake. 2015-08-26 15:05:34 +09:00
Tsuda Kageyu
021c539002 Small cleanups in CMakeLists.txt. 2015-08-26 08:14:31 +09:00
Lukáš Lalinský
aedfeba66b Missed the full version string in taglib-config.cmd.cmake 2015-08-25 20:46:11 +02:00
Lukáš Lalinský
29ed265281 Skip the patch version if it's 0 2015-08-25 17:04:34 +02:00
Tsuda Kageyu
b6adb1f108 Remove a private data member not needed to carry. 2015-08-25 11:03:00 +09:00
Tsuda Kageyu
c7231c58a3 Improve a test about handling duplicate tags in WAV files. 2015-08-25 11:03:00 +09:00
Tsuda Kageyu
c5cf9b93bc Fix segfaults when calling File::properties() after strip().
Backport TagUnion::properties() and TagUnion::removeUnsupportedProperties() from taglib2.
2015-08-25 11:03:00 +09:00
Tsuda Kageyu
1bb06b1f7a Add RIFF::WAV::File::strip() function.
Avoid saving an empty tag.
2015-08-25 11:03:00 +09:00
Lukáš Lalinský
35aa6c4e84 Update date in NEWS 2015-08-23 20:09:22 +02:00
Lukáš Lalinský
54cea3edc3 Reorganize NEWS to put new features first 2015-08-23 12:20:25 +02:00
Lukáš Lalinský
0178d47c85 Don't use const ref to an integer in ChapterFrame 2015-08-23 12:20:25 +02:00
Lukáš Lalinský
71bc17b5e6 Lalala 2015-08-23 12:20:25 +02:00
Tsuda Kageyu
ac38f4ade1 Remove an unused private data member. 2015-08-23 18:25:17 +09:00
Tsuda Kageyu
b9f898698d Revert "Remove some private data members not needed to carry."
This reverts commit 7c17d32b3a.
2015-08-23 18:21:51 +09:00
Tsuda Kageyu
bd564546f4 Fix some typos in comments. 2015-08-14 00:10:19 +09:00
Tsuda Kageyu
d90617959b Fix some typos in comments. 2015-08-11 09:45:32 +09:00
Tsuda Kageyu
bc106ad81e Separate two variable initializations. 2015-08-10 00:50:13 +09:00
Tsuda Kageyu
a23f808627 Remove an useless #include. 2015-08-09 13:26:51 +09:00
Tsuda Kageyu
e4cd963b12 Improve a test about splitting OGG pages.
Check for #529.
2015-08-08 15:18:16 +09:00
Tsuda Kageyu
eff92fed98 Improve a test about splitting OGG pages.
Check for #529.
2015-08-07 16:47:13 +09:00
Tsuda Kageyu
80441ff754 Remove a workaround for an older version of GCC.
GLIBC's byte swap functions are a good fallback option.
2015-08-07 08:59:16 +09:00
Festus Hagen
35d5ba4eff Add BUILD_BINDINGS option, moved if(BUILD_EXAMPLES) to taglib/CMakeLists, plus some cleanup. 2015-08-06 17:26:37 -04:00
Tsuda Kageyu
edbafdbd88 Remove some redundant code from trefcounter.cpp. 2015-08-07 02:10:56 +09:00
Tsuda Kageyu
98ac8ba569 Add a comment about unused constants. 2015-08-07 02:03:03 +09:00
Tsuda Kageyu
173c58cf49 Merge pull request #580 from TsudaKageyu/negative-seek
Fix inconsistent negative seek behavior between Linux and Windows.
2015-08-07 01:32:26 +09:00
Tsuda Kageyu
ec3d050adc Merge pull request #644 from petrows/master
Install target should be checked to avoid conflicts
2015-08-07 01:20:03 +09:00
Tsuda Kageyu
19a7e45997 Merge pull request #639 from TsudaKageyu/remove-cmake-check-float
Run-time check for byte order rather than CMake check.
2015-08-07 01:17:27 +09:00
Tsuda Kageyu
49563a9cd0 Merge pull request #640 from TsudaKageyu/cmake-vsprintf
CMake check for vsprintf_s/vsnprintf rather than sprintf_s/snprintf.
2015-08-07 01:14:12 +09:00
Tsuda Kageyu
1abe61640a Merge pull request #645 from FestusHagen/fh1.m_UintAmbiguities_WavePackProperties
Silence uint ambiguity error in wavepackproperties.
2015-08-06 16:35:45 +09:00
Festus Hagen
65a6572299 Silence uint ambiguity errors in wavepackproperties. 2015-08-06 03:22:03 -04:00
Peter Petrovich
46e74c9391 Install target check fix 2015-08-05 08:45:27 +03:00
Tsuda Kageyu
7c17d32b3a Remove some private data members not needed to carry. 2015-08-05 11:54:30 +09:00
Tsuda Kageyu
bfe4ec5df5 Update NEWS.
Added support for reading the encoder information of WMA files.
Added support for reading the codec of WAV files.
Added support for multi channel WavPack files.
Added support for reading the nominal bitrate of Ogg Speex files.
Added support for VBR headers in MPEG files.
2015-08-05 02:58:45 +09:00
Tsuda Kageyu
81b7d0046e Merge branch 'master' of https://github.com/TsudaKageyu/taglib 2015-08-04 23:34:01 +09:00
Tsuda Kageyu
5990c72a01 Fix a typo in NEWS. 2015-08-04 23:27:28 +09:00
Tsuda Kageyu
018e969026 Add warnings about calling File::save() repeatedly. 2015-08-04 15:47:18 +09:00
Tsuda Kageyu
04ec7eae25 Add some supplementary comments. 2015-08-03 22:31:16 +09:00
Tsuda Kageyu
b6e7bb2c84 Update version to 1.10.0. 2015-08-03 16:14:48 +09:00
Tsuda Kageyu
f112d538ea Merge pull request #612 from TsudaKageyu/flac-id3v1
Avoid overwriting the audio stream when adding an ID3v1 tag to a FLAC…
2015-08-03 15:54:16 +09:00
Tsuda Kageyu
ac5ef0291c Update NEWS.
Fixed possible file corruptions when adding an ID3v1 tag to FLAC files.
2015-08-03 15:51:50 +09:00
Tsuda Kageyu
8fa86162c7 Add a test to check if the FLAC audio stream remains intact after adding an ID3v1 tag. 2015-08-03 15:40:30 +09:00
Tsuda Kageyu
fa17b4da6b Avoid overwriting the audio stream when adding an ID3v1 tag to a FLAC file. 2015-08-03 15:39:27 +09:00
Tsuda Kageyu
076e845912 Run-time check for integer byte order rather than CMake check.
It will easily be optimized out.
2015-08-03 13:08:58 +09:00
Tsuda Kageyu
f830177b3b Correct the order of #includes in tests. 2015-08-03 11:41:55 +09:00
Tsuda Kageyu
0650dc77a1 Fix some comments. 2015-08-02 03:24:25 +09:00
Tsuda Kageyu
c0f537a155 Merge pull request #581 from FestusHagen/fh2.m_FixUintAmbiguitiesInTests
Silence uint ambiguity errors in tests.
2015-08-02 02:40:20 +09:00
Tsuda Kageyu
44d9f2bf25 Run-time check for floating point byte order rather than CMake check.
It's safer not to use an unofficial CMake script.
2015-08-01 23:40:23 +09:00
Tsuda Kageyu
89e6ad96a4 Check for vsnprintf first. 2015-08-01 23:38:50 +09:00
Tsuda Kageyu
aa1dd0278d CMake check for vsprintf_s/vsnprintf rather than sprintf_s/snprintf. 2015-08-01 23:38:50 +09:00
Tsuda Kageyu
13dab99af0 Remove unused includes from ConfigureChecks.cmake. 2015-08-01 23:34:36 +09:00
Tsuda Kageyu
03ec83ecca Add AudioProperties::lengthInSeconds() and lengthInMilliseconds() functions to emulate virtual abstract functions. 2015-08-01 02:23:18 +09:00
Tsuda Kageyu
ce02910c6b Update NEWS.
Marked FLAC::File::streamInfoData() deprecated. It returns an empty ByteVector.
Marked FLAC::File::streamLength() deprecated. It returns zero.
2015-08-01 02:06:35 +09:00
Tsuda Kageyu
f25e30d33f Revert "Reorder CMake checks for sprintf() variants."
This reverts commit c69364d831.
2015-08-01 01:59:36 +09:00
Tsuda Kageyu
4dcccfbd6a Update NEWS.
New API for the audio length in milliseconds.
2015-08-01 01:46:35 +09:00
Tsuda Kageyu
7ca1b0c4d3 Merge pull request #575 from TsudaKageyu/chunk-offset
Stop calculating the offset in RIFF::File::chunkData().
2015-08-01 01:31:17 +09:00
Tsuda Kageyu
11f4e4e1ca Merge pull request #567 from TsudaKageyu/audioprop-wv
(wishlist) WavPack: AudioProperties improvements
2015-08-01 01:30:19 +09:00
Tsuda Kageyu
9759bd2dd7 Merge pull request #566 from TsudaKageyu/audioprop-tta
(wishlist) TrueAudio: AudioProperties improvements
2015-08-01 01:30:12 +09:00
Tsuda Kageyu
b28784538a Merge pull request #565 from TsudaKageyu/audioprop-wav
(wishlist) WAV: AudioProperties improvements
2015-08-01 01:30:03 +09:00
Tsuda Kageyu
fdea096c8d Merge pull request #564 from TsudaKageyu/audioprop-aiff
(wishlist) AIFF: AudioProperties improvements
2015-08-01 01:29:46 +09:00
Tsuda Kageyu
a9d030544a Merge pull request #563 from TsudaKageyu/audioprop-vorbis
(wishlist) Ogg Vorbis: AudioProperties improvements
2015-08-01 01:29:35 +09:00
Tsuda Kageyu
8344c4d7f8 Merge pull request #562 from TsudaKageyu/audioprop-speex
(wishlist) Ogg Speex: AudioProperties improvements
2015-08-01 01:29:22 +09:00
Tsuda Kageyu
8b1e872f81 Merge pull request #561 from TsudaKageyu/audioprop-opus
(wishlist) Ogg Opus: AudioProperties improvements
2015-08-01 01:29:13 +09:00
Tsuda Kageyu
6a778751ee Merge pull request #560 from TsudaKageyu/audioprop-mpeg
(wishlist) MPEG: AudioProperties improvements
2015-08-01 01:29:06 +09:00
Tsuda Kageyu
4328a830f9 Merge pull request #559 from TsudaKageyu/audioprop-mpc
(wishlist) MusePak: AudioProperties improvements
2015-08-01 01:27:20 +09:00
Tsuda Kageyu
ae99cbe64e Merge pull request #558 from TsudaKageyu/audioprop-mp4
(wishlist) MP4: AudioProperties improvements
2015-08-01 01:27:15 +09:00
Tsuda Kageyu
692ce897cb Merge pull request #557 from TsudaKageyu/audioprop-flac
(wishlist) FLAC: AudioProperties improvements
2015-08-01 01:27:06 +09:00
Tsuda Kageyu
0ed1c29acc Merge pull request #556 from TsudaKageyu/audioprop-asf
(wishlist) ASF: AudioProperties improvements
2015-08-01 01:26:52 +09:00
Tsuda Kageyu
3c59b7858a Merge pull request #555 from TsudaKageyu/audioprop-ape
(wishlist) APE: AudioProperties improvements
2015-08-01 01:21:59 +09:00
Tsuda Kageyu
2c9f63d9b0 Merge pull request #568 from TsudaKageyu/audioprop-mod
(wishlist) MOD: AudioProperties improvements
2015-08-01 01:04:34 +09:00
Tsuda Kageyu
0b43b7136d FLAC: Remove unused formal parameters. 2015-08-01 00:49:25 +09:00
Tsuda Kageyu
1e752a1e8f FLAC: Add debug messages to tell some functions are obsolete. 2015-08-01 00:49:23 +09:00
Tsuda Kageyu
29a31859ff FLAC: Move some public functions to above private ones. 2015-08-01 00:49:22 +09:00
Tsuda Kageyu
0b01461d50 FLAC: Avoid using deprecated functions. 2015-08-01 00:49:22 +09:00
Tsuda Kageyu
a77abedf63 Remove some more data members not needed to carry. 2015-08-01 00:49:22 +09:00
Tsuda Kageyu
21412e2ba2 FLAC: AudioProperties improvements
Add lengthInSeconds(), lengthInMilliseconds() properties. (#503)
Add bitsPerSample() property besides sampleWidth(). (#360)
Remove some data members which are not needed to carry.
Add some tests for audio properties.
Add some supplementary comments.
2015-08-01 00:49:21 +09:00
Tsuda Kageyu
dbf9644c8d WavPack: Remove unused formal parameters. 2015-08-01 00:44:41 +09:00
Tsuda Kageyu
592e14f950 TrueAudio: Remove unused formal parameters. 2015-08-01 00:41:42 +09:00
Tsuda Kageyu
1fb3727c4c WAV: Remove unused formal parameters. 2015-08-01 00:37:31 +09:00
Tsuda Kageyu
0501fbdd72 AIFF: Remove unused formal parameters. 2015-08-01 00:33:56 +09:00
Tsuda Kageyu
5235abc498 Vorbis: Remove unused formal parameters. 2015-08-01 00:30:13 +09:00
Tsuda Kageyu
d944009904 Speex: Remove unused formal parameters. 2015-08-01 00:22:09 +09:00
Tsuda Kageyu
f729f863cd Opus: Remove unused formal parameters. 2015-08-01 00:19:20 +09:00
Tsuda Kageyu
c715ec09e4 MPEG: Hide an internal function from the public header. 2015-08-01 00:15:51 +09:00
Tsuda Kageyu
4f621140ce MPEG: Remove unused formal parameters. 2015-08-01 00:09:59 +09:00
Tsuda Kageyu
bd251aed37 MusePak: Remove unused formal parameters. 2015-08-01 00:04:27 +09:00
Tsuda Kageyu
34ab65aa57 MP4: Hide an internal function from the public header. 2015-07-31 23:59:22 +09:00
Tsuda Kageyu
7f0c547ba6 MP4: Remove unused formal parameters. 2015-07-31 23:55:30 +09:00
Tsuda Kageyu
0ff38ed52b ASF: Remove unused formal parameters. 2015-07-31 23:39:58 +09:00
Tsuda Kageyu
2b1116cec1 APE: Remove unused formal parameters. 2015-07-31 23:18:50 +09:00
Tsuda Kageyu
88947e7a48 Silence some MSVC specific warnings in tests. 2015-07-31 20:55:23 +09:00
Tsuda Kageyu
5ad69a81dc Silence some MSVC2015 specific warnings. 2015-07-31 12:23:01 +09:00
Tsuda Kageyu
c9963af848 MP4 atom length is limited up to 31 bits.
32-bit value will be negative.
2015-07-31 11:24:36 +09:00
Tsuda Kageyu
f38e32163e Add some comments to tutils.h. 2015-07-31 10:27:16 +09:00
Tsuda Kageyu
c69364d831 Reorder CMake checks for sprintf() variants.
VS2015 has snprintf(), however sprintf_s() is still recommended.
2015-07-31 10:11:01 +09:00
Tsuda Kageyu
3142330bee Remove sudo from .travis.yml.
This allows our tests to run faster on the container-based infrastructure.
2015-07-31 09:06:24 +09:00
Tsuda Kageyu
9b849c5da8 Merge pull request #554 from FestusHagen/fh1.m_FriendlyCppUnitSearch
Don't look for CppUnit unless BUILD_TESTS, general cleanup.
2015-07-30 23:55:16 +09:00
Tsuda Kageyu
a197f45ca8 Merge pull request #595 from TsudaKageyu/gcc-warning-test
Silence a GCC warning about ignoring a return value in test.
2015-07-30 23:39:04 +09:00
Tsuda Kageyu
6dcecf0e71 Fix a typo in a debug message. 2015-07-30 23:36:44 +09:00
Tsuda Kageyu
47800d1000 Merge pull request #625 from TsudaKageyu/checksum-comment
(minor, comment only) Add a supplementary comment to ByteVector::checksum().
2015-07-30 23:30:43 +09:00
Tsuda Kageyu
cc8c3cd1fd Merge pull request #617 from TsudaKageyu/useless-call
Remove an useless function call.
2015-07-30 23:30:18 +09:00
Tsuda Kageyu
58994e330e Update NEWS.
Fixed reading FLAC files with zero-sized padding blocks.
2015-07-30 22:03:02 +09:00
Tsuda Kageyu
572afd437d Merge pull request #638 from TsudaKageyu/update-news
Update NEWS.
2015-07-30 22:00:00 +09:00
Tsuda Kageyu
1eef4177e7 Merge pull request #637 from TsudaKageyu/flac-zero-sized-padding
Make FLAC::File tolerant to zero-sized padding blocks.
2015-07-30 21:58:42 +09:00
Tsuda Kageyu
64fac517ed Update NEWS. 2015-07-30 10:03:47 +09:00
Tsuda Kageyu
f79c766ba4 Avoid creating zero-sized padding blocks. 2015-07-29 23:05:17 +09:00
Tsuda Kageyu
6f944b0291 Make FLAC::File tolerant to zero-sized padding blocks. 2015-07-29 20:52:56 +09:00
Ryan Lucchese
04bee3faec added comments explaining ByteVector::rbegin() const and ByteVector::rend() const 2015-07-06 16:25:57 -06:00
Ryan Lucchese
e8c1a11730 better const correctness for ByteVector::rbegin() const and ByteVector::rend() const 2015-07-01 15:47:21 -06:00
Ryan Lucchese
d8e8ec69fe changed SUNPRO_CC version check to first check if SUNPRO_CC is defined 2015-06-30 13:41:09 -06:00
Ryan Lucchese
6d0712c8df changed SUNPRO_CC version check to first check if SUNPRO_CC is defined 2015-06-30 13:23:25 -06:00
Ryan Lucchese
00b2f6a386 check SUNPRO_CC version before defining WANT_CLASS_INSTANTIATION_OF_MAP in apetag.cpp - causes problems on newer Solaris Studio compiler 2015-06-30 12:50:03 -06:00
Ryan Lucchese
a3dccdc7a3 added sun compiler version check before changing constness of ConstReverseIterator in ByteVector 2015-06-30 12:46:25 -06:00
Ryan Lucchese
b698c73690 return const correct reverse iterator to prevent Solaris compiler from choking on const conversion 2015-06-30 12:18:12 -06:00
Tsuda Kageyu
801c9db810 WAV: Avoid using a magic number. 2015-06-30 15:59:01 +09:00
Tsuda Kageyu
409b135dd5 MPEG: Fix warnings about signed/unsigned mismatch on some compilers. 2015-06-30 11:58:07 +09:00
Tsuda Kageyu
4dd14d4d73 Add a supplementary comment to ByteVector::checksum(). 2015-06-27 01:29:16 +09:00
Tsuda Kageyu
44e6419644 ASF: Hide some internal functions from the public header. 2015-06-23 18:22:31 +09:00
Tsuda Kageyu
467658e463 ASF: Make use of List iterators and setAutoDelete(). 2015-06-23 17:43:50 +09:00
Tsuda Kageyu
3fcb21642c ASF: Hide internal class declarations from the public header. 2015-06-23 17:34:33 +09:00
Tsuda Kageyu
472ce9f42c ASF: Use CodecType enum instead of a magic number. 2015-06-23 11:48:25 +09:00
Tsuda Kageyu
be33340383 Remove an useless function call. 2015-06-22 09:54:17 +09:00
Tsuda Kageyu
91ed3548f1 ASF: Enable ASF::Properties to get the audio codec information. 2015-06-21 02:42:47 +09:00
Tsuda Kageyu
b56f4c4372 APE: Reduce useless File::Find() operations. 2015-06-20 20:41:02 +09:00
Tsuda Kageyu
2155b4fd50 TrueAudio: A bit more accurate calculation of the stream length. 2015-06-20 17:36:00 +09:00
Tsuda Kageyu
e605e96835 MusePak: Avoid seeking a file when not needed. 2015-06-20 17:30:25 +09:00
Tsuda Kageyu
dfee7020da APE: Find an ID3v2 tag and calculate the stream length in APE::File. 2015-06-20 04:34:34 +09:00
Scott Wheeler
e90b5e5f2f Merge pull request #614 from TsudaKageyu/win32-pkg-config
Enable pkg-config on Windows.
2015-06-19 09:12:32 +02:00
Tsuda Kageyu
642baca4ed Fix inconsistent negative seek behavior between Linux and Windows. 2015-06-19 02:42:18 +09:00
Tsuda Kageyu
da01fa5745 Enable pkg-config on Windows. 2015-06-19 01:26:12 +09:00
Tsuda Kageyu
78c70cf5bb MOD: AudioProperties improvements
Add lengthInSeconds(), lengthInMilliseconds() properties. (always return 0)
2015-06-18 18:03:53 +09:00
Tsuda Kageyu
8f6af3f020 WavPack: A bit more accurate calculation of the stream length. 2015-06-18 18:00:03 +09:00
Tsuda Kageyu
22f250eaa4 WavPack: AudioProperties improvements
Add lengthInSeconds(), lengthInMilliseconds() properties. (#503)
Add isLossless() property.
Support multi channel. (#92)
Remove some data members which are not needed to carry.
Add some tests for audio properties.
Add some supplementary comments.
2015-06-18 17:59:06 +09:00
Tsuda Kageyu
eb73612a2b TrueAudio: AudioProperties improvements
Add lengthInSeconds(), lengthInMilliseconds() properties. (#503)
Avoid possible arithmetic overflows. (#520)
Remove some data members which are not needed to carry.
Add some tests for audio properties.
Add some supplementary comments.
2015-06-18 17:52:36 +09:00
Tsuda Kageyu
9c8e36d3be WAV: Move property parsing code to Properties.
Make use of 'fact' chunk to get the number of total samples.
2015-06-18 17:47:39 +09:00
Tsuda Kageyu
ed25204d75 WAV: AudioProperties improvements
Add lengthInSeconds(), lengthInMilliseconds() properties. (#503)
Add bitsPerSample() property besides sampleWidth(). (#360)
Add format() property. (#360)
Add some tests for audio properties.
Add some supplementary comments.
2015-06-18 17:43:09 +09:00
Tsuda Kageyu
03fd0a3ead AIFF: Calculate the actual average bitrate even if a file is compressed.
Move property parsing code to Properties.
2015-06-18 17:38:41 +09:00
Tsuda Kageyu
aede4ac851 AIFF: AudioProperties improvements
Add lengthInSeconds(), lengthInMilliseconds() properties. (#503)
Add bitsPerSample() property besides sampleWidth(). (#360)
Add some tests for audio properties.
Add some supplementary comments.
2015-06-18 17:30:58 +09:00
Tsuda Kageyu
3823afcc87 Ogg Vorbis: AudioProperties improvements
Add lengthInSeconds(), lengthInMilliseconds() properties. (#503)
Remove some data members which are not needed to carry.
Add some tests for audio properties.
Add some supplementary comments.
2015-06-18 17:24:31 +09:00
Tsuda Kageyu
4dba88fa31 Ogg Speex: AudioProperties improvements
Add lengthInSeconds(), lengthInMilliseconds() properties. (#503)
Add bitrateNominal() property.
Remove some data members which are not needed to carry.
Add some tests for audio properties.
Add some supplementary comments.
2015-06-18 17:23:03 +09:00
Tsuda Kageyu
f3d8100c7b Ogg Opus: AudioProperties improvements
Add lengthInSeconds(), lengthInMilliseconds() properties. (#503)
Remove some data members which are not needed to carry.
Add some tests for audio properties.
Add some supplementary comments.
2015-06-18 17:19:23 +09:00
Tsuda Kageyu
b2c79bc084 MPEG: No need to get the length of an ID3v2 tag twice. 2015-06-18 17:14:12 +09:00
Tsuda Kageyu
f82be353b4 MPEG: Properties::xingHeader() should return null if a VBR header is not found. 2015-06-18 17:14:12 +09:00
Tsuda Kageyu
9ec6d28239 MPEG: AudioProperties improvements
Add lengthInSeconds(), lengthInMilliseconds() properties. (#503)
Support VBRI header in addition to Xing. (#136)
Fix MPEG frame seeker functions. (maybe #190)
Calculate MPEG frame length accurately.
Remove some data members which are not needed to carry.
Add some tests for audio properties.
Add some supplementary comments.
2015-06-18 17:14:04 +09:00
Tsuda Kageyu
3a1c784eec MusePak: A bit more accurate calculation of the stream length. 2015-06-18 17:05:58 +09:00
Tsuda Kageyu
4a014c8113 MusePak: AudioProperties improvements
Add lengthInSeconds(), lengthInMilliseconds() properties. (#503)
Remove some data members which are not needed to carry.
Add some tests for audio properties.
Add some supplementary comments.
2015-06-18 17:05:58 +09:00
Tsuda Kageyu
5d77553759 MP4: Remove useless ByteVector::mid() operations. 2015-06-18 17:00:10 +09:00
Tsuda Kageyu
da14f67e2c MP4: Do rounding when calculating the bit rate. 2015-06-18 17:00:10 +09:00
Tsuda Kageyu
9226fa76b3 MP4: AudioProperties improvements
Add lengthInSeconds(), lengthInMilliseconds() properties. (#503)
Add some tests for audio properties.
Add some supplementary comments.
Move parsing code to read() for consistency with other classes.
2015-06-18 17:00:10 +09:00
Tsuda Kageyu
ff36648e92 ASF: AudioProperties improvements
Add lengthInSeconds(), lengthInMilliseconds() properties. (#503)
Add bitsPerSample() property. (#360)
Add some tests for audio properties.
Add some supplementary comments.
2015-06-18 16:33:56 +09:00
Scott Wheeler
447a4739c5 Merge pull request #611 from TsudaKageyu/fix-bufsize
Fix mismatched file I/O buffer sizes.
2015-06-18 08:22:59 +02:00
Tsuda Kageyu
f15fe869a5 Add a test for APE files with an ID3v2 tag. 2015-06-18 14:41:37 +09:00
Tsuda Kageyu
125d887b85 APE: Use the audio stream length in calculating the bit rate. 2015-06-18 14:41:37 +09:00
Tsuda Kageyu
9a8e41b9d6 APE: AudioProperties improvements
Add lengthInSeconds(), lengthInMilliseconds() properties. (#503)
Enable to read bit depth from older version files. (#360)
Remove some data members which are not needed to carry.
Add some tests for audio properties.
Add some supplementary comments.
2015-06-18 14:41:37 +09:00
Tsuda Kageyu
68ef160dbc Fix mismatched file I/O buffer sizes. 2015-06-18 11:23:31 +09:00
Scott Wheeler
6d71bdf8b7 Merge pull request #607 from TsudaKageyu/unused-function
Remove some unused private function prototypes.
2015-06-15 21:15:08 +02:00
Tsuda Kageyu
b37eaace15 Removed an unused data member from MPE::File.
It seems to be related to scan().
2015-06-12 14:07:52 +09:00
Tsuda Kageyu
c1c70edb76 Remove some unused private function prototypes. 2015-06-12 11:50:38 +09:00
Tsuda Kageyu
75159d5d8a Silence a GCC warning about ignoring a return value in test. 2015-06-10 03:44:30 +09:00
Stephen F. Booth
46862bf537 Merge pull request #594 from TsudaKageyu/typos-in-comment
Fix some typos in comments.
2015-06-08 22:19:27 -04:00
Tsuda Kageyu
2b260fd2e8 Fix some typos in comments. 2015-06-09 10:33:32 +09:00
Scott Wheeler
b1a35a8b31 Add const to docs 2015-06-03 01:21:12 +02:00
Stephen F. Booth
88ef556d4e Merge pull request #591 from TsudaKageyu/wmemcpy
Use wmemcpy() rather than memcpy() and sizeof(wchar_t).
2015-06-02 08:19:33 -04:00
Samuel Martin
00c6e7ea79 cmake: use BUILD_SHARED_LIBS instead of ENABLE_STATIC to drive the shared object build
In case ENABLE_STATIC is still set on the CMake command line, this
change will makes CMake bail out becaus e this option is no longer
supported.

This patch makes taglib more compliant with the distro package framework
that uses the standards CMake options.

Signed-off-by: Samuel Martin <s.martin49@gmail.com>
2015-06-01 19:27:05 +02:00
Tsuda Kageyu
0c0f123a08 Use wmemcpy() rather than memcpy() and sizeof(wchar_t). 2015-06-01 13:52:55 +09:00
Festus Hagen
48311cca14 Undo comment stacking. 2015-05-31 22:12:18 -04:00
Michael Helmling
db90f4b358 Prefix #ifndef with TAGLIB_ in tpropertymap.h 2015-05-29 19:53:42 +02:00
Festus Hagen
b5d65edab7 Silence uint ambiguity errors in tests. 2015-05-27 14:07:53 -04:00
Scott Wheeler
a06d55ae6f Merge pull request #577 from TsudaKageyu/frameoffset
Fix MPEG::File::firstFrameOffset() and lastFrameOffset(). (#190)
2015-05-26 17:53:24 +02:00
Stephen F. Booth
c5f2e9342d Merge pull request #578 from TsudaKageyu/test-temp
A lot of zero-sized temporary files are left after a test session on …
2015-05-25 08:41:37 -04:00
Tsuda Kageyu
2d778d5a0a A lot of zero-sized temporary files are left after a test session on Windows. 2015-05-25 15:51:19 +09:00
Stephen F. Booth
0aa75b2e8d Merge pull request #574 from TsudaKageyu/skip-dup-chunks
Skip duplicate chunks when reading AIFF/WAV files.
2015-05-24 14:38:50 -04:00
Tsuda Kageyu
618a939c56 Fix MPEG::File::firstFrameOffset() and lastFrameOffset(). (#190) 2015-05-25 02:41:27 +09:00
Tsuda Kageyu
2337fbcfc6 Stop calculating the offset in RIFF::File::chunkData(). 2015-05-25 00:43:36 +09:00
Tsuda Kageyu
9da20a8a52 Skip duplicate chunks when reading AIFF/WAV files.
Similar to #492.
There is no good reason to use the last chunk rather than the first one.
2015-05-25 00:40:13 +09:00
Stephen F. Booth
4129b919c1 Merge pull request #572 from TsudaKageyu/map-erase2
Map::erase() can take a key directly so no need to call find() before…
2015-05-23 08:21:58 -04:00
Tsuda Kageyu
6d6f544010 Map::erase() can take a key directly so no need to call find() beforehand. 2015-05-23 18:12:01 +09:00
Stephen F. Booth
93da3ba6d9 Merge pull request #571 from TsudaKageyu/fuzzed-wv
Avoid an infinite loop when reading fuzzed WavPack files. (#482)
2015-05-22 07:44:41 -04:00
Stephen F. Booth
e064b88049 Merge pull request #570 from TsudaKageyu/audioprop-typo
Fix a typo in comment.
2015-05-22 07:41:46 -04:00
Tsuda Kageyu
3094540a4b Avoid an infinite loop when reading fuzzed WavPack files. (#482) 2015-05-22 14:15:10 +09:00
Tsuda Kageyu
8e21dcc3d4 Fix a typo in comment. 2015-05-22 12:06:01 +09:00
Stephen F. Booth
0ea22348cb Merge pull request #569 from TsudaKageyu/map-erase
std::map::erase() can take a key directly and has no-throw guarantee.
2015-05-21 22:46:15 -04:00
Stephen F. Booth
48ca54de1f Merge pull request #484 from TsudaKageyu/fix-rfind
Fix File::rfind() for small files.
2015-05-21 22:44:10 -04:00
Tsuda Kageyu
54de66f275 std::map::erase() can take a key directly and has no-throw guarantee. 2015-05-22 01:26:56 +09:00
Lukáš Lalinský
d4b0ba2a7a TagLib::XM::Properties is a public class that should be exported 2015-05-20 15:18:01 -07:00
Festus Hagen
abc5743222 Removed missed unnecessary BUILD_TESTS check. 2015-05-20 15:34:05 -04:00
Scott Wheeler
525396d9c2 Merge pull request #550 from TsudaKageyu/fix-test
Fix test code to work on some environments.
2015-05-20 19:24:52 +02:00
Tsuda Kageyu
bb9679b51a Fix test code to work on some environments.
const char * is more preferable than string for ifstream constructor.
2015-05-21 02:03:06 +09:00
Festus Hagen
e44cba56b5 Don't look for CppUnit unless BUILD_TESTS, general cleanup. 2015-05-20 11:11:55 -04:00
Scott Wheeler
e4cf012522 Bring the API more in line with the rest of TagLib
Like in #255, this also makes it possible to read values from the
tag in a const function.
2015-05-20 14:26:53 +02:00
Scott Wheeler
a37d423ea8 Merge pull request #549 from TsudaKageyu/win32-test
Modify the test code to work on MSVC/Windows.
2015-05-20 13:50:49 +02:00
Tsuda Kageyu
88a4cf34b8 Modify the test code to work on MSVC/Windows. 2015-05-20 20:19:20 +09:00
Scott Wheeler
81261dd128 This should also work Windows and has less duplicated code 2015-05-20 11:53:32 +02:00
Scott Wheeler
3bea9f6bee Don't use tempnam on UNIX
This silences the huge stream of warnings when building the tests.

I think I didn't break the Windows version in the process (though
it may make sense to use the built in Windows functions there
instead), but I don't have a Windows build environment here, so
I can't test.
2015-05-20 11:45:32 +02:00
Scott Wheeler
9cc2d7cfee Merge pull request #546 from TsudaKageyu/test-surrogate-pair
Add a test for strings that contains surrogate pairs.
2015-05-19 19:57:47 +02:00
Scott Wheeler
fe1bf015bb Merge pull request #548 from TsudaKageyu/render-twice
No need to call ID3v2::Frame::render() twice when saving an ID3v2 tag.
2015-05-19 11:28:50 +02:00
Tsuda Kageyu
287078566f No need to call ID3v2::Frame::render() twice when saving an ID3v2 tag. 2015-05-19 17:39:37 +09:00
Scott Wheeler
60966518e2 Quote path names including user-provided variables
This should make these work even if the value contains spaces

Closes #344
2015-05-19 10:15:15 +02:00
Tsuda Kageyu
2268efb49e Add a test for strings that contains surrogate pairs. 2015-05-19 09:01:47 +09:00
Scott Wheeler
29f535dc8d Remove *file argument to private members
This is already covered by d->file, so there's no reason to pass a pointer
to the member dozens of times.
2015-05-18 23:11:51 +02:00
Scott Wheeler
592522d34c Moar const fixes 2015-05-18 23:00:16 +02:00
Scott Wheeler
6b9e4e4403 1001 Const fixes (plus iterator rename) 2015-05-18 22:48:55 +02:00
Scott Wheeler
089e44f3db Remove unnecessary checks for null before delete
Closes #343
2015-05-18 21:40:21 +02:00
Scott Wheeler
bd3c4dc1b2 Merge pull request #515 from TsudaKageyu/empty-id3v2-frame
Skip empty ID3v2 frames when saving an ID3v2 tag.
2015-05-18 21:36:37 +02:00
Scott Wheeler
bba562b557 Add accessors to manipulate MP4 tags without modifying the internal structure
This brings the MP4 API in line closer to other tag formats and makes it
possible to access the tag data from const functions.

"ItemListMap" has been renamed to "ItemMap" (with the old version
deprecated).  It seems that the "ListMap" notion was copied probably
from Allan's ApeTag implementation, which incorrectly copied the term
from the XiphTag.  Notably, in XiphTag, because a field can have multiple
values, the "ListMap" is a map of lists.  Calling things a "ListMap" where
there can be only one value doesn't fit.

Closes #255
2015-05-18 21:18:33 +02:00
Scott Wheeler
451d23ca37 Add isEmpty() to MP4
Closes #457
2015-05-18 20:30:19 +02:00
Scott Wheeler
d248f77ab9 Show something useful for ChapterFrame::toString()
Closes #517
2015-05-18 19:51:18 +02:00
Tsuda Kageyu
727a11573a Fix File::rfind() for small files to work just like ByteVector::rfind(). 2015-05-19 01:24:37 +09:00
Scott Wheeler
34da0c0dab Update signature and docs
Specifically this allows the frame to be constructed and then to have
the children and embedded frames set later.
2015-05-18 18:19:43 +02:00
Scott Wheeler
7316cd331d Remove the null termination stuff from the ToC frames too 2015-05-18 18:17:10 +02:00
Scott Wheeler
3a977c55c4 We've moved away from including the null byte in the returned value
This does change previous behavior, but the previous behavior was
particularly stupid and inconsistent with everything else in TagLib.

It should be possible to mitigate this by putting the same safety
guards in the TableOfContents
2015-05-18 17:29:52 +02:00
Scott Wheeler
a094ce7dd2 Don't underflow if there are no embedded frames
Closes #513
2015-05-18 17:11:18 +02:00
Scott Wheeler
941efcba18 This isn't tracked as part of the elementID anymore 2015-05-18 16:53:12 +02:00
Scott Wheeler
ffb543acbb Split chapter data and embedded frame data
This will allow us to test parsing them separately
2015-05-18 16:45:57 +02:00
Scott Wheeler
fc24b3d22b Don't require users to include a padding byte explicitly
This makes it where the natural construction can be used of something
like:

new ChapterFrame("ID", ... )

Closes #514
2015-05-18 16:41:30 +02:00
Scott Wheeler
a9e064c58e Also test second constructor 2015-05-18 16:30:37 +02:00
Scott Wheeler
1a5d89d29e Update signature and docs to be more in-line with TagLib's style 2015-05-18 16:00:47 +02:00
Scott Wheeler
650b581f9e Merge pull request #544 from TsudaKageyu/advance
Use std::advance rather than a loop and increment.
2015-05-18 15:09:28 +02:00
Scott Wheeler
0739dd232a It doesn't make sense to set the factory after construction
Closes #259
2015-05-18 14:21:55 +02:00
Scott Wheeler
41a250a15d Make this macro work with current Qt versions
Closes #499
2015-05-18 12:42:24 +02:00
Tsuda Kageyu
8da0013482 Add a test to check if an empty ID3v2 frame is really skipped. 2015-05-18 19:03:20 +09:00
Scott Wheeler
0288059495 Add astylerc 2015-05-18 11:31:55 +02:00
Tsuda Kageyu
6e40361c0e Use std::advance rather than a loop and increment. 2015-05-18 09:31:43 +09:00
Lukáš Lalinský
c2570fe115 Merge pull request #521 from TsudaKageyu/gcc-warning
Fix a GCC/Clang warning about singed/unsigned comparison.
2015-05-17 10:06:10 -07:00
Lukáš Lalinský
bd91da55ba Merge pull request #542 from TsudaKageyu/comment-typos
Fix some typos in comment.
2015-05-17 10:05:43 -07:00
Tsuda Kageyu
3a3a6a6fda Fix some typos in comment. 2015-05-18 00:31:46 +09:00
Lukáš Lalinský
64c43b0d3f Merge pull request #535 from FestusHagen/fh1.m_UintAmbiguity
Silenced uint ambiguity error.
2015-05-16 17:03:53 -07:00
Lukáš Lalinský
db2dfa9515 Merge pull request #538 from gogglesmm/optimize-mid-to-uint
bytevector.mid(pos,4).toUInt() => bytevector.toUInt(pos)
2015-05-15 20:06:24 -07:00
Lukáš Lalinský
0069debf0c Merge pull request #536 from FestusHagen/fh1.m_CMakeSubProjectFix
Fix for using Taglib as a CMake sub directory project.
2015-05-15 19:48:43 -07:00
Lukáš Lalinský
69563d83a0 Merge pull request #533 from TsudaKageyu/bytevector-resize
Fix the wrong padding of ByteVector::resize().
2015-05-15 19:43:38 -07:00
Sander Jansen
60775306ea Fix code styling 2015-05-15 21:39:34 -05:00
Sander Jansen
4f77420248 replace use of bytevector.mid(pos,4).toUInt() with more optimized bytevector.toUInt(pos) 2015-05-15 21:25:44 -05:00
Tsuda Kageyu
b021ed44e9 Revert the last two commits.
But leave the tests unchanged, and add some comments.
2015-05-16 11:16:00 +09:00
Tsuda Kageyu
1f99c93a61 Reduce redundant memset when resizing ByteVector. 2015-05-16 03:46:34 +09:00
Tsuda Kageyu
a924ca0db7 Expand the internal buffer of ByteVector only if really needed.
Add tests for all execution paths of ByteVector::resize().
2015-05-14 11:20:35 +09:00
Urs Fleisch
b5c0ab58ba Get version information from taglib.h.
This avoids duplication of the version information in both
CMakeLists.txt and taglib.h while keeping the possibility to
use something different from CMake.
2015-05-10 10:30:51 +02:00
Festus Hagen
e43c1a3c09 Fix for using Taglib as a CMake sub directory project. 2015-05-09 20:29:52 -04:00
Festus Hagen
1e9529380d Silenced uint ambiguity error. 2015-05-09 14:19:33 -04:00
Tsuda Kageyu
ff8443f33a Fix the wrong padding of ByteVector::resize().
The expanded area will be filled with garbage instead of correct padding in some corner cases.
2015-05-02 02:43:08 +09:00
Lukáš Lalinský
62ab41fa07 Merge pull request #532 from TsudaKageyu/bytevector-detach-test
A little robuster tests for ByteVector iterators after detaching.
2015-04-29 10:14:16 -07:00
Tsuda Kageyu
00e3504264 A little robuster tests for ByteVector iterators after detaching. 2015-04-29 17:15:13 +09:00
Lukáš Lalinský
852da79899 Merge pull request #531 from TsudaKageyu/bytevector-detach
Fix ByteVector to return correct iterators after detached.
2015-04-28 20:34:53 -07:00
Tsuda Kageyu
ab047f6054 Fix ByteVector to return correct iterators after detached. 2015-04-29 10:28:08 +09:00
Lukáš Lalinský
5bad35c4cb Merge pull request #527 from TsudaKageyu/wma-guid
Fix saving WMA files with some GUID fields.
2015-04-28 10:42:06 -07:00
Tsuda Kageyu
8d708c03e1 Store any GUID fields in Metadata Library Object. 2015-04-28 16:40:24 +09:00
Tsuda Kageyu
f9a0b50830 Fix saving WMA files with some GUID fields. 2015-04-28 12:06:24 +09:00
Tsuda Kageyu
8491266b12 Fix a GCC/Clang warning about singed/unsigned comparison. 2015-04-17 09:38:21 +09:00
Stephen F. Booth
066b5aa57e Merge pull request #492 from TsudaKageyu/duplicate-id3v2-aiff-wav
Check AIFF/WAV files for duplicate tags.
2015-03-25 08:16:41 -04:00
Tsuda Kageyu
5f0a7da481 Take into account the frame header version when skipping an empty frame. 2015-03-24 10:41:39 +09:00
Tsuda Kageyu
d33d684fab Discard empty ID3v2 frames instead of adding a dummy null byte. 2015-03-24 10:31:52 +09:00
Tsuda Kageyu
4c4be0a263 Add a dummy byte to an empty ID3v2 frame to stick to the ID3v2 spec. 2015-03-22 22:14:21 +09:00
Michael Helmling
f476cf2b45 Merge pull request #511 from TsudaKageyu/tag-prop
Fix a bug that Tag::setProperties() clears the date instead of the track number
2015-03-20 10:28:20 +01:00
Tsuda Kageyu
68c0b0591b Fix a bug that Tag::setProperties() clears the date instead of the track number. 2015-03-20 13:33:13 +09:00
Stephen F. Booth
8fccaf30d2 Merge pull request #479 from TsudaKageyu/fuzzed-mp4
Fix an infinite loop when parsing MP4 files.
2015-02-18 08:15:50 -05:00
Tsuda Kageyu
b69973bcf2 Fix infinite loops when parsing MP4 files. 2015-02-18 11:33:10 +09:00
Tsuda Kageyu
30eac7569f Check AIFF/WAV files for duplicate tags.
AIFF/WAV files can have duplicate tags and it leads to memory leak.
2015-02-18 11:31:55 +09:00
Lukáš Lalinský
f6081ed32e Merge pull request #505 from TsudaKageyu/fuzzed-ape-tests
Separate the tests for fuzzed APE files.
2015-02-17 09:22:35 +01:00
Tsuda Kageyu
0839a23902 Separate the tests for fuzzed APE files. 2015-02-17 12:36:57 +09:00
Lukáš Lalinský
f35a279dd1 Merge pull request #496 from TsudaKageyu/fuzzed-mpc
Some fixes for fuzzed MPC files.
2015-02-16 19:26:57 +01:00
Tsuda Kageyu
e463d14f2e Check for EOF to fix a segfault while parsing fuzzed MPC files. 2015-02-17 01:22:39 +09:00
Tsuda Kageyu
829ae2112a Check the packet size to fix a segfault error while parsing fuzzed MPC files. 2015-02-17 01:22:39 +09:00
Tsuda Kageyu
65664e6855 Check for EOF to fix an infinite loop while parsing fuzzed MPC files. 2015-02-17 01:22:39 +09:00
Tsuda Kageyu
2193d6dd84 Fix an out-of-bounds access and consequent errors while parsing fuzzed MPC files.
Consequent errors may vary: segfault, zerodiv and so forth.
2015-02-17 01:22:38 +09:00
Stephen F. Booth
ef786188ad Merge pull request #502 from TsudaKageyu/fix-save-wav
Fix saving ID3v2/INFO tags of WAV files.
2015-01-31 14:12:18 -05:00
Tsuda Kageyu
2b5ee8deb9 Fix saving ID3v2/INFO tags of WAV files.
The old tag won't be removed when the new tag is empty.
2015-01-30 14:51:28 +09:00
Scott Wheeler
ed09c9cf87 Merge pull request #494 from TsudaKageyu/fix-zlib
Fix a wrong parameter for zlib.
2015-01-06 17:06:10 +01:00
Tsuda Kageyu
9d91610fc0 Fix a wrong parameter for zlib.
z_stream.avail_in has to be the length of the input buffer.
It will fail when frameDataLength is smaller than the actual compressed data size.
2015-01-06 18:45:42 +09:00
Stephen F. Booth
aed689c145 Merge pull request #491 from TsudaKageyu/aiff-hasid3v2
Implement missing AIFF::File::hasID3v2Tag().
2015-01-05 08:17:21 -05:00
Tsuda Kageyu
c6a63a3a2f Implement missing AIFF::File::hasID3v2Tag(). 2015-01-05 18:20:31 +09:00
Stephen F. Booth
2b0a540228 Merge pull request #490 from TsudaKageyu/remove-uncompr
Remove an unused file from taglib/CMakeLists.txt.
2015-01-04 22:31:42 -05:00
Tsuda Kageyu
2685ec1842 Remove an unused file from taglib/CMakeLists.txt.
uncompr.c is no longer used since caa53e8
2015-01-05 11:00:47 +09:00
Scott Wheeler
fdea6dcd40 Merge pull request #489 from TsudaKageyu/frame-flag-tests
Add some tests for huge memory allocation due to bad ID3v2 frame header ...
2015-01-05 02:03:06 +01:00
Tsuda Kageyu
ed253d3691 Add some tests for huge memory allocation due to bad ID3v2 frame header flags.
The tests covers #466 and #486.
Also fixes a compilation error on some compilers.
2015-01-05 09:34:30 +09:00
Scott Wheeler
57729b834a Show a debugging message when we have an error reading 2015-01-04 20:13:24 +01:00
Scott Wheeler
caa53e8de5 Read the compressed data as a stream
This avoids allocating the complete buffer at first based solely
on the value read from the frame header.  This then does a sanity
check at the end of reading to make sure that the two values match.
At present, it just prints a debugging message if the values do not
match.

Fixes #466
2015-01-04 19:58:18 +01:00
Lukáš Lalinský
31982660c8 Merge pull request #485 from TsudaKageyu/oga-segfault
Fix a segfault when reading faulty Ogg/FLAC files.
2015-01-01 19:48:23 +01:00
Lukáš Lalinský
bd7b8cc36a Merge pull request #473 from TsudaKageyu/fix-infotag
Fix an infinite loop when parsing an INFO tag.
2015-01-01 19:46:53 +01:00
Tsuda Kageyu
bc9bbfe3fa Add a check for faulty Ogg/FLAC files. 2015-01-02 00:10:51 +09:00
Tsuda Kageyu
7adea3df22 Separate tests for fuzzed WAV files. 2015-01-01 23:18:43 +09:00
Tsuda Kageyu
5ebb2ece80 Fix a segfault when reading faulty Ogg/FLAC files. 2015-01-01 19:59:30 +09:00
Tsuda Kageyu
0d2e01df61 Fix a segfault when parsing WAV properties. 2014-12-31 01:46:30 +09:00
Tsuda Kageyu
695fb5ec16 Add a test for fuzzed WAV files. 2014-12-31 01:44:25 +09:00
Tsuda Kageyu
3170d47ec3 Fix an infinite loop when parsing an INFO tag. 2014-12-31 01:44:25 +09:00
Stephen F. Booth
79b7c14129 Merge pull request #481 from TsudaKageyu/aiff-c
Add support for AIFF-C files.
2014-12-30 10:51:17 -05:00
Tsuda Kageyu
977fb2aeb0 Fix compilation errors on Clang. 2014-12-31 00:41:22 +09:00
Tsuda Kageyu
3b8c7d4e3a Add support for AIFF-C files. 2014-12-30 23:53:40 +09:00
Lukáš Lalinský
be33389884 Merge pull request #476 from TsudaKageyu/fuzzed-ape
Some fixes for fuzzed APE files.
2014-12-29 09:47:10 +01:00
Lukáš Lalinský
59ae61de6e Merge pull request #456 from TsudaKageyu/id3v2-padding
ID3v2 padding won't increase beyond 1% of the file size.
2014-12-29 09:26:55 +01:00
Tsuda Kageyu
16ac2cd240 Added some tests for fuzzed APE files. 2014-12-23 21:02:00 +09:00
Tsuda Kageyu
61543432c0 Fix an excessive loop when parsing an APE file. 2014-12-23 20:58:36 +09:00
Tsuda Kageyu
4a9614bfc3 Fix a division by zero error when parsing an APE file. 2014-12-23 20:54:20 +09:00
Tsuda Kageyu
1a917a38cd Fix ID3v2 padding size calculation. 2014-12-09 10:54:21 +09:00
Lukáš Lalinský
735e17f504 Merge remote-tracking branch 'TsudaKageyu/64bit-atom'
Conflicts:
	ConfigureChecks.cmake
2014-12-08 16:52:33 -08:00
Tsuda Kageyu
0731bc7b2e Remove CMake check for sizeof(long). 2014-12-09 08:55:46 +09:00
Bart van der Velden
fb1c744daf Compile without warnings with MSVC 2014-12-08 09:42:04 -08:00
Lukáš Lalinský
1bc5acd7a7 Merge pull request #424 from TsudaKageyu/format
Unified redundant string format functions. (backport from taglib2)
2014-12-08 09:38:03 -08:00
Lukáš Lalinský
f30f5ccce5 Merge pull request #441 from xhochy/tests-fix-vectorsize
Fix ByteVector sizes in some test cases
2014-12-08 09:36:28 -08:00
Lukáš Lalinský
421eb5e97e Merge pull request #420 from TsudaKageyu/remove-codecvt
Replaced codecvt with Win32 API.
2014-12-08 09:14:00 -08:00
Lukáš Lalinský
5e60ec48e9 Merge pull request #454 from TsudaKageyu/duplicate-id3v2
Skip duplicate ID3v2 tags in MPEG files.
2014-12-08 09:09:28 -08:00
Lukáš Lalinský
68eca440c9 Merge pull request #458 from achalddave/fix-gcc-errors
Fix more uint/TagLib::uint ambiguities
2014-12-08 08:54:27 -08:00
Achal Dave
7b03403791 Fix more uint/TagLib::uint ambiguities 2014-11-18 15:06:10 -08:00
Tsuda Kageyu
d80c4c96c3 ID3v2 padding won't increase beyond 1% of the file size. 2014-10-23 12:38:38 +09:00
Tsuda Kageyu
73b9b9b58d Avoid reading an entire ID3v2 tag when skipping it. 2014-10-23 08:14:10 +09:00
Lukáš Lalinský
072851869a Merge pull request #453 from TsudaKageyu/editorconfig
Suggestion: Use EditorConfig to help us stick to our coding style.
2014-10-22 19:31:14 +02:00
Tsuda Kageyu
269e78f1a0 Add a test for duplicate ID3v2 tags. 2014-10-21 00:16:43 +09:00
Tsuda Kageyu
71acf3b6f7 Comment on a weird workaround for duplicate ID3v2 tags. 2014-10-20 23:13:15 +09:00
Tsuda Kageyu
e41dc68a6b Skip duplicate ID3v2 tags in MPEG files. 2014-10-20 21:21:32 +09:00
Tsuda Kageyu
cfad951442 Use EditorConfig to help us stick to our coding style.
http://editorconfig.org/
2014-10-20 11:27:30 +09:00
Lukáš Lalinský
23ffb24472 Merge pull request #448 from TsudaKageyu/msvc-error
Fix a compilation error on MSVC.
2014-10-16 10:15:51 +02:00
Tsuda Kageyu
a8bfcd81be Fix a compilation error on MSVC. 2014-10-16 07:19:31 +09:00
Scott Wheeler
bd7419f0bd Rebuild TRDC from v2.3 fields
This fixes an issue that was reported to me via email with the recording
date being thrown away from v2.3 tags.
2014-09-25 23:07:09 +02:00
Scott Wheeler
71c1ce375f Don't leak 2014-09-25 20:46:47 +02:00
Scott Wheeler
cff8d22818 Missing assignment 2014-09-25 20:46:47 +02:00
Scott Wheeler
0051351ebb TableOfContents and ChapterFrame can be added to v2.3 or v2.4 tags 2014-09-25 20:01:00 +02:00
Scott Wheeler
82315276db Take ownership of embedded frames, as documented
Previously embedded frames that were created automatically were
never deleted.

Fixes #440
2014-09-18 16:23:28 +02:00
Scott Wheeler
8ea07be47e Merge branch 'master' of https://github.com/taglib/taglib 2014-09-18 16:12:34 +02:00
Scott Wheeler
eae4605e6e Merge pull request #439 from xhochy/test-memleaks
Fix various memleaks in the tests
2014-09-18 16:11:12 +02:00
Scott Wheeler
b6289c64dd Break up the mega-lines 2014-09-18 11:21:18 +02:00
Scott Wheeler
e6d7dd08f2 No reason to store this in the d-pointer 2014-09-18 10:24:48 +02:00
Uwe L. Korn
ee283789b7 Fix ByteVector sizes in some test cases 2014-09-14 20:08:31 +01:00
Uwe L. Korn
205569c8d2 Fix various memleaks in the tests 2014-09-14 18:03:27 +01:00
Lukáš Lalinský
4b67beffc8 Merge pull request #434 from DidierMalenfant/master
Ignoring files generated when creating an Xcode project via cmake.
2014-08-25 14:09:44 +02:00
Didier Malenfant
b7a514886d Ignoring files generated when creating an Xcode project via cmake. 2014-08-23 20:37:32 -07:00
Tsuda Kageyu
0e6d8617ae Revert "Removed an ambiguous cast from FileNameHandle to FileName."
This reverts commit 9af7601bae.
2014-08-23 00:04:33 +09:00
Tsuda Kageyu
dfbaee4103 Removed an ambiguous cast from FileNameHandle to FileName. 2014-08-23 00:04:33 +09:00
Tsuda Kageyu
f29c5f45f8 Workaround for 64-bit MP4 atoms. 2014-08-23 00:04:33 +09:00
Lukáš Lalinský
b1a8205561 Merge pull request #430 from TsudaKageyu/const-iterator
Reduce useless detach operations by making some non-const iterators cons...
2014-08-21 09:47:36 +02:00
Tsuda Kageyu
2971891c69 Reduce useless detach operations by making some non-const iterators const. 2014-08-21 16:35:35 +09:00
Lukáš Lalinský
d5e3d6ea7c Merge pull request #421 from TsudaKageyu/fix-wav-strip
Fixed a bug on stripping tags when saving .WAV files.
2014-08-20 10:12:43 +02:00
Lukáš Lalinský
20ce66f98b Merge pull request #422 from TsudaKageyu/detach
Added some missing detach()s to List<T>, ByteVector and String.
2014-08-20 10:09:08 +02:00
Stephen F. Booth
aff3351a25 Merge pull request #427 from TsudaKageyu/linebreak
Small fixes in line breaking.
2014-08-17 09:01:47 -04:00
Tsuda Kageyu
387890d956 Small fixes in line breaking. 2014-08-16 15:33:39 +09:00
Lukáš Lalinský
8765d40c2c Merge pull request #426 from TsudaKageyu/string-cmp
Added some operators to compare String to string literals.
2014-08-12 18:15:37 +02:00
Tsuda Kageyu
9bb0eb7ee9 Added some operators to compare String to string literals. 2014-08-12 23:58:45 +09:00
Lukáš Lalinský
d34c922c75 Merge pull request #425 from TsudaKageyu/fix-cmake
Removed a useless line from ConfigureChecks.cmake.
2014-08-12 09:42:19 +02:00
Tsuda Kageyu
3c727e091e Removed a useless line from ConfigureChecks.cmake. 2014-08-12 13:42:44 +09:00
Tsuda Kageyu
a055933e10 Unified the same debug messages. 2014-08-12 13:33:25 +09:00
Tsuda Kageyu
20c0aac309 Unified redundant string format functions. (backport from taglib2) 2014-08-11 01:09:07 +09:00
Tsuda Kageyu
929829b2b5 Removed useless strlen() and wcslen(). 2014-08-10 01:13:25 +09:00
Tsuda Kageyu
1944588595 Added some missing detach()s to List<T>, ByteVector and String. 2014-08-08 13:12:14 +09:00
Tsuda Kageyu
31a74927e4 Fixed a bug on stripping tags when saving .WAV files. 2014-08-07 18:49:53 +09:00
Tsuda Kageyu
972aa1feef Replaced codecvt with Win32 API. 2014-08-07 17:35:42 +09:00
Lukáš Lalinský
c23ee5331f Merge pull request #418 from TsudaKageyu/debug-msg
Fixed some debug messages.
2014-08-07 00:37:17 +02:00
Lukáš Lalinský
0f605be198 Merge pull request #414 from TsudaKageyu/fix-floattest
Fixed some mistakes in the CMake module for float types.
2014-08-07 00:36:59 +02:00
Tsuda Kageyu
590cb29f99 Improved the CMake check for float/double format. (Intel compiler support) 2014-08-06 20:57:04 +09:00
Tsuda Kageyu
286fd2abec Fixed some debug messages. 2014-08-06 16:13:15 +09:00
Tsuda Kageyu
f76d76728d Fixed some mistakes in the CMake module for float types. 2014-08-04 17:51:05 +09:00
Lukáš Lalinský
80f5436370 Merge pull request #413 from TsudaKageyu/msvc-tests3
Fixed a test to work with MSVC.
2014-08-04 09:09:20 +02:00
Tsuda Kageyu
043d2bbef5 Fixed a test to work with MSVC. 2014-08-04 15:11:50 +09:00
Lukáš Lalinský
cf59d51d93 Merge pull request #406 from TsudaKageyu/utf16-byteorder
Fixed handling UTF-16 byte order.
2014-07-23 10:21:37 +02:00
Lukáš Lalinský
9569b99e4c Merge pull request #405 from TsudaKageyu/missing-include
Added a missing #include.
2014-07-23 10:20:38 +02:00
Tsuda Kageyu
590405d878 Fixed handling UTF-16 byte order. 2014-07-23 15:36:16 +09:00
Tsuda Kageyu
c03b91aed3 Added a missing #include. 2014-07-23 14:56:18 +09:00
Lukáš Lalinský
ee2908a6cf Merge pull request #399 from TsudaKageyu/float-conv
Added float conversion functions to ByteVector.
2014-07-22 12:10:54 +02:00
Tsuda Kageyu
e29f1d39e7 Added float conversion functions to ByteVector.
Added CMake checks about IEEE754 compliance.
2014-07-17 12:09:14 +09:00
Lukáš Lalinský
b509106031 Merge pull request #398 from TsudaKageyu/fix-chap
Fixed some compilation error on MSVC in CHAP and CTOC frames support.
2014-07-16 22:30:10 +02:00
Tsuda Kageyu
47cd6fad4f Fixed some compilation error on MSVC in CHAP and CTOC frames support. 2014-07-17 01:43:56 +09:00
Lukáš Lalinský
fb24dfe7cf Merge pull request #393 from TsudaKageyu/msvc-tests
Modified some tests to work with MSVC.
2014-07-16 16:02:10 +02:00
Lukáš Lalinský
f42263e04a Merge pull request #392 from TsudaKageyu/zlib-source
Added a Windows-friendly build option ZLIB_SOURCE.
2014-07-16 15:58:52 +02:00
Lukáš Lalinský
43872f362d Merge pull request #390 from TsudaKageyu/vector-bounds
Fixed out-of-bounds access in findVector().
2014-07-16 15:57:55 +02:00
Lukáš Lalinský
73d3436570 Merge pull request #391 from TsudaKageyu/setlasterror
Added some SetLastError()s to get correct error codes in Win32.
2014-07-16 15:56:59 +02:00
Lukáš Lalinský
1e50b8dc25 Merge pull request #394 from ufleisch/master
Add genres 148 to 191 to list of ID3v1 genres.
2014-07-16 15:54:03 +02:00
Lukáš Lalinský
4a585c5258 Merge remote-tracking branch 'krejclu6/krejclu6_chapters' 2014-07-16 15:52:41 +02:00
Tsuda Kageyu
5f738a9819 Changed to skip the ZLib check when ZLIB_SOURCE is set. 2014-07-09 09:52:08 +09:00
Tsuda Kageyu
d7995b807f Fixed the tests to work with MSVC when UNICODE is set. 2014-06-24 01:29:15 +09:00
Urs Fleisch
3e60e339a4 Add genres 148 to 191 to list of ID3v1 genres.
This genres were added for Winamp version 5.6, see
<http://en.wikipedia.org/wiki/ID3#List_of_genres>.
2014-06-21 12:34:42 +02:00
Tsuda Kageyu
5feabe0988 Updated INSTALL. 2014-06-20 10:36:09 +09:00
Tsuda Kageyu
29a5129096 Modified some tests to work with MSVC. 2014-06-20 09:06:26 +09:00
Tsuda Kageyu
fadb57e4cd Added a Windows-friendly build option ZLIB_SOURCE. 2014-06-19 19:54:12 +09:00
Tsuda Kageyu
653b631aea Added a check for patternSize and removed a useless check. 2014-06-19 16:57:36 +09:00
Tsuda Kageyu
cb3abf15ad Added a test for out-of-bounds access in findVector(). 2014-06-19 13:22:50 +09:00
Tsuda Kageyu
29377fc8f1 Added some SetLastError()s to get correct error codes in Win32. 2014-06-19 12:58:07 +09:00
Tsuda Kageyu
663f10d51d Fixed out-of-bounds access in findVector(). 2014-06-19 12:19:18 +09:00
Lukas Krejci
a192db07c0 Code clean-up. 2014-06-11 17:52:05 +02:00
Lukas Krejci
459adc69ad Merge branch 'krejclu6_chapters' of https://github.com/krejclu6/taglib into krejclu6_chapters
Conflicts:
	taglib/mpeg/id3v2/id3v2framefactory.cpp
	tests/test_id3v2.cpp
2014-05-18 16:29:54 +02:00
Lukas Krejci
500b3e630b Uncommenting unit test. 2014-05-18 16:15:35 +02:00
Lukas Krejci
5c5c89e8d9 Added functions for work with embedded frames.
Added embedded frames parsing.

Added embedded frames rendering.
Modified constructor of CHAP and CTOC frame, so it can accept list of embedded frames.

Added unit tests for CHAP and CTOC frames parsing and rendering (with support of embedded frames).
Fixed bugs in rendering of CTOC frames.
Added functions for adding and removing child elements in CTOC frames.
2014-05-18 16:15:35 +02:00
Lukáš Krejčí
140fb2b3f6 Removed duplicated CHAP frame testing funtion. 2014-05-18 16:15:35 +02:00
Lukáš Krejčí
98ed58f910 Changed copyright and e-mail in modified files. 2014-05-18 16:15:35 +02:00
Lukáš Krejčí
7d99b8276a Fixed errors in ChapterFrame constructor.
Fixed errors in ChapterFrame method renderFields.
Fixed errors in TableOfContentsFrame method parseFields.
Added ChapterFrame and TableOfContentsFrame headers and sources to CMakeLists.txt.
Added some basic testing of CHAP and CTOC frames parsing.
2014-05-18 16:15:35 +02:00
Lukáš Krejčí
17841e89ae Fixed error in childElements function. 2014-05-18 16:14:00 +02:00
Lukáš Krejčí
c5f9258462 Finished parseFields, renderFields and asProperty methods of ChapterFrame and TableOfContentsFrame classes.
Methods setElementID of ChapterFrame and TableOfContentsFrame classes now automatically terminates new element ID with null.
2014-05-18 16:14:00 +02:00
Lukáš Krejčí
3a1040d55b Added basic members of ChapterFrame and TableOfContentsFrame classes.
Fixed minor bugs in ChapterFrame and TableOfContentsFrame headers.
2014-05-18 16:11:55 +02:00
Lukáš Krejčí
0864634ea6 Created CPP and H files for CTOC and CHAP frames. 2014-05-18 16:11:55 +02:00
Stephen F. Booth
f27e0aaecf Merge pull request #370 from supermihi/fix162v2
Fix #162: Xiph and APE generic getters return space-concatenated values
2014-04-24 21:35:50 -04:00
Lukáš Lalinský
ecc768d459 Merge pull request #380 from TsudaKageyu/fix-comment
Removed a wrong note from a comment in tstring.h.
2014-04-11 10:33:56 +02:00
Tsuda Kageyu
fabc411cb9 Removed a wrong note from a comment in tstring.h. 2014-04-11 17:16:55 +09:00
Lukáš Lalinský
2d528d844a Merge pull request #356 from TsudaKageyu/string-comment
Added some comment about UTF-16 encoding to String::data().
2014-04-10 20:05:55 +02:00
Lukáš Lalinský
c65753c766 Add ALBUMARTIST property mapping for WMA 2014-04-10 20:05:14 +02:00
Lukáš Lalinský
7e10b13962 Merge pull request #372 from TsudaKageyu/cmake-warning
Suppressed the warning "Policy CMP0022 is not set" on CMake 2.8.12.
2014-04-10 20:04:17 +02:00
Lukáš Lalinský
05e26119f7 Merge pull request #377 from amigadave/master
Add ALBUMARTIST property mapping for MP4
2014-04-10 20:01:36 +02:00
David King
95a59c416b Add ALBUMARTIST property mapping for MP4
https://code.google.com/p/mp4v2/wiki/iTunesMetadata
2014-04-10 17:33:29 +01:00
Tsuda Kageyu
3f557be608 Suppressed the warning "Policy CMP0022 is not set" on CMake 2.8.12. 2014-04-05 21:08:21 +09:00
Lukáš Lalinský
9ca7877587 Merge pull request #371 from TsudaKageyu/cmake-ver
Changed cmake_minimum_required from 2.6.0 to 2.8.0.
2014-04-05 13:07:58 +02:00
Tsuda Kageyu
a93ee10134 Changed cmake_minimum_required from 2.6.0 to 2.8.0. 2014-04-05 19:44:47 +09:00
Michael Helmling
1723e9b18a Fix #162: Xiph and APE generic getters return space-concatenated values 2014-04-03 21:07:29 +02:00
Lukáš Lalinský
16b8688c1a Merge pull request #362 from ufleisch/master
Add support for synchronized lyrics (SYLT) and event timing codes (ETCO) for ID3v2
2014-03-30 19:21:47 +02:00
Urs Fleisch
3b60af2c0b Add support for ID3v2 ETCO frames (event timing codes). 2014-03-30 09:28:14 +02:00
Urs Fleisch
eba99c3a70 Add support for ID3v2 SYLT frames (synchronized lyrics). 2014-03-30 09:26:03 +02:00
Tsuda Kageyu
6bcd422afa Small fix of a comment 2014-03-14 15:12:03 +09:00
Tsuda Kageyu
c357d293a1 Added some comment about UTF-16 encoding to String::data(). 2014-03-13 14:38:03 +09:00
Stephen F. Booth
cfb43223dc Merge pull request #354 from TsudaKageyu/opus-bitrate
opus: Estimate the bitrate if possible
2014-03-09 21:37:50 -04:00
Tsuda Kageyu
72cd379f30 opus: Estimate the bitrate if possible 2014-03-10 09:37:10 +09:00
Lukáš Lalinský
a3d724af27 Merge pull request #352 from TsudaKageyu/tagreader-sprintf
Avoid using sprintf() in tagreader.cpp to fix an MSVC warning.
2014-02-19 16:34:42 +01:00
Tsuda Kageyu
05e0081414 Avoid using sprintf() in tagreader.cpp to fix an MSVC warning. 2014-02-19 23:35:23 +09:00
Lukáš Lalinský
b658f95e06 Merge pull request #338 from funman/master
Also build/install taglib_c.pc when targetting windows
2014-02-17 08:46:13 +01:00
Lukáš Lalinský
2758aed34d Merge pull request #348 from TsudaKageyu/string-memcpy
Reduced a useless memory copy in tstring.cpp.
2014-02-17 08:45:49 +01:00
Lukáš Lalinský
12b25868b7 Merge pull request #349 from TsudaKageyu/install-typo
Fixed a typo in INSTALL.
2014-02-17 08:44:54 +01:00
Tsuda Kageyu
1deff52d95 Fixed a typo in INSTALL. 2014-02-17 11:22:40 +09:00
Tsuda Kageyu
69eaa75f64 Reduced a useless memory copy in tstring.cpp. 2014-02-15 20:25:00 +09:00
Lukáš Lalinský
3e0c73bdbc Merge pull request #347 from TsudaKageyu/xm-seek
Changed XM::File::save() to use seek() properly.
2014-02-14 17:21:49 +01:00
Tsuda Kageyu
9e94f8bb6c Changed XM::File::save() to use seek() properly. 2014-02-14 19:22:53 +09:00
Lukáš Lalinský
aa49723b48 Merge pull request #330 from TsudaKageyu/fix-emptystring
Fixed a bug reported at #328
2014-02-05 00:02:47 -08:00
Tsuda Kageyu
167513ae57 Avoid using &d->data[0] when d->data is empty. 2014-02-05 10:35:07 +09:00
Tsuda Kageyu
d0f3e9b186 Use std::string::c_str() rather than &s[0] where a const pointer is required. 2014-01-24 09:48:39 +09:00
Stephen F. Booth
4fc020e0d3 Merge pull request #340 from mineo/patch-1
Remove a superfluos 'x'
2014-01-18 13:12:21 -08:00
Wieland Hoffmann
db147e672e Remove a superfluos 'x' 2014-01-18 18:11:31 +01:00
Rafaël Carré
c45a0694f9 Also build/install taglib.pc when targetting windows
.pc file is notably used when cross-compiling windows software
2014-01-13 22:49:17 +01:00
Tsuda Kageyu
584bbc7c78 Fixed conversion from empty String to ByteVector 2013-12-04 09:37:59 +09:00
Lukáš Lalinský
10b804e0ae Merge pull request #322 from TsudaKageyu/mailaddress
Fixed a mail address
2013-11-24 23:25:19 -08:00
Tsuda Kageyu
4f8ab4b63f Fixed a mail address 2013-11-25 14:12:34 +09:00
Stephen F. Booth
5593c251c8 Merge pull request #321 from TsudaKageyu/nominmax
Fixed warnings in MinGW
2013-11-23 19:24:55 -08:00
Tsuda Kageyu
7d7a7be115 Fixed warnings in MinGW 2013-11-23 23:42:20 +09:00
Stephen F. Booth
f733077917 Merge pull request #307 from TsudaKageyu/string-abi
Fixed the ABI breakage in TagLib::String
2013-11-15 15:04:30 -08:00
Stephen F. Booth
670493f97f Merge pull request #310 from TsudaKageyu/backslash
Replaced wrongly used slashes with backslashes in some comments
2013-11-15 03:55:04 -08:00
Tsuda Kageyu
d4adc22922 Replaced wrongly used slashes with backslashes in some comments 2013-11-15 15:31:23 +09:00
Stephen F. Booth
34f8cd8a90 Merge pull request #309 from TsudaKageyu/fix-replace
Rewrote ByteVector::replace() simpler (fix for the issue #308)
2013-11-14 07:01:29 -08:00
Tsuda Kageyu
4a7d31c87b Rewrote ByteVector::replace() simpler 2013-11-14 14:58:14 +09:00
Tsuda Kageyu
3bf30af66c Fixed ABI breakage in TagLib::String 2013-11-06 17:01:21 +09:00
Lukáš Lalinský
ee9720a997 Merge pull request #304 from funman/master
Win32: avoid symbol lookup if UNICODE is defined
2013-10-28 01:12:48 -07:00
Rafaël Carré
69e58b5f3f Win32: avoid symbol lookup if UNICODE is defined 2013-10-28 04:39:04 +01:00
Duke Yin
de17843d8c Update tests 2013-10-17 15:14:29 -04:00
Duke Yin
944143a91b Handle iTunes ID3v2.3 hacks
iTunes writes the 2.4 frames TSOA, TSOT, TSOP to 2.3 files.  (It
additionally defines TSO2 and TSOC for both 2.3 and 2.4.)  TagLib should
not delete these frames.
2013-10-16 18:48:49 -04:00
Lukas Krejci
1ce5385e30 Uncommenting unit test. 2013-10-13 18:38:54 +02:00
Lukas Krejci
5ed2d88f78 Added functions for work with embedded frames.
Added embedded frames parsing.

Added embedded frames rendering.
Modified constructor of CHAP and CTOC frame, so it can accept list of embedded frames.

Added unit tests for CHAP and CTOC frames parsing and rendering (with support of embedded frames).
Fixed bugs in rendering of CTOC frames.
Added functions for adding and removing child elements in CTOC frames.
2013-10-13 18:27:43 +02:00
Lukáš Krejčí
cbd6f73431 Removed duplicated CHAP frame testing funtion. 2013-05-04 21:25:55 +02:00
Lukáš Krejčí
65006952f3 Changed copyright and e-mail in modified files. 2013-04-27 16:09:15 +02:00
Lukáš Krejčí
4815dbba68 Fixed errors in ChapterFrame constructor.
Fixed errors in ChapterFrame method renderFields.
Fixed errors in TableOfContentsFrame method parseFields.
Added ChapterFrame and TableOfContentsFrame headers and sources to CMakeLists.txt.
Added some basic testing of CHAP and CTOC frames parsing.
2013-04-27 15:42:23 +02:00
Lukáš Krejčí
bcad792e75 Fixed error in childElements function. 2013-04-26 23:16:06 +02:00
Lukáš Krejčí
fcfd9f59fe Finished parseFields, renderFields and asProperty methods of ChapterFrame and TableOfContentsFrame classes.
Methods setElementID of ChapterFrame and TableOfContentsFrame classes now automatically terminates new element ID with null.
2013-04-21 16:16:57 +02:00
Lukáš Krejčí
4be1279430 Added basic members of ChapterFrame and TableOfContentsFrame classes.
Fixed minor bugs in ChapterFrame and TableOfContentsFrame headers.
2013-04-20 16:49:57 +02:00
Lukáš Krejčí
08ae0e8c63 Created CPP and H files for CTOC and CHAP frames. 2013-04-20 15:52:52 +02:00
308 changed files with 16960 additions and 7867 deletions

18
.astylerc Normal file
View File

@@ -0,0 +1,18 @@
--suffix=none
--style=kr
--indent=spaces=2
--indent-col1-comments
--min-conditional-indent=0
--attach-extern-c
--attach-namespaces
--indent-namespaces
--pad-oper
--unpad-paren
--align-pointer=name
--align-reference=name
--max-instatement-indent=40
--break-closing-brackets
--remove-brackets
--convert-tabs
--max-code-length=100
--break-after-logical

21
.editorconfig Normal file
View File

@@ -0,0 +1,21 @@
# EditorConfig is awesome: http://EditorConfig.org
# top-most EditorConfig file
root = true
# Non-specified newlines with a newline ending every file
[*]
insert_final_newline = true
# 2 space indentation
[*.{h,cpp,tcc,cmake,yml}]
indent_style = space
indent_size = 2
# Trim traling whitespaces
[*.{h,cpp,tcc,cmake,yml}]
trim_trailing_whitespace = true
# UTF-8 without BOM
[*]
charset = utf-8

2
.gitignore vendored
View File

@@ -42,3 +42,5 @@ CMakeFiles/
/taglib/tag.dir/Release
/ALL_BUILD.dir
/ZERO_CHECK.dir
taglib.xcodeproj
CMakeScripts

View File

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

View File

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

View File

@@ -1,64 +1,98 @@
cmake_minimum_required(VERSION 2.8.0 FATAL_ERROR)
project(taglib)
cmake_minimum_required(VERSION 2.6.0 FATAL_ERROR)
if(NOT ${CMAKE_VERSION} VERSION_LESS 2.8.12)
cmake_policy(SET CMP0022 OLD)
endif()
option(ENABLE_STATIC "Make static version of libtag" OFF)
if(ENABLE_STATIC)
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()
option(BUILD_SHARED_LIBS "Build shared libraries" OFF)
if(NOT BUILD_SHARED_LIBS)
add_definitions(-DTAGLIB_STATIC)
set(BUILD_SHARED_LIBS OFF)
else()
set(BUILD_SHARED_LIBS ON)
endif()
OPTION(ENABLE_STATIC_RUNTIME "Visual Studio, link with runtime statically" OFF)
option(ENABLE_STATIC_RUNTIME "Visual Studio, link with runtime statically" OFF)
option(VISIBILITY_HIDDEN "Build with -fvisibility=hidden" OFF)
option(ENABLE_CCACHE "Use ccache when building libtag" OFF)
if(ENABLE_CCACHE)
find_program(CCACHE_FOUND ccache)
if(CCACHE_FOUND)
set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE ccache)
set_property(GLOBAL PROPERTY RULE_LAUNCH_LINK ccache)
endif()
endif()
option(VISIBILITY_HIDDEN "Build with -fvisibility=hidden" OFF)
if(VISIBILITY_HIDDEN)
add_definitions (-fvisibility=hidden)
add_definitions(-fvisibility=hidden)
endif()
option(BUILD_TESTS "Build the test suite" OFF)
option(BUILD_EXAMPLES "Build the examples" OFF)
option(BUILD_TESTS "Build the test suite" OFF)
option(BUILD_EXAMPLES "Build the examples" OFF)
option(BUILD_BINDINGS "Build the bindings" ON)
option(NO_ITUNES_HACKS "Disable workarounds for iTunes bugs" OFF)
option(NO_ITUNES_HACKS "Disable workarounds for iTunes bugs" OFF)
add_definitions(-DHAVE_CONFIG_H)
set(TESTS_DIR "${CMAKE_CURRENT_SOURCE_DIR}/tests/")
## the following are directories where stuff will be installed to
set(LIB_SUFFIX "" CACHE STRING "Define suffix of directory name (32/64)")
set(EXEC_INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}" CACHE PATH "Base directory for executables and libraries" FORCE)
set(BIN_INSTALL_DIR "${EXEC_INSTALL_PREFIX}/bin" CACHE PATH "The subdirectory to the binaries prefix (default prefix/bin)" FORCE)
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})" FORCE)
set(INCLUDE_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/include" CACHE PATH "The subdirectory to the header prefix" FORCE)
set(EXEC_INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}" CACHE PATH "Base directory for executables and libraries")
set(BIN_INSTALL_DIR "${EXEC_INSTALL_PREFIX}/bin" CACHE PATH "The subdirectory to the binaries prefix (default prefix/bin)")
set(LIB_INSTALL_DIR "${EXEC_INSTALL_PREFIX}/lib${LIB_SUFFIX}" CACHE PATH "The subdirectory relative to the install prefix where libraries will be installed (default is /lib${LIB_SUFFIX})")
set(INCLUDE_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/include" CACHE PATH "The subdirectory to the header prefix")
if(APPLE)
option(BUILD_FRAMEWORK "Build an OS X framework" OFF)
set(FRAMEWORK_INSTALL_DIR "/Library/Frameworks" CACHE STRING "Directory to install frameworks to.")
option(BUILD_FRAMEWORK "Build an OS X framework" OFF)
set(FRAMEWORK_INSTALL_DIR "/Library/Frameworks" CACHE STRING "Directory to install frameworks to.")
endif()
if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall")
endif()
if (MSVC AND ENABLE_STATIC_RUNTIME)
if(MSVC AND ENABLE_STATIC_RUNTIME)
foreach(flag_var CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE CMAKE_CXX_FLAGS_MINSIZEREL CMAKE_CXX_FLAGS_RELWITHDEBINFO)
string(REGEX REPLACE "/MD" "/MT" ${flag_var} "${${flag_var}}")
endforeach(flag_var)
endif()
set(TAGLIB_LIB_MAJOR_VERSION "1")
set(TAGLIB_LIB_MINOR_VERSION "9")
set(TAGLIB_LIB_PATCH_VERSION "1")
# Read version information from file taglib/toolkit/taglib.h into variables
# TAGLIB_LIB_MAJOR_VERSION, TAGLIB_LIB_MINOR_VERSION, TAGLIB_LIB_PATCH_VERSION.
foreach(version_part MAJOR MINOR PATCH)
set(version_var_name "TAGLIB_${version_part}_VERSION")
file(STRINGS taglib/toolkit/taglib.h version_line
REGEX "^#define +${version_var_name}")
if(NOT version_line)
message(FATAL_ERROR "${version_var_name} not found in taglib.h")
endif()
string(REGEX MATCH "${version_var_name} +([^ ]+)" result ${version_line})
set(TAGLIB_LIB_${version_part}_VERSION ${CMAKE_MATCH_1})
endforeach(version_part)
set(TAGLIB_LIB_VERSION_STRING "${TAGLIB_LIB_MAJOR_VERSION}.${TAGLIB_LIB_MINOR_VERSION}.${TAGLIB_LIB_PATCH_VERSION}")
# Only used to force cmake rerun when taglib.h changes.
configure_file(taglib/toolkit/taglib.h ${CMAKE_CURRENT_BINARY_DIR}/taglib.h.stamp)
if("${TAGLIB_LIB_PATCH_VERSION}" EQUAL "0")
set(TAGLIB_LIB_VERSION_STRING "${TAGLIB_LIB_MAJOR_VERSION}.${TAGLIB_LIB_MINOR_VERSION}")
else()
set(TAGLIB_LIB_VERSION_STRING "${TAGLIB_LIB_MAJOR_VERSION}.${TAGLIB_LIB_MINOR_VERSION}.${TAGLIB_LIB_PATCH_VERSION}")
endif()
# 1. If the library source code has changed at all since the last update, then increment revision.
# 2. If any interfaces have been added, removed, or changed since the last update, increment current, and set revision to 0.
# 3. If any interfaces have been added since the last public release, then increment age.
# 4. If any interfaces have been removed since the last public release, then set age to 0.
set(TAGLIB_SOVERSION_CURRENT 15)
# 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_REVISION 0)
set(TAGLIB_SOVERSION_AGE 14)
set(TAGLIB_SOVERSION_AGE 17)
math(EXPR TAGLIB_SOVERSION_MAJOR "${TAGLIB_SOVERSION_CURRENT} - ${TAGLIB_SOVERSION_AGE}")
math(EXPR TAGLIB_SOVERSION_MINOR "${TAGLIB_SOVERSION_AGE}")
@@ -67,22 +101,27 @@ math(EXPR TAGLIB_SOVERSION_PATCH "${TAGLIB_SOVERSION_REVISION}")
include(ConfigureChecks.cmake)
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")
install(PROGRAMS "${CMAKE_CURRENT_BINARY_DIR}/taglib-config" DESTINATION "${BIN_INSTALL_DIR}")
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})
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}")
endif()
if(NOT WIN32 AND NOT BUILD_FRAMEWORK)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/taglib.pc.cmake ${CMAKE_CURRENT_BINARY_DIR}/taglib.pc )
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/taglib.pc DESTINATION ${LIB_INSTALL_DIR}/pkgconfig)
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)
endif()
include_directories(${CMAKE_CURRENT_BINARY_DIR})
configure_file(config.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config.h)
configure_file(config.h.cmake "${CMAKE_CURRENT_BINARY_DIR}/config.h")
if(WITH_ASF)
set(TAGLIB_WITH_ASF TRUE)
@@ -91,30 +130,35 @@ if(WITH_MP4)
set(TAGLIB_WITH_MP4 TRUE)
endif()
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)
configure_file(taglib/taglib_config.h.cmake "${CMAKE_CURRENT_BINARY_DIR}/taglib_config.h")
add_subdirectory(taglib)
add_subdirectory(bindings)
if(BUILD_TESTS)
enable_testing()
add_subdirectory(tests)
endif(BUILD_TESTS)
add_subdirectory(examples)
add_subdirectory(taglib)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/Doxyfile.cmake ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile)
if(BUILD_BINDINGS)
add_subdirectory(bindings)
endif()
if(BUILD_TESTS AND NOT BUILD_SHARED_LIBS)
enable_testing()
add_subdirectory(tests)
endif()
if(BUILD_EXAMPLES)
add_subdirectory(examples)
endif()
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/Doxyfile.cmake" "${CMAKE_CURRENT_BINARY_DIR}/Doxyfile")
file(COPY doc/taglib.png DESTINATION doc)
add_custom_target(docs doxygen)
# uninstall target
configure_file(
"${CMAKE_CURRENT_SOURCE_DIR}/cmake_uninstall.cmake.in"
"${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake"
IMMEDIATE @ONLY)
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/cmake_uninstall.cmake.in" "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake" IMMEDIATE @ONLY)
add_custom_target(uninstall
COMMAND ${CMAKE_COMMAND} -P "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake")
if(NOT TARGET uninstall)
add_custom_target(uninstall COMMAND "${CMAKE_COMMAND}" -P "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake")
endif()

View File

@@ -1,107 +1,106 @@
include(CheckIncludeFile)
include(CheckIncludeFiles)
include(CheckSymbolExists)
include(CheckFunctionExists)
include(CheckLibraryExists)
include(CheckTypeSize)
include(CheckCXXSourceCompiles)
include(TestBigEndian)
# Check if the size of integral types are suitable.
# Check if the size of numeric types are suitable.
check_type_size("short" SIZEOF_SHORT)
if(NOT ${SIZEOF_SHORT} EQUAL 2)
MESSAGE(FATAL_ERROR "TagLib requires that short is 16-bit wide.")
message(FATAL_ERROR "TagLib requires that short is 16-bit wide.")
endif()
check_type_size("int" SIZEOF_INT)
if(NOT ${SIZEOF_INT} EQUAL 4)
MESSAGE(FATAL_ERROR "TagLib requires that int is 32-bit wide.")
message(FATAL_ERROR "TagLib requires that int is 32-bit wide.")
endif()
check_type_size("long long" SIZEOF_LONGLONG)
if(NOT ${SIZEOF_LONGLONG} EQUAL 8)
MESSAGE(FATAL_ERROR "TagLib requires that long long is 64-bit wide.")
message(FATAL_ERROR "TagLib requires that long long is 64-bit wide.")
endif()
check_type_size("wchar_t" SIZEOF_WCHAR_T)
if(${SIZEOF_WCHAR_T} LESS 2)
MESSAGE(FATAL_ERROR "TagLib requires that wchar_t is sufficient to store a UTF-16 char.")
message(FATAL_ERROR "TagLib requires that wchar_t is sufficient to store a UTF-16 char.")
endif()
# Determine the CPU byte order.
check_type_size("float" SIZEOF_FLOAT)
if(NOT ${SIZEOF_FLOAT} EQUAL 4)
message(FATAL_ERROR "TagLib requires that float is 32-bit wide.")
endif()
test_big_endian(IS_BIG_ENDIAN)
check_type_size("double" SIZEOF_DOUBLE)
if(NOT ${SIZEOF_DOUBLE} EQUAL 8)
message(FATAL_ERROR "TagLib requires that double is 64-bit wide.")
endif()
if(NOT IS_BIG_ENDIAN)
set(SYSTEM_BYTEORDER 1)
else()
set(SYSTEM_BYTEORDER 2)
# 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() {
int main() {
std::atomic<unsigned int> x;
x.fetch_add(1);
x.fetch_sub(1);
return 0;
return 0;
}
" HAVE_STD_ATOMIC)
if(NOT HAVE_STD_ATOMIC)
check_cxx_source_compiles("
#include <boost/atomic.hpp>
int main() {
boost::atomic<unsigned int> x(1);
x.fetch_add(1);
x.fetch_sub(1);
return 0;
}
" HAVE_BOOST_ATOMIC)
find_package(Boost COMPONENTS atomic)
if(Boost_ATOMIC_FOUND)
set(HAVE_BOOST_ATOMIC 1)
else()
set(HAVE_BOOST_ATOMIC 0)
endif()
if(NOT HAVE_BOOST_ATOMIC)
check_cxx_source_compiles("
int main() {
int main() {
volatile int x;
__sync_add_and_fetch(&x, 1);
int y = __sync_sub_and_fetch(&x, 1);
return 0;
return 0;
}
" HAVE_GCC_ATOMIC)
if(NOT HAVE_GCC_ATOMIC)
check_cxx_source_compiles("
#include <libkern/OSAtomic.h>
int main() {
int main() {
volatile int32_t x;
OSAtomicIncrement32Barrier(&x);
int32_t y = OSAtomicDecrement32Barrier(&x);
return 0;
return 0;
}
" HAVE_MAC_ATOMIC)
if(NOT HAVE_MAC_ATOMIC)
check_cxx_source_compiles("
#include <windows.h>
int main() {
int main() {
volatile LONG x;
InterlockedIncrement(&x);
LONG y = InterlockedDecrement(&x);
return 0;
return 0;
}
" HAVE_WIN_ATOMIC)
if(NOT HAVE_WIN_ATOMIC)
check_cxx_source_compiles("
#include <ia64intrin.h>
int main() {
int main() {
volatile int x;
__sync_add_and_fetch(&x, 1);
int y = __sync_sub_and_fetch(&x, 1);
return 0;
return 0;
}
" HAVE_IA64_ATOMIC)
endif()
@@ -112,115 +111,138 @@ endif()
# Determine which kind of byte swap functions your compiler supports.
# GCC's __builtin_bswap* should be checked individually
# because some of them can be missing depends on the GCC version.
check_cxx_source_compiles("
#include <boost/endian/conversion.hpp>
int main() {
__builtin_bswap16(0);
return 0;
boost::endian::endian_reverse(static_cast<uint16_t>(1));
boost::endian::endian_reverse(static_cast<uint32_t>(1));
boost::endian::endian_reverse(static_cast<uint64_t>(1));
return 0;
}
" HAVE_GCC_BYTESWAP_16)
" HAVE_BOOST_BYTESWAP)
check_cxx_source_compiles("
int main() {
__builtin_bswap32(0);
return 0;
}
" HAVE_GCC_BYTESWAP_32)
check_cxx_source_compiles("
int main() {
__builtin_bswap64(0);
return 0;
}
" HAVE_GCC_BYTESWAP_64)
if(NOT HAVE_GCC_BYTESWAP_16 OR NOT HAVE_GCC_BYTESWAP_32 OR NOT HAVE_GCC_BYTESWAP_64)
if(NOT HAVE_BOOST_BYTESWAP)
check_cxx_source_compiles("
#include <byteswap.h>
int main() {
__bswap_16(0);
__bswap_32(0);
__bswap_64(0);
return 0;
__builtin_bswap16(0);
__builtin_bswap32(0);
__builtin_bswap64(0);
return 0;
}
" HAVE_GLIBC_BYTESWAP)
" HAVE_GCC_BYTESWAP)
if(NOT HAVE_GLIBC_BYTESWAP)
if(NOT HAVE_GCC_BYTESWAP)
check_cxx_source_compiles("
#include <stdlib.h>
#include <byteswap.h>
int main() {
_byteswap_ushort(0);
_byteswap_ulong(0);
_byteswap_uint64(0);
return 0;
__bswap_16(0);
__bswap_32(0);
__bswap_64(0);
return 0;
}
" HAVE_MSC_BYTESWAP)
" HAVE_GLIBC_BYTESWAP)
if(NOT HAVE_MSC_BYTESWAP)
if(NOT HAVE_GLIBC_BYTESWAP)
check_cxx_source_compiles("
#include <libkern/OSByteOrder.h>
#include <stdlib.h>
int main() {
OSSwapInt16(0);
OSSwapInt32(0);
OSSwapInt64(0);
return 0;
_byteswap_ushort(0);
_byteswap_ulong(0);
_byteswap_uint64(0);
return 0;
}
" HAVE_MAC_BYTESWAP)
" HAVE_MSC_BYTESWAP)
if(NOT HAVE_MAC_BYTESWAP)
if(NOT HAVE_MSC_BYTESWAP)
check_cxx_source_compiles("
#include <sys/endian.h>
#include <libkern/OSByteOrder.h>
int main() {
swap16(0);
swap32(0);
swap64(0);
return 0;
OSSwapInt16(0);
OSSwapInt32(0);
OSSwapInt64(0);
return 0;
}
" HAVE_OPENBSD_BYTESWAP)
" HAVE_MAC_BYTESWAP)
if(NOT HAVE_MAC_BYTESWAP)
check_cxx_source_compiles("
#include <sys/endian.h>
int main() {
swap16(0);
swap32(0);
swap64(0);
return 0;
}
" HAVE_OPENBSD_BYTESWAP)
endif()
endif()
endif()
endif()
endif()
# Determine whether your compiler supports some safer version of sprintf.
# Determine whether your compiler supports some safer version of vsprintf.
check_cxx_source_compiles("
#include <cstdio>
int main() { char buf[20]; snprintf(buf, 20, \"%d\", 1); return 0; }
" HAVE_SNPRINTF)
#include <cstdarg>
int main() {
char buf[20];
va_list args;
vsnprintf(buf, 20, \"%d\", args);
return 0;
}
" HAVE_VSNPRINTF)
if(NOT HAVE_SNPRINTF)
if(NOT HAVE_VSNPRINTF)
check_cxx_source_compiles("
#include <cstdio>
int main() { char buf[20]; sprintf_s(buf, \"%d\", 1); return 0; }
" HAVE_SPRINTF_S)
#include <cstdarg>
int main() {
char buf[20];
va_list args;
vsprintf_s(buf, \"%d\", args);
return 0;
}
" HAVE_VSPRINTF_S)
endif()
# Determine whether your compiler supports codecvt.
# Determine whether your compiler supports ISO _strdup.
check_cxx_source_compiles("
#include <codecvt>
int main() {
std::codecvt_utf8_utf16<wchar_t> x;
return 0;
#include <cstring>
int main() {
_strdup(0);
return 0;
}
" HAVE_STD_CODECVT)
" HAVE_ISO_STRDUP)
# Check for libz using the cmake supplied FindZLIB.cmake
# Determine whether zlib is installed.
find_package(ZLIB)
if(ZLIB_FOUND)
set(HAVE_ZLIB 1)
else()
set(HAVE_ZLIB 0)
if(NOT ZLIB_SOURCE)
find_package(ZLIB)
if(ZLIB_FOUND)
set(HAVE_ZLIB 1)
else()
set(HAVE_ZLIB 0)
endif()
if(NOT HAVE_ZLIB)
find_package(Boost COMPONENTS iostreams zlib)
if(Boost_IOSTREAMS_FOUND AND Boost_ZLIB_FOUND)
set(HAVE_BOOST_ZLIB 1)
else()
set(HAVE_BOOST_ZLIB 0)
endif()
endif()
endif()
# Determine whether CppUnit is installed.
set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules)
find_package(CppUnit)
if(NOT CppUnit_FOUND AND BUILD_TESTS)
message(STATUS "CppUnit not found, disabling tests.")
set(BUILD_TESTS OFF)
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()

12
INSTALL
View File

@@ -5,7 +5,7 @@ TagLib uses the CMake build system. As a user, you will most likely want to
build TagLib in release mode and install it into a system-wide location.
This can be done using the following commands:
cmake -DCMAKE_INSTALL_PREFIX=/usr/local -DCMAKE_RELEASE_TYPE=Release .
cmake -DCMAKE_INSTALL_PREFIX=/usr/local -DCMAKE_BUILD_TYPE=Release .
make
sudo make install
@@ -37,7 +37,7 @@ For a 10.6 Snow Leopard static library with both 32-bit and 64-bit code, use:
cmake -DCMAKE_BUILD_TYPE=Release \
-DCMAKE_OSX_DEPLOYMENT_TARGET=10.6 \
-DCMAKE_OSX_ARCHITECTURES="i386;x86_64" \
-DENABLE_STATIC=ON \
-DBUILD_SHARED_LIBS=OFF \
-DCMAKE_INSTALL_PREFIX="<folder you want to build to>"
After 'make', and 'make install', add libtag.a to your XCode project, and add
@@ -72,6 +72,8 @@ Useful configuration options used with CMake (GUI and/or Command line):
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)
@@ -137,9 +139,9 @@ The easiest way is at the Command Prompt.
4. Select: Project Only
5. Select: Build Only INSTALL
To build a static library enable the following two options with CMake.
-DENABLE_STATIC=ON -DENABLE_STATIC_RUNTIME=ON
To build a static library, set the following two options with CMake.
-DBUILD_SHARED_LIBS=OFF -DENABLE_STATIC_RUNTIME=ON
Including ENABLE_STATIC_RUNTIME=ON indicates you want TagLib built using the
static runtime library, rather than the DLL form of the runtime.

113
NEWS
View File

@@ -1,3 +1,114 @@
TagLib 1.11.1 (Oct 24, 2016)
============================
* Fixed binary incompatible change in TagLib::String.
* Fixed reading ID3v2 CTOC frames with a lot of entries.
* Fixed seeking ByteVectorStream from the end.
TagLib 1.11 (Apr 29, 2016)
==========================
1.11:
* Fixed reading APE items with long keys.
* Fixed reading ID3v2 SYLT frames when description is empty.
1.11 BETA 2:
* Better handling of PCM WAV files with a 'fact' chunk.
* Better handling of corrupted APE tags.
* Efficient decoding of unsynchronized ID3v2 frames.
* Fixed text encoding when saving certain frames in ID3v2.3 tags.
* Fixed updating the size of RIFF files when removing chunks.
* Several smaller bug fixes and performance improvements.
1.11 BETA:
* New API for creating FileRef from IOStream.
* Added support for ID3v2 PCST and WFED frames.
* Added support for pictures in XiphComment.
* Added String::clear().
* Added FLAC::File::strip() for removing non-standard tags.
* Added alternative functions to XiphComment::removeField().
* Added BUILD_BINDINGS build option.
* Added ENABLE_CCACHE build option.
* Replaced ENABLE_STATIC build option with BUILD_SHARED_LIBS.
* Better handling of duplicate ID3v2 tags in all kinds of files.
* Better handling of duplicate tag chunks in WAV files.
* Better handling of duplicate tag chunks in AIFF files.
* Better handling of duplicate Vorbis comment blocks in FLAC files.
* Better handling of broken MPEG audio frames.
* Fixed crash when calling File::properties() after strip().
* Fixed crash when parsing certain MPEG files.
* Fixed crash when saving Ogg files.
* Fixed possible file corruptions when saving ASF files.
* Fixed possible file corruptions when saving FLAC files.
* Fixed possible file corruptions when saving MP4 files.
* Fixed possible file corruptions when saving MPEG files.
* Fixed possible file corruptions when saving APE files.
* Fixed possible file corruptions when saving Musepack files.
* Fixed possible file corruptions when saving WavPack files.
* Fixed updating the comment field of Vorbis comments.
* Fixed reading date and time in ID3v2.3 tags.
* Marked ByteVector::null and ByteVector::isNull() deprecated.
* Marked String::null and String::isNull() deprecated.
* Marked XiphComment::removeField() deprecated.
* Marked Ogg::Page::getCopyWithNewPageSequenceNumber() deprecated. It returns null.
* Marked custom integer types deprecated.
* Many smaller bug fixes and performance improvements.
TagLib 1.10 (Nov 11, 2015)
==========================
1.10:
* Added new options to the tagwriter example.
* Fixed self-assignment operator in some types.
* Fixed extraction of MP4 tag keys with an empty list.
1.10 BETA:
* New API for the audio length in milliseconds.
* Added support for ID3v2 ETCO and SYLT frames.
* Added support for album artist in PropertyMap API of MP4 files.
* Added support for embedded frames in ID3v2 CHAP and CTOC frames.
* Added support for AIFF-C files.
* Better handling of duplicate ID3v2 tags in MPEG files.
* Allowed generating taglib.pc on Windows.
* Added ZLIB_SOURCE build option.
* Fixed backwards-incompatible change in TagLib::String when constructing UTF16 strings.
* Fixed crash when parsing certain FLAC files.
* Fixed crash when encoding empty strings.
* Fixed saving of certain XM files on OS X.
* Changed Xiph and APE generic getters to return space-concatenated values.
* Fixed possible file corruptions when removing tags from WAV files.
* Added support for MP4 files with 64-bit atoms in certain 64-bit environments.
* Prevented ID3v2 padding from being too large.
* Fixed crash when parsing corrupted APE files.
* Fixed crash when parsing corrupted WAV files.
* Fixed crash when parsing corrupted Ogg FLAC files.
* Fixed crash when parsing corrupted MPEG files.
* Fixed saving empty tags in WAV files.
* Fixed crash when parsing corrupted Musepack files.
* Fixed possible memory leaks when parsing AIFF and WAV files.
* Fixed crash when parsing corrupted MP4 files.
* Stopped writing empty ID3v2 frames.
* Fixed possible file corruptions when saving WMA files.
* Added TagLib::MP4::Tag::isEmpty().
* Added accessors to manipulate MP4 tags.
* Fixed crash when parsing corrupted WavPack files.
* Fixed seeking MPEG frames.
* Fixed reading FLAC files with zero-sized padding blocks.
* Added support for reading the encoder information of WMA files.
* Added support for reading the codec of WAV files.
* Added support for multi channel WavPack files.
* Added support for reading the nominal bitrate of Ogg Speex files.
* Added support for VBR headers in MPEG files.
* Marked FLAC::File::streamInfoData() deprecated. It returns an empty ByteVector.
* Marked FLAC::File::streamLength() deprecated. It returns zero.
* Fixed possible file corruptions when adding an ID3v1 tag to FLAC files.
* Many smaller bug fixes and performance improvements.
TagLib 1.9.1 (Oct 8, 2013)
==========================
@@ -5,7 +116,7 @@ TagLib 1.9.1 (Oct 8, 2013)
* Fixed constructing String from ByteVector.
* Fixed compilation on MSVC with the /Zc:wchar_t- option.
* Fixed detecting of RIFF files with invalid chunk sizes.
* Added TagLib::MP4::PropertyMap::codec().
* Added TagLib::MP4::Properties::codec().
TagLib 1.9 (Oct 6, 2013)
========================

View File

@@ -61,7 +61,7 @@ install(TARGETS tag_c
PUBLIC_HEADER DESTINATION ${INCLUDE_INSTALL_DIR}/taglib
)
if(NOT WIN32 AND NOT BUILD_FRAMEWORK)
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)
endif()

View File

@@ -19,6 +19,10 @@
* USA *
***************************************************************************/
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include <stdlib.h>
#include <fileref.h>
#include <tfile.h>
@@ -40,18 +44,41 @@
using namespace TagLib;
static List<char *> strings;
static bool unicodeStrings = true;
static bool stringManagementEnabled = true;
namespace
{
List<char *> strings;
bool unicodeStrings = true;
bool stringManagementEnabled = true;
char *stringToCharArray(const String &s)
{
const std::string str = s.to8Bit(unicodeStrings);
#ifdef HAVE_ISO_STRDUP
return ::_strdup(str.c_str());
#else
return ::strdup(str.c_str());
#endif
}
String charArrayToString(const char *s)
{
return String(s, unicodeStrings ? String::UTF8 : String::Latin1);
}
}
void taglib_set_strings_unicode(BOOL unicode)
{
unicodeStrings = bool(unicode);
unicodeStrings = (unicode != 0);
}
void taglib_set_string_management_enabled(BOOL management)
{
stringManagementEnabled = bool(management);
stringManagementEnabled = (management != 0);
}
void taglib_free(void* pointer)
@@ -94,8 +121,6 @@ TagLib_File *taglib_file_new_type(const char *filename, TagLib_File_Type type)
default:
return 0;
}
return 0;
}
void taglib_file_free(TagLib_File *file)
@@ -132,7 +157,7 @@ BOOL taglib_file_save(TagLib_File *file)
char *taglib_tag_title(const TagLib_Tag *tag)
{
const Tag *t = reinterpret_cast<const Tag *>(tag);
char *s = ::strdup(t->title().toCString(unicodeStrings));
char *s = stringToCharArray(t->title());
if(stringManagementEnabled)
strings.append(s);
return s;
@@ -141,7 +166,7 @@ char *taglib_tag_title(const TagLib_Tag *tag)
char *taglib_tag_artist(const TagLib_Tag *tag)
{
const Tag *t = reinterpret_cast<const Tag *>(tag);
char *s = ::strdup(t->artist().toCString(unicodeStrings));
char *s = stringToCharArray(t->artist());
if(stringManagementEnabled)
strings.append(s);
return s;
@@ -150,7 +175,7 @@ char *taglib_tag_artist(const TagLib_Tag *tag)
char *taglib_tag_album(const TagLib_Tag *tag)
{
const Tag *t = reinterpret_cast<const Tag *>(tag);
char *s = ::strdup(t->album().toCString(unicodeStrings));
char *s = stringToCharArray(t->album());
if(stringManagementEnabled)
strings.append(s);
return s;
@@ -159,7 +184,7 @@ char *taglib_tag_album(const TagLib_Tag *tag)
char *taglib_tag_comment(const TagLib_Tag *tag)
{
const Tag *t = reinterpret_cast<const Tag *>(tag);
char *s = ::strdup(t->comment().toCString(unicodeStrings));
char *s = stringToCharArray(t->comment());
if(stringManagementEnabled)
strings.append(s);
return s;
@@ -168,7 +193,7 @@ char *taglib_tag_comment(const TagLib_Tag *tag)
char *taglib_tag_genre(const TagLib_Tag *tag)
{
const Tag *t = reinterpret_cast<const Tag *>(tag);
char *s = ::strdup(t->genre().toCString(unicodeStrings));
char *s = stringToCharArray(t->genre());
if(stringManagementEnabled)
strings.append(s);
return s;
@@ -189,31 +214,31 @@ unsigned int taglib_tag_track(const TagLib_Tag *tag)
void taglib_tag_set_title(TagLib_Tag *tag, const char *title)
{
Tag *t = reinterpret_cast<Tag *>(tag);
t->setTitle(String(title, unicodeStrings ? String::UTF8 : String::Latin1));
t->setTitle(charArrayToString(title));
}
void taglib_tag_set_artist(TagLib_Tag *tag, const char *artist)
{
Tag *t = reinterpret_cast<Tag *>(tag);
t->setArtist(String(artist, unicodeStrings ? String::UTF8 : String::Latin1));
t->setArtist(charArrayToString(artist));
}
void taglib_tag_set_album(TagLib_Tag *tag, const char *album)
{
Tag *t = reinterpret_cast<Tag *>(tag);
t->setAlbum(String(album, unicodeStrings ? String::UTF8 : String::Latin1));
t->setAlbum(charArrayToString(album));
}
void taglib_tag_set_comment(TagLib_Tag *tag, const char *comment)
{
Tag *t = reinterpret_cast<Tag *>(tag);
t->setComment(String(comment, unicodeStrings ? String::UTF8 : String::Latin1));
t->setComment(charArrayToString(comment));
}
void taglib_tag_set_genre(TagLib_Tag *tag, const char *genre)
{
Tag *t = reinterpret_cast<Tag *>(tag);
t->setGenre(String(genre, unicodeStrings ? String::UTF8 : String::Latin1));
t->setGenre(charArrayToString(genre));
}
void taglib_tag_set_year(TagLib_Tag *tag, unsigned int year)
@@ -233,7 +258,7 @@ void taglib_tag_free_strings()
if(!stringManagementEnabled)
return;
for(List<char *>::Iterator it = strings.begin(); it != strings.end(); ++it)
for(List<char *>::ConstIterator it = strings.begin(); it != strings.end(); ++it)
free(*it);
strings.clear();
}

View File

@@ -124,7 +124,7 @@ TAGLIB_C_EXPORT TagLib_File *taglib_file_new_type(const char *filename, TagLib_F
TAGLIB_C_EXPORT void taglib_file_free(TagLib_File *file);
/*!
* Returns true if the file is open and readble and valid information for
* Returns true if the file is open and readable and valid information for
* the Tag and / or AudioProperties was found.
*/
@@ -137,7 +137,7 @@ TAGLIB_C_EXPORT BOOL taglib_file_is_valid(const TagLib_File *file);
TAGLIB_C_EXPORT TagLib_Tag *taglib_file_tag(const TagLib_File *file);
/*!
* Returns a pointer to the the audio properties associated with this file. This
* Returns a pointer to the audio properties associated with this file. This
* will be freed automatically when the file is freed.
*/
TAGLIB_C_EXPORT const TagLib_AudioProperties *taglib_file_audioproperties(const TagLib_File *file);

View File

@@ -7,6 +7,6 @@ includedir=${INCLUDE_INSTALL_DIR}
Name: TagLib C Bindings
Description: Audio meta-data library (C bindings)
Requires: taglib
Version: ${TAGLIB_LIB_MAJOR_VERSION}.${TAGLIB_LIB_MINOR_VERSION}.${TAGLIB_LIB_PATCH_VERSION}
Version: ${TAGLIB_LIB_VERSION_STRING}
Libs: -L${LIB_INSTALL_DIR} -ltag_c
Cflags: -I${INCLUDE_INSTALL_DIR}/taglib

View File

@@ -1,21 +1,13 @@
/* config.h. Generated by cmake from config.h.cmake */
/* Indicates the byte order of your target system */
/* 1 if little-endian, 2 if big-endian. */
#cmakedefine SYSTEM_BYTEORDER ${SYSTEM_BYTEORDER}
/* Defined if your compiler supports some byte swap functions */
#cmakedefine HAVE_GCC_BYTESWAP_16 1
#cmakedefine HAVE_GCC_BYTESWAP_32 1
#cmakedefine HAVE_GCC_BYTESWAP_64 1
#cmakedefine HAVE_BOOST_BYTESWAP 1
#cmakedefine HAVE_GCC_BYTESWAP 1
#cmakedefine HAVE_GLIBC_BYTESWAP 1
#cmakedefine HAVE_MSC_BYTESWAP 1
#cmakedefine HAVE_MAC_BYTESWAP 1
#cmakedefine HAVE_OPENBSD_BYTESWAP 1
/* Defined if your compiler supports codecvt */
#cmakedefine HAVE_STD_CODECVT 1
/* Defined if your compiler supports some atomic operations */
#cmakedefine HAVE_STD_ATOMIC 1
#cmakedefine HAVE_BOOST_ATOMIC 1
@@ -24,12 +16,16 @@
#cmakedefine HAVE_WIN_ATOMIC 1
#cmakedefine HAVE_IA64_ATOMIC 1
/* Defined if your compiler supports some safer version of sprintf */
#cmakedefine HAVE_SNPRINTF 1
#cmakedefine HAVE_SPRINTF_S 1
/* Defined if your compiler supports some safer version of vsprintf */
#cmakedefine HAVE_VSNPRINTF 1
#cmakedefine HAVE_VSPRINTF_S 1
/* Defined if you have libz */
/* Defined if your compiler supports ISO _strdup */
#cmakedefine HAVE_ISO_STRDUP 1
/* Defined if zlib is installed */
#cmakedefine HAVE_ZLIB 1
#cmakedefine HAVE_BOOST_ZLIB 1
/* Indicates whether debug messages are shown even in release mode */
#cmakedefine TRACE_IN_RELEASE 1

View File

@@ -1,50 +1,39 @@
if(BUILD_EXAMPLES)
INCLUDE_DIRECTORIES( ${CMAKE_CURRENT_SOURCE_DIR}/../taglib
${CMAKE_CURRENT_SOURCE_DIR}/../taglib/toolkit
${CMAKE_CURRENT_SOURCE_DIR}/../taglib/ape
${CMAKE_CURRENT_SOURCE_DIR}/../taglib/mpeg
${CMAKE_CURRENT_SOURCE_DIR}/../taglib/mpeg/id3v1
${CMAKE_CURRENT_SOURCE_DIR}/../taglib/mpeg/id3v2
${CMAKE_CURRENT_SOURCE_DIR}/../bindings/c/ )
include_directories(
${CMAKE_CURRENT_SOURCE_DIR}/../taglib
${CMAKE_CURRENT_SOURCE_DIR}/../taglib/toolkit
${CMAKE_CURRENT_SOURCE_DIR}/../taglib/ape
${CMAKE_CURRENT_SOURCE_DIR}/../taglib/mpeg
${CMAKE_CURRENT_SOURCE_DIR}/../taglib/mpeg/id3v1
${CMAKE_CURRENT_SOURCE_DIR}/../taglib/mpeg/id3v2
${CMAKE_CURRENT_SOURCE_DIR}/../bindings/c/
)
if(ENABLE_STATIC)
add_definitions(-DTAGLIB_STATIC)
endif(ENABLE_STATIC)
if(NOT BUILD_SHARED_LIBS)
add_definitions(-DTAGLIB_STATIC)
endif()
########### next target ###############
ADD_EXECUTABLE(tagreader tagreader.cpp)
TARGET_LINK_LIBRARIES(tagreader tag )
add_executable(tagreader tagreader.cpp)
target_link_libraries(tagreader tag)
########### next target ###############
ADD_EXECUTABLE(tagreader_c tagreader_c.c)
TARGET_LINK_LIBRARIES(tagreader_c tag_c )
add_executable(tagreader_c tagreader_c.c)
target_link_libraries(tagreader_c tag_c)
########### next target ###############
ADD_EXECUTABLE(tagwriter tagwriter.cpp)
TARGET_LINK_LIBRARIES(tagwriter tag )
add_executable(tagwriter tagwriter.cpp)
target_link_libraries(tagwriter tag)
########### next target ###############
ADD_EXECUTABLE(framelist framelist.cpp)
TARGET_LINK_LIBRARIES(framelist tag )
add_executable(framelist framelist.cpp)
target_link_libraries(framelist tag)
########### next target ###############
ADD_EXECUTABLE(strip-id3v1 strip-id3v1.cpp)
TARGET_LINK_LIBRARIES(strip-id3v1 tag )
endif(BUILD_EXAMPLES)
add_executable(strip-id3v1 strip-id3v1.cpp)
target_link_libraries(strip-id3v1 tag)

View File

@@ -32,13 +32,6 @@
using namespace std;
TagLib::String formatSeconds(int seconds)
{
char secondsString[3];
sprintf(secondsString, "%02i", seconds);
return secondsString;
}
int main(int argc, char *argv[])
{
for(int i = 1; i < argc; i++) {
@@ -89,7 +82,7 @@ int main(int argc, char *argv[])
cout << "bitrate - " << properties->bitrate() << endl;
cout << "sample rate - " << properties->sampleRate() << endl;
cout << "channels - " << properties->channels() << endl;
cout << "length - " << minutes << ":" << formatSeconds(seconds) << endl;
cout << "length - " << minutes << ":" << setfill('0') << setw(2) << seconds << endl;
}
}
return 0;

View File

@@ -23,6 +23,7 @@
*/
#include <iostream>
#include <iomanip>
#include <string.h>
#include <stdio.h>
@@ -34,6 +35,7 @@
#include <fileref.h>
#include <tfile.h>
#include <tag.h>
#include <tpropertymap.h>
using namespace std;
@@ -65,11 +67,32 @@ void usage()
cout << " -g <genre>" << endl;
cout << " -y <year>" << endl;
cout << " -T <track>" << endl;
cout << " -R <tagname> <tagvalue>" << endl;
cout << " -I <tagname> <tagvalue>" << endl;
cout << " -D <tagname>" << endl;
cout << endl;
exit(1);
}
void checkForRejectedProperties(const TagLib::PropertyMap &tags)
{ // stolen from tagreader.cpp
if(tags.size() > 0) {
unsigned int longest = 0;
for(TagLib::PropertyMap::ConstIterator i = tags.begin(); i != tags.end(); ++i) {
if(i->first.size() > longest) {
longest = i->first.size();
}
}
cout << "-- rejected TAGs (properties) --" << endl;
for(TagLib::PropertyMap::ConstIterator i = tags.begin(); i != tags.end(); ++i) {
for(TagLib::StringList::ConstIterator j = i->second.begin(); j != i->second.end(); ++j) {
cout << left << std::setw(longest) << i->first << " - " << '"' << *j << '"' << endl;
}
}
}
}
int main(int argc, char *argv[])
{
TagLib::List<TagLib::FileRef> fileList;
@@ -94,7 +117,7 @@ int main(int argc, char *argv[])
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();
@@ -121,6 +144,29 @@ int main(int argc, char *argv[])
case 'T':
t->setTrack(value.toInt());
break;
case 'R':
case 'I':
if(i + 2 < argc) {
TagLib::PropertyMap map = (*it).file()->properties ();
if(field == 'R') {
map.replace(value, TagLib::String(argv[i + 2]));
}
else {
map.insert(value, TagLib::String(argv[i + 2]));
}
++i;
checkForRejectedProperties((*it).file()->setProperties(map));
}
else {
usage();
}
break;
case 'D': {
TagLib::PropertyMap map = (*it).file()->properties();
map.erase(value);
checkForRejectedProperties((*it).file()->setProperties(map));
break;
}
default:
usage();
break;
@@ -131,7 +177,7 @@ int main(int argc, char *argv[])
usage();
}
TagLib::List<TagLib::FileRef>::Iterator it;
TagLib::List<TagLib::FileRef>::ConstIterator it;
for(it = fileList.begin(); it != fileList.end(); ++it)
(*it).file()->save();

View File

@@ -35,7 +35,7 @@ do
flags="$flags -I$includedir/taglib"
;;
--version)
echo ${TAGLIB_LIB_MAJOR_VERSION}.${TAGLIB_LIB_MINOR_VERSION}.${TAGLIB_LIB_PATCH_VERSION}
echo ${TAGLIB_LIB_VERSION_STRING}
;;
--prefix)
echo $prefix

View File

@@ -29,7 +29,7 @@ goto theend
: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_MAJOR_VERSION}.${TAGLIB_LIB_MINOR_VERSION}.${TAGLIB_LIB_PATCH_VERSION}
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}
prefix=@CMAKE_INSTALL_PREFIX@
exec_prefix=@CMAKE_INSTALL_PREFIX@
libdir=@LIB_INSTALL_DIR@
includedir=@INCLUDE_INSTALL_DIR@
Name: TagLib
Description: Audio meta-data library
Requires:
Version: ${TAGLIB_LIB_MAJOR_VERSION}.${TAGLIB_LIB_MINOR_VERSION}.${TAGLIB_LIB_PATCH_VERSION}
Libs: -L${LIB_INSTALL_DIR} -ltag
Cflags: -I${INCLUDE_INSTALL_DIR}/taglib
Version: @TAGLIB_LIB_VERSION_STRING@
Libs: -L${libdir} -ltag
Cflags: -I${includedir}/taglib

View File

@@ -28,6 +28,12 @@ include_directories(
if(ZLIB_FOUND)
include_directories(${ZLIB_INCLUDE_DIR})
elseif(HAVE_ZLIB_SOURCE)
include_directories(${ZLIB_SOURCE})
endif()
if(HAVE_BOOST_BYTESWAP OR HAVE_BOOST_ATOMIC OR HAVE_BOOST_ZLIB)
include_directories(${Boost_INCLUDE_DIR})
endif()
set(tag_HDRS
@@ -35,7 +41,7 @@ set(tag_HDRS
fileref.h
audioproperties.h
taglib_export.h
${CMAKE_BINARY_DIR}/taglib_config.h
${CMAKE_CURRENT_BINARY_DIR}/../taglib_config.h
toolkit/taglib.h
toolkit/tstring.h
toolkit/tlist.h
@@ -67,16 +73,21 @@ set(tag_HDRS
mpeg/id3v2/id3v2tag.h
mpeg/id3v2/frames/attachedpictureframe.h
mpeg/id3v2/frames/commentsframe.h
mpeg/id3v2/frames/eventtimingcodesframe.h
mpeg/id3v2/frames/generalencapsulatedobjectframe.h
mpeg/id3v2/frames/ownershipframe.h
mpeg/id3v2/frames/popularimeterframe.h
mpeg/id3v2/frames/privateframe.h
mpeg/id3v2/frames/relativevolumeframe.h
mpeg/id3v2/frames/synchronizedlyricsframe.h
mpeg/id3v2/frames/textidentificationframe.h
mpeg/id3v2/frames/uniquefileidentifierframe.h
mpeg/id3v2/frames/unknownframe.h
mpeg/id3v2/frames/unsynchronizedlyricsframe.h
mpeg/id3v2/frames/urllinkframe.h
mpeg/id3v2/frames/chapterframe.h
mpeg/id3v2/frames/tableofcontentsframe.h
mpeg/id3v2/frames/podcastframe.h
ogg/oggfile.h
ogg/oggpage.h
ogg/oggpageheader.h
@@ -157,16 +168,21 @@ set(id3v2_SRCS
set(frames_SRCS
mpeg/id3v2/frames/attachedpictureframe.cpp
mpeg/id3v2/frames/commentsframe.cpp
mpeg/id3v2/frames/eventtimingcodesframe.cpp
mpeg/id3v2/frames/generalencapsulatedobjectframe.cpp
mpeg/id3v2/frames/ownershipframe.cpp
mpeg/id3v2/frames/popularimeterframe.cpp
mpeg/id3v2/frames/privateframe.cpp
mpeg/id3v2/frames/relativevolumeframe.cpp
mpeg/id3v2/frames/synchronizedlyricsframe.cpp
mpeg/id3v2/frames/textidentificationframe.cpp
mpeg/id3v2/frames/uniquefileidentifierframe.cpp
mpeg/id3v2/frames/unknownframe.cpp
mpeg/id3v2/frames/unsynchronizedlyricsframe.cpp
mpeg/id3v2/frames/urllinkframe.cpp
mpeg/id3v2/frames/chapterframe.cpp
mpeg/id3v2/frames/tableofcontentsframe.cpp
mpeg/id3v2/frames/podcastframe.cpp
)
set(ogg_SRCS
@@ -293,24 +309,51 @@ set(toolkit_SRCS
toolkit/tpropertymap.cpp
toolkit/trefcounter.cpp
toolkit/tdebuglistener.cpp
toolkit/unicode.cpp
toolkit/tzlib.cpp
)
if(NOT WIN32)
set(unicode_SRCS
toolkit/unicode.cpp
)
endif()
if(HAVE_ZLIB_SOURCE)
set(zlib_SRCS
${ZLIB_SOURCE}/adler32.c
${ZLIB_SOURCE}/crc32.c
${ZLIB_SOURCE}/inffast.c
${ZLIB_SOURCE}/inflate.c
${ZLIB_SOURCE}/inftrees.c
${ZLIB_SOURCE}/zutil.c
)
endif()
set(tag_LIB_SRCS
${mpeg_SRCS} ${id3v1_SRCS} ${id3v2_SRCS} ${frames_SRCS} ${ogg_SRCS}
${vorbis_SRCS} ${oggflacs_SRCS} ${mpc_SRCS} ${ape_SRCS} ${toolkit_SRCS} ${flacs_SRCS}
${wavpack_SRCS} ${speex_SRCS} ${trueaudio_SRCS} ${riff_SRCS} ${aiff_SRCS} ${wav_SRCS}
${asf_SRCS} ${mp4_SRCS} ${mod_SRCS} ${s3m_SRCS} ${it_SRCS} ${xm_SRCS} ${opus_SRCS}
${unicode_SRCS} ${zlib_SRCS}
tag.cpp
tagunion.cpp
fileref.cpp
audioproperties.cpp
tagutils.cpp
)
add_library(tag ${tag_LIB_SRCS} ${tag_HDRS})
if(ZLIB_FOUND)
target_link_libraries(tag ${ZLIB_LIBRARIES})
target_link_libraries(tag ${ZLIB_LIBRARIES})
endif()
if(HAVE_BOOST_ATOMIC)
target_link_libraries(tag ${Boost_ATOMIC_LIBRARY})
endif()
if(HAVE_BOOST_ZLIB)
target_link_libraries(tag ${Boost_IOSTREAMS_LIBRARY} ${Boost_ZLIB_LIBRARY})
endif()
set_target_properties(tag PROPERTIES
@@ -326,10 +369,9 @@ if(BUILD_FRAMEWORK)
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
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
)

View File

@@ -36,10 +36,11 @@
#include <tdebug.h>
#include <tagunion.h>
#include <id3v1tag.h>
#include <id3v2header.h>
#include <tpropertymap.h>
#include <tagutils.h>
#include "apefile.h"
#include "apetag.h"
#include "apefooter.h"
@@ -57,49 +58,49 @@ public:
APELocation(-1),
APESize(0),
ID3v1Location(-1),
properties(0),
hasAPE(false),
hasID3v1(false) {}
ID3v2Header(0),
ID3v2Location(-1),
ID3v2Size(0),
properties(0) {}
~FilePrivate()
{
delete ID3v2Header;
delete properties;
}
long APELocation;
uint APESize;
long APESize;
long ID3v1Location;
ID3v2::Header *ID3v2Header;
long ID3v2Location;
long ID3v2Size;
TagUnion tag;
Properties *properties;
// These indicate whether the file *on disk* has these tags, not if
// this data structure does. This is used in computing offsets.
bool hasAPE;
bool hasID3v1;
};
////////////////////////////////////////////////////////////////////////////////
// public members
////////////////////////////////////////////////////////////////////////////////
APE::File::File(FileName file, bool readProperties,
Properties::ReadStyle propertiesStyle) : TagLib::File(file)
APE::File::File(FileName file, bool readProperties, Properties::ReadStyle) :
TagLib::File(file),
d(new FilePrivate())
{
d = new FilePrivate;
if(isOpen())
read(readProperties, propertiesStyle);
read(readProperties);
}
APE::File::File(IOStream *stream, bool readProperties,
Properties::ReadStyle propertiesStyle) : TagLib::File(stream)
APE::File::File(IOStream *stream, bool readProperties, Properties::ReadStyle) :
TagLib::File(stream),
d(new FilePrivate())
{
d = new FilePrivate;
if(isOpen())
read(readProperties, propertiesStyle);
read(readProperties);
}
APE::File::~File()
@@ -114,26 +115,20 @@ TagLib::Tag *APE::File::tag() const
PropertyMap APE::File::properties() const
{
if(d->hasAPE)
return d->tag.access<APE::Tag>(ApeAPEIndex, false)->properties();
if(d->hasID3v1)
return d->tag.access<ID3v1::Tag>(ApeID3v1Index, false)->properties();
return PropertyMap();
return d->tag.properties();
}
void APE::File::removeUnsupportedProperties(const StringList &properties)
{
if(d->hasAPE)
d->tag.access<APE::Tag>(ApeAPEIndex, false)->removeUnsupportedProperties(properties);
if(d->hasID3v1)
d->tag.access<ID3v1::Tag>(ApeID3v1Index, false)->removeUnsupportedProperties(properties);
d->tag.removeUnsupportedProperties(properties);
}
PropertyMap APE::File::setProperties(const PropertyMap &properties)
{
if(d->hasID3v1)
d->tag.access<ID3v1::Tag>(ApeID3v1Index, false)->setProperties(properties);
return d->tag.access<APE::Tag>(ApeAPEIndex, true)->setProperties(properties);
if(ID3v1Tag())
ID3v1Tag()->setProperties(properties);
return APETag(true)->setProperties(properties);
}
APE::Properties *APE::File::audioProperties() const
@@ -150,64 +145,67 @@ bool APE::File::save()
// Update ID3v1 tag
if(ID3v1Tag()) {
if(d->hasID3v1) {
if(ID3v1Tag() && !ID3v1Tag()->isEmpty()) {
// ID3v1 tag is not empty. Update the old one or create a new one.
if(d->ID3v1Location >= 0) {
seek(d->ID3v1Location);
writeBlock(ID3v1Tag()->render());
}
else {
seek(0, End);
d->ID3v1Location = tell();
writeBlock(ID3v1Tag()->render());
d->hasID3v1 = true;
}
writeBlock(ID3v1Tag()->render());
}
else {
if(d->hasID3v1) {
removeBlock(d->ID3v1Location, 128);
d->hasID3v1 = false;
if(d->hasAPE) {
if(d->APELocation > d->ID3v1Location)
d->APELocation -= 128;
}
// ID3v1 tag is empty. Remove the old one.
if(d->ID3v1Location >= 0) {
truncate(d->ID3v1Location);
d->ID3v1Location = -1;
}
}
// Update APE tag
if(APETag()) {
if(d->hasAPE)
insert(APETag()->render(), d->APELocation, d->APESize);
else {
if(d->hasID3v1) {
insert(APETag()->render(), d->ID3v1Location, 0);
d->APESize = APETag()->footer()->completeTagSize();
d->hasAPE = true;
if(APETag() && !APETag()->isEmpty()) {
// APE tag is not empty. Update the old one or create a new one.
if(d->APELocation < 0) {
if(d->ID3v1Location >= 0)
d->APELocation = d->ID3v1Location;
d->ID3v1Location += d->APESize;
}
else {
seek(0, End);
d->APELocation = tell();
writeBlock(APETag()->render());
d->APESize = APETag()->footer()->completeTagSize();
d->hasAPE = true;
}
else
d->APELocation = length();
}
const ByteVector data = APETag()->render();
insert(data, d->APELocation, d->APESize);
if(d->ID3v1Location >= 0)
d->ID3v1Location += (static_cast<long>(data.size()) - d->APESize);
d->APESize = data.size();
}
else {
if(d->hasAPE) {
// APE tag is empty. Remove the old one.
if(d->APELocation >= 0) {
removeBlock(d->APELocation, d->APESize);
d->hasAPE = false;
if(d->hasID3v1) {
if(d->ID3v1Location > d->APELocation) {
d->ID3v1Location -= d->APESize;
}
}
if(d->ID3v1Location >= 0)
d->ID3v1Location -= d->APESize;
d->APELocation = -1;
d->APESize = 0;
}
}
return true;
return true;
}
ID3v1::Tag *APE::File::ID3v1Tag(bool create)
@@ -222,93 +220,83 @@ APE::Tag *APE::File::APETag(bool create)
void APE::File::strip(int tags)
{
if(tags & ID3v1) {
if(tags & ID3v1)
d->tag.set(ApeID3v1Index, 0);
APETag(true);
}
if(tags & APE) {
if(tags & APE)
d->tag.set(ApeAPEIndex, 0);
if(!ID3v1Tag())
APETag(true);
}
if(!ID3v1Tag())
APETag(true);
}
bool APE::File::hasAPETag() const
{
return d->hasAPE;
return (d->APELocation >= 0);
}
bool APE::File::hasID3v1Tag() const
{
return d->hasID3v1;
return (d->ID3v1Location >= 0);
}
////////////////////////////////////////////////////////////////////////////////
// private members
////////////////////////////////////////////////////////////////////////////////
void APE::File::read(bool readProperties, Properties::ReadStyle /* propertiesStyle */)
void APE::File::read(bool readProperties)
{
// Look for an ID3v2 tag
d->ID3v2Location = Utils::findID3v2(this);
if(d->ID3v2Location >= 0) {
seek(d->ID3v2Location);
d->ID3v2Header = new ID3v2::Header(readBlock(ID3v2::Header::size()));
d->ID3v2Size = d->ID3v2Header->completeTagSize();
}
// Look for an ID3v1 tag
d->ID3v1Location = findID3v1();
d->ID3v1Location = Utils::findID3v1(this);
if(d->ID3v1Location >= 0) {
if(d->ID3v1Location >= 0)
d->tag.set(ApeID3v1Index, new ID3v1::Tag(this, d->ID3v1Location));
d->hasID3v1 = true;
}
// Look for an APE tag
d->APELocation = findAPE();
d->APELocation = Utils::findAPE(this, d->ID3v1Location);
if(d->APELocation >= 0) {
d->tag.set(ApeAPEIndex, new APE::Tag(this, d->APELocation));
d->APESize = APETag()->footer()->completeTagSize();
d->APELocation = d->APELocation + APETag()->footer()->size() - d->APESize;
d->hasAPE = true;
d->APELocation = d->APELocation + APE::Footer::size() - d->APESize;
}
if(!d->hasID3v1)
if(d->ID3v1Location < 0)
APETag(true);
// Look for APE audio properties
if(readProperties) {
d->properties = new Properties(this);
long streamLength;
if(d->APELocation >= 0)
streamLength = d->APELocation;
else if(d->ID3v1Location >= 0)
streamLength = d->ID3v1Location;
else
streamLength = length();
if(d->ID3v2Location >= 0) {
seek(d->ID3v2Location + d->ID3v2Size);
streamLength -= (d->ID3v2Location + d->ID3v2Size);
}
else {
seek(0);
}
d->properties = new Properties(this, streamLength);
}
}
long APE::File::findAPE()
{
if(!isValid())
return -1;
if(d->hasID3v1)
seek(-160, End);
else
seek(-32, End);
long p = tell();
if(readBlock(8) == APE::Tag::fileIdentifier())
return p;
return -1;
}
long APE::File::findID3v1()
{
if(!isValid())
return -1;
seek(-128, End);
long p = tell();
if(readBlock(3) == ID3v1::Tag::fileIdentifier())
return p;
return -1;
}

View File

@@ -130,7 +130,7 @@ namespace TagLib {
/*!
* Implements the unified property interface -- import function.
* Creates an APEv2 tag if necessary. A pontentially existing ID3v1
* Creates an APEv2 tag if necessary. A potentially existing ID3v1
* tag will be updated as well.
*/
PropertyMap setProperties(const PropertyMap &);
@@ -156,8 +156,8 @@ namespace TagLib {
* if there is no valid ID3v1 tag. If \a create is true it will create
* an ID3v1 tag if one does not exist and returns a valid pointer.
*
* \note This may return a valid pointer regardless of whether or not the
* file on disk has an ID3v1 tag. Use hasID3v1Tag() to check if the file
* \note This may return a valid pointer regardless of whether or not the
* file on disk has an ID3v1 tag. Use hasID3v1Tag() to check if the file
* on disk actually has an ID3v1 tag.
*
* \note The Tag <b>is still</b> owned by the MPEG::File and should not be
@@ -175,8 +175,8 @@ namespace TagLib {
* if there is no valid APE tag. If \a create is true it will create
* an APE tag if one does not exist and returns a valid pointer.
*
* \note This may return a valid pointer regardless of whether or not the
* file on disk has an APE tag. Use hasAPETag() to check if the file
* \note This may return a valid pointer regardless of whether or not the
* file on disk has an APE tag. Use hasAPETag() to check if the file
* on disk actually has an APE tag.
*
* \note The Tag <b>is still</b> owned by the MPEG::File and should not be
@@ -215,10 +215,7 @@ namespace TagLib {
File(const File &);
File &operator=(const File &);
void read(bool readProperties, Properties::ReadStyle propertiesStyle);
void scan();
long findID3v1();
long findAPE();
void read(bool readProperties);
class FilePrivate;
FilePrivate *d;

View File

@@ -38,54 +38,51 @@ using namespace APE;
class APE::Footer::FooterPrivate
{
public:
FooterPrivate() : version(0),
footerPresent(true),
headerPresent(false),
isHeader(false),
itemCount(0),
tagSize(0) {}
FooterPrivate() :
version(0),
footerPresent(true),
headerPresent(false),
isHeader(false),
itemCount(0),
tagSize(0) {}
~FooterPrivate() {}
uint version;
unsigned int version;
bool footerPresent;
bool headerPresent;
bool isHeader;
uint itemCount;
uint tagSize;
static const uint size = 32;
unsigned int itemCount;
unsigned int tagSize;
};
////////////////////////////////////////////////////////////////////////////////
// static members
////////////////////////////////////////////////////////////////////////////////
TagLib::uint APE::Footer::size()
unsigned int APE::Footer::size()
{
return FooterPrivate::size;
return 32;
}
ByteVector APE::Footer::fileIdentifier()
{
return ByteVector::fromCString("APETAGEX");
return ByteVector("APETAGEX");
}
////////////////////////////////////////////////////////////////////////////////
// public members
////////////////////////////////////////////////////////////////////////////////
APE::Footer::Footer()
APE::Footer::Footer() :
d(new FooterPrivate())
{
d = new FooterPrivate;
}
APE::Footer::Footer(const ByteVector &data)
APE::Footer::Footer(const ByteVector &data) :
d(new FooterPrivate())
{
d = new FooterPrivate;
parse(data);
}
@@ -94,7 +91,7 @@ APE::Footer::~Footer()
delete d;
}
TagLib::uint APE::Footer::version() const
unsigned int APE::Footer::version() const
{
return d->version;
}
@@ -119,30 +116,30 @@ void APE::Footer::setHeaderPresent(bool b) const
d->headerPresent = b;
}
TagLib::uint APE::Footer::itemCount() const
unsigned int APE::Footer::itemCount() const
{
return d->itemCount;
}
void APE::Footer::setItemCount(uint s)
void APE::Footer::setItemCount(unsigned int s)
{
d->itemCount = s;
}
TagLib::uint APE::Footer::tagSize() const
unsigned int APE::Footer::tagSize() const
{
return d->tagSize;
}
TagLib::uint APE::Footer::completeTagSize() const
unsigned int APE::Footer::completeTagSize() const
{
if(d->headerPresent)
return d->tagSize + d->size;
return d->tagSize + size();
else
return d->tagSize;
}
void APE::Footer::setTagSize(uint s)
void APE::Footer::setTagSize(unsigned int s)
{
d->tagSize = s;
}
@@ -154,13 +151,14 @@ void APE::Footer::setData(const ByteVector &data)
ByteVector APE::Footer::renderFooter() const
{
return render(false);
return render(false);
}
ByteVector APE::Footer::renderHeader() const
{
if (!d->headerPresent) return ByteVector();
if(!d->headerPresent)
return ByteVector();
else
return render(true);
}

View File

@@ -37,7 +37,7 @@ namespace TagLib {
/*!
* This class implements APE footers (and headers). It attempts to follow, both
* semantically and programatically, the structure specified in
* semantically and programmatically, the structure specified in
* the APE v2.0 standard. The API is based on the properties of APE footer and
* headers specified there.
*/
@@ -64,7 +64,7 @@ namespace TagLib {
/*!
* Returns the version number. (Note: This is the 1000 or 2000.)
*/
uint version() const;
unsigned int version() const;
/*!
* Returns true if a header is present in the tag.
@@ -89,13 +89,13 @@ namespace TagLib {
/*!
* Returns the number of items in the tag.
*/
uint itemCount() const;
unsigned int itemCount() const;
/*!
* Set the item count to \a s.
* \see itemCount()
*/
void setItemCount(uint s);
void setItemCount(unsigned int s);
/*!
* Returns the tag size in bytes. This is the size of the frame content and footer.
@@ -103,7 +103,7 @@ namespace TagLib {
*
* \see completeTagSize()
*/
uint tagSize() const;
unsigned int tagSize() const;
/*!
* Returns the tag size, including if present, the header
@@ -111,18 +111,18 @@ namespace TagLib {
*
* \see tagSize()
*/
uint completeTagSize() const;
unsigned int completeTagSize() const;
/*!
* Set the tag size to \a s.
* \see tagSize()
*/
void setTagSize(uint s);
void setTagSize(unsigned int s);
/*!
* Returns the size of the footer. Presently this is always 32 bytes.
*/
static uint size();
static unsigned int size();
/*!
* Returns the string used to identify an APE tag inside of a file.

View File

@@ -34,7 +34,9 @@ using namespace APE;
class APE::Item::ItemPrivate
{
public:
ItemPrivate() : type(Text), readOnly(false) {}
ItemPrivate() :
type(Text),
readOnly(false) {}
Item::ItemTypes type;
String key;
@@ -43,40 +45,45 @@ public:
bool readOnly;
};
APE::Item::Item()
////////////////////////////////////////////////////////////////////////////////
// public members
////////////////////////////////////////////////////////////////////////////////
APE::Item::Item() :
d(new ItemPrivate())
{
d = new ItemPrivate;
}
APE::Item::Item(const String &key, const String &value)
APE::Item::Item(const String &key, const String &value) :
d(new ItemPrivate())
{
d = new ItemPrivate;
d->key = key;
d->text.append(value);
}
APE::Item::Item(const String &key, const StringList &values)
APE::Item::Item(const String &key, const StringList &values) :
d(new ItemPrivate())
{
d = new ItemPrivate;
d->key = key;
d->text = values;
}
APE::Item::Item(const String &key, const ByteVector &value, bool binary)
APE::Item::Item(const String &key, const ByteVector &value, bool binary) :
d(new ItemPrivate())
{
d = new ItemPrivate;
d->key = key;
if(binary) {
d->type = Binary;
d->value = value;
}
else
else {
d->text.append(value);
}
}
APE::Item::Item(const Item &item)
APE::Item::Item(const Item &item) :
d(new ItemPrivate(*item.d))
{
d = new ItemPrivate(*item.d);
}
APE::Item::~Item()
@@ -86,11 +93,17 @@ APE::Item::~Item()
Item &APE::Item::operator=(const Item &item)
{
delete d;
d = new ItemPrivate(*item.d);
Item(item).swap(*this);
return *this;
}
void APE::Item::swap(Item &item)
{
using std::swap;
swap(d, item.d);
}
void APE::Item::setReadOnly(bool readOnly)
{
d->readOnly = readOnly;
@@ -171,11 +184,10 @@ void APE::Item::appendValues(const StringList &values)
int APE::Item::size() const
{
// SFB: Why is d->key.size() used when size() returns the length in UniChars and not UTF-8?
int result = 8 + d->key.size() /* d->key.data(String::UTF8).size() */ + 1;
switch (d->type) {
int result = 8 + d->key.size() + 1;
switch(d->type) {
case Text:
if(d->text.size()) {
if(!d->text.isEmpty()) {
StringList::ConstIterator it = d->text.begin();
result += it->data(String::UTF8).size();
@@ -208,7 +220,7 @@ String APE::Item::toString() const
if(d->type == Text && !isEmpty())
return d->text.front();
else
return String::null;
return String();
}
bool APE::Item::isEmpty() const
@@ -237,17 +249,20 @@ void APE::Item::parse(const ByteVector &data)
return;
}
const uint valueLength = data.toUInt(0, false);
const uint flags = data.toUInt(4, false);
const unsigned int valueLength = data.toUInt(0, false);
const unsigned int flags = data.toUInt(4, false);
d->key = String(data.mid(8), String::UTF8);
// 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->key = String(&data[8], String::Latin1);
const ByteVector value = data.mid(8 + d->key.size() + 1, valueLength);
setReadOnly(flags & 1);
setType(ItemTypes((flags >> 1) & 3));
if(Text == d->type)
if(Text == d->type)
d->text = StringList(ByteVectorList::split(value, '\0'), String::UTF8);
else
d->value = value;
@@ -256,7 +271,7 @@ void APE::Item::parse(const ByteVector &data)
ByteVector APE::Item::render() const
{
ByteVector data;
TagLib::uint flags = ((d->readOnly) ? 1 : 0) | (d->type << 1);
unsigned int flags = ((d->readOnly) ? 1 : 0) | (d->type << 1);
ByteVector value;
if(isEmpty())
@@ -278,7 +293,7 @@ ByteVector APE::Item::render() const
data.append(ByteVector::fromUInt(value.size(), false));
data.append(ByteVector::fromUInt(flags, false));
data.append(d->key.data(String::UTF8));
data.append(d->key.data(String::Latin1));
data.append(ByteVector('\0'));
data.append(value);

View File

@@ -90,6 +90,11 @@ namespace TagLib {
*/
Item &operator=(const Item &item);
/*!
* Exchanges the content of this item by the content of \a item.
*/
void swap(Item &item);
/*!
* Returns the key.
*/
@@ -153,7 +158,7 @@ namespace TagLib {
/*!
* Returns the value as a single string. In case of multiple strings,
* the first is returned. If the data type is not \a Text, always returns
* the first is returned. If the data type is not \a Text, always returns
* an empty String.
*/
String toString() const;
@@ -164,7 +169,7 @@ namespace TagLib {
#endif
/*!
* Returns the list of text values. If the data type is not \a Text, always
* Returns the list of text values. If the data type is not \a Text, always
* returns an empty StringList.
*/
StringList values() const;

View File

@@ -33,22 +33,22 @@
#include "id3v2tag.h"
#include "apeproperties.h"
#include "apefile.h"
#include "apetag.h"
#include "apefooter.h"
using namespace TagLib;
class APE::Properties::PropertiesPrivate
{
public:
PropertiesPrivate(File *file, long streamLength) :
PropertiesPrivate() :
length(0),
bitrate(0),
sampleRate(0),
channels(0),
version(0),
bitsPerSample(0),
sampleFrames(0),
file(file),
streamLength(streamLength) {}
sampleFrames(0) {}
int length;
int bitrate;
@@ -56,19 +56,25 @@ public:
int channels;
int version;
int bitsPerSample;
uint sampleFrames;
File *file;
long streamLength;
unsigned int sampleFrames;
};
////////////////////////////////////////////////////////////////////////////////
// public members
////////////////////////////////////////////////////////////////////////////////
APE::Properties::Properties(File *file, ReadStyle style) : AudioProperties(style)
APE::Properties::Properties(File *, ReadStyle style) :
AudioProperties(style),
d(new PropertiesPrivate())
{
d = new PropertiesPrivate(file, file->length());
read();
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::Properties::~Properties()
@@ -77,6 +83,16 @@ APE::Properties::~Properties()
}
int APE::Properties::length() const
{
return lengthInSeconds();
}
int APE::Properties::lengthInSeconds() const
{
return d->length / 1000;
}
int APE::Properties::lengthInMilliseconds() const
{
return d->length;
}
@@ -106,7 +122,7 @@ int APE::Properties::bitsPerSample() const
return d->bitsPerSample;
}
TagLib::uint APE::Properties::sampleFrames() const
unsigned int APE::Properties::sampleFrames() const
{
return d->sampleFrames;
}
@@ -115,118 +131,122 @@ TagLib::uint APE::Properties::sampleFrames() const
// private members
////////////////////////////////////////////////////////////////////////////////
void APE::Properties::read()
namespace
{
// First we are searching the descriptor
long offset = findDescriptor();
if(offset < 0)
return;
int headerVersion(const ByteVector &header)
{
if(header.size() < 6 || !header.startsWith("MAC "))
return -1;
// Then we read the header common for all versions of APE
d->file->seek(offset);
ByteVector commonHeader = d->file->readBlock(6);
if(!commonHeader.startsWith("MAC "))
return;
d->version = commonHeader.toUShort(4, false);
if(d->version >= 3980) {
analyzeCurrent();
}
else {
analyzeOld();
return header.toUShort(4, false);
}
}
long APE::Properties::findDescriptor()
void APE::Properties::read(File *file, long streamLength)
{
long ID3v2Location = findID3v2();
long ID3v2OriginalSize = 0;
bool hasID3v2 = false;
if(ID3v2Location >= 0) {
ID3v2::Tag tag(d->file, ID3v2Location);
ID3v2OriginalSize = tag.header()->completeTagSize();
if(tag.header()->tagSize() > 0)
hasID3v2 = true;
// First, we assume that the file pointer is set at the first descriptor.
long offset = file->tell();
int version = headerVersion(file->readBlock(6));
// Next, we look for the descriptor.
if(version < 0) {
offset = file->find("MAC ", offset);
file->seek(offset);
version = headerVersion(file->readBlock(6));
}
long offset = 0;
if(hasID3v2)
offset = d->file->find("MAC ", ID3v2Location + ID3v2OriginalSize);
if(version < 0) {
debug("APE::Properties::read() -- APE descriptor not found");
return;
}
d->version = version;
if(d->version >= 3980)
analyzeCurrent(file);
else
offset = d->file->find("MAC ");
analyzeOld(file);
if(offset < 0) {
debug("APE::Properties::findDescriptor() -- APE descriptor not found");
return -1;
if(d->sampleFrames > 0 && d->sampleRate > 0) {
const double length = d->sampleFrames * 1000.0 / d->sampleRate;
d->length = static_cast<int>(length + 0.5);
d->bitrate = static_cast<int>(streamLength * 8.0 / length + 0.5);
}
return offset;
}
long APE::Properties::findID3v2()
{
if(!d->file->isValid())
return -1;
d->file->seek(0);
if(d->file->readBlock(3) == ID3v2::Header::fileIdentifier())
return 0;
return -1;
}
void APE::Properties::analyzeCurrent()
void APE::Properties::analyzeCurrent(File *file)
{
// Read the descriptor
d->file->seek(2, File::Current);
ByteVector descriptor = d->file->readBlock(44);
const uint descriptorBytes = descriptor.toUInt(0, false);
file->seek(2, File::Current);
const ByteVector descriptor = file->readBlock(44);
if(descriptor.size() < 44) {
debug("APE::Properties::analyzeCurrent() -- descriptor is too short.");
return;
}
if ((descriptorBytes - 52) > 0)
d->file->seek(descriptorBytes - 52, File::Current);
const unsigned int descriptorBytes = descriptor.toUInt(0, false);
if((descriptorBytes - 52) > 0)
file->seek(descriptorBytes - 52, File::Current);
// Read the header
ByteVector header = d->file->readBlock(24);
const ByteVector header = file->readBlock(24);
if(header.size() < 24) {
debug("APE::Properties::analyzeCurrent() -- MAC header is too short.");
return;
}
// Get the APE info
d->channels = header.toShort(18, false);
d->sampleRate = header.toUInt(20, false);
d->bitsPerSample = header.toShort(16, false);
//d->compressionLevel =
const uint totalFrames = header.toUInt(12, false);
const uint blocksPerFrame = header.toUInt(4, false);
const uint finalFrameBlocks = header.toUInt(8, false);
d->sampleFrames = totalFrames > 0 ? (totalFrames - 1) * blocksPerFrame + finalFrameBlocks : 0;
d->length = d->sampleRate > 0 ? d->sampleFrames / d->sampleRate : 0;
d->bitrate = d->length > 0 ? ((d->streamLength * 8L) / d->length) / 1000 : 0;
const unsigned int totalFrames = header.toUInt(12, false);
if(totalFrames == 0)
return;
const unsigned int blocksPerFrame = header.toUInt(4, false);
const unsigned int finalFrameBlocks = header.toUInt(8, false);
d->sampleFrames = (totalFrames - 1) * blocksPerFrame + finalFrameBlocks;
}
void APE::Properties::analyzeOld()
void APE::Properties::analyzeOld(File *file)
{
ByteVector header = d->file->readBlock(26);
const uint totalFrames = header.toUInt(18, false);
const ByteVector header = file->readBlock(26);
if(header.size() < 26) {
debug("APE::Properties::analyzeOld() -- MAC header is too short.");
return;
}
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.toShort(0, false);
uint blocksPerFrame;
unsigned int blocksPerFrame;
if(d->version >= 3950)
blocksPerFrame = 73728 * 4;
else if(d->version >= 3900 || (d->version >= 3800 && compressionLevel == 4000))
blocksPerFrame = 73728;
else
blocksPerFrame = 9216;
// Get the APE info
d->channels = header.toShort(4, false);
d->sampleRate = header.toUInt(6, false);
const uint finalFrameBlocks = header.toUInt(22, false);
const uint totalBlocks
= totalFrames > 0 ? (totalFrames - 1) * blocksPerFrame + finalFrameBlocks : 0;
d->length = totalBlocks / d->sampleRate;
d->bitrate = d->length > 0 ? ((d->streamLength * 8L) / d->length) / 1000 : 0;
}
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::Properties::analyzeOld() -- fmt header is too short.");
return;
}
d->bitsPerSample = fmt.toShort(26, false);
}

View File

@@ -51,27 +51,74 @@ namespace TagLib {
public:
/*!
* Create an instance of APE::Properties with the data read from the
* ByteVector \a data.
* APE::File \a file.
*
* \deprecated
*/
Properties(File *f, ReadStyle style = Average);
Properties(File *file, ReadStyle style = Average);
/*!
* Create an instance of APE::Properties with the data read from the
* APE::File \a file.
*/
Properties(File *file, long streamLength, ReadStyle style = Average);
/*!
* Destroys this APE::Properties instance.
*/
virtual ~Properties();
// Reimplementations.
/*!
* Returns the length of the file in seconds. The length is rounded down to
* the nearest whole second.
*
* \note This method is just an alias of lengthInSeconds().
*
* \deprecated
*/
virtual int length() const;
/*!
* Returns the length of the file in seconds. The length is rounded down to
* the nearest whole second.
*
* \see lengthInMilliseconds()
*/
// BIC: make virtual
int lengthInSeconds() const;
/*!
* Returns the length of the file in milliseconds.
*
* \see lengthInSeconds()
*/
// BIC: make virtual
int lengthInMilliseconds() const;
/*!
* Returns the average bit rate of the file in kb/s.
*/
virtual int bitrate() const;
/*!
* Returns the sample rate in Hz.
*/
virtual int sampleRate() const;
/*!
* Returns the number of audio channels.
*/
virtual int channels() const;
/*!
* Returns number of bits per sample.
* Returns the number of bits per audio sample.
*/
int bitsPerSample() const;
uint sampleFrames() const;
/*!
* Returns the total number of audio samples in file.
*/
unsigned int sampleFrames() const;
/*!
* Returns APE version.
@@ -82,13 +129,10 @@ namespace TagLib {
Properties(const Properties &);
Properties &operator=(const Properties &);
void read();
void read(File *file, long streamLength);
long findDescriptor();
long findID3v2();
void analyzeCurrent();
void analyzeOld();
void analyzeCurrent(File *file);
void analyzeOld(File *file);
class PropertiesPrivate;
PropertiesPrivate *d;

View File

@@ -23,7 +23,7 @@
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#ifdef __SUNPRO_CC
#if defined(__SUNPRO_CC) && (__SUNPRO_CC < 0x5130)
// Sun Studio finds multiple specializations of Map because
// it considers specializations with and without class types
// to be different; this define forces Map to use only the
@@ -35,6 +35,8 @@
#include <tstring.h>
#include <tmap.h>
#include <tpropertymap.h>
#include <tdebug.h>
#include <tutils.h>
#include "apetag.h"
#include "apefooter.h"
@@ -43,17 +45,43 @@
using namespace TagLib;
using namespace APE;
namespace
{
bool isKeyValid(const char *key, size_t length)
{
const char *invalidKeys[] = { "ID3", "TAG", "OGGS", "MP+", 0 };
if(length < 2 || length > 255)
return false;
// only allow printable ASCII including space (32..126)
for(const char *p = key; p < key + length; ++p) {
const int c = static_cast<unsigned char>(*p);
if(c < 32 || c > 126)
return false;
}
for(size_t i = 0; invalidKeys[i] != 0; ++i) {
if(Utils::equalsIgnoreCase(key, invalidKeys[i]))
return false;
}
return true;
}
}
class APE::Tag::TagPrivate
{
public:
TagPrivate() : file(0), footerLocation(-1), tagLength(0) {}
TagPrivate() :
file(0),
footerLocation(0) {}
TagLib::File *file;
File *file;
long footerLocation;
long tagLength;
Footer footer;
ItemListMap itemListMap;
};
@@ -61,14 +89,16 @@ public:
// public methods
////////////////////////////////////////////////////////////////////////////////
APE::Tag::Tag() : TagLib::Tag()
APE::Tag::Tag() :
TagLib::Tag(),
d(new TagPrivate())
{
d = new TagPrivate;
}
APE::Tag::Tag(TagLib::File *file, long footerLocation) : TagLib::Tag()
APE::Tag::Tag(TagLib::File *file, long footerLocation) :
TagLib::Tag(),
d(new TagPrivate())
{
d = new TagPrivate;
d->file = file;
d->footerLocation = footerLocation;
@@ -88,46 +118,46 @@ ByteVector APE::Tag::fileIdentifier()
String APE::Tag::title() const
{
if(d->itemListMap["TITLE"].isEmpty())
return String::null;
return d->itemListMap["TITLE"].toString();
return String();
return d->itemListMap["TITLE"].values().toString();
}
String APE::Tag::artist() const
{
if(d->itemListMap["ARTIST"].isEmpty())
return String::null;
return d->itemListMap["ARTIST"].toString();
return String();
return d->itemListMap["ARTIST"].values().toString();
}
String APE::Tag::album() const
{
if(d->itemListMap["ALBUM"].isEmpty())
return String::null;
return d->itemListMap["ALBUM"].toString();
return String();
return d->itemListMap["ALBUM"].values().toString();
}
String APE::Tag::comment() const
{
if(d->itemListMap["COMMENT"].isEmpty())
return String::null;
return d->itemListMap["COMMENT"].toString();
return String();
return d->itemListMap["COMMENT"].values().toString();
}
String APE::Tag::genre() const
{
if(d->itemListMap["GENRE"].isEmpty())
return String::null;
return d->itemListMap["GENRE"].toString();
return String();
return d->itemListMap["GENRE"].values().toString();
}
TagLib::uint APE::Tag::year() const
unsigned int APE::Tag::year() const
{
if(d->itemListMap["YEAR"].isEmpty())
return 0;
return d->itemListMap["YEAR"].toString().toInt();
}
TagLib::uint APE::Tag::track() const
unsigned int APE::Tag::track() const
{
if(d->itemListMap["TRACK"].isEmpty())
return 0;
@@ -159,7 +189,7 @@ void APE::Tag::setGenre(const String &s)
addValue("GENRE", s, true);
}
void APE::Tag::setYear(uint i)
void APE::Tag::setYear(unsigned int i)
{
if(i <= 0)
removeItem("YEAR");
@@ -167,7 +197,7 @@ void APE::Tag::setYear(uint i)
addValue("YEAR", String::number(i), true);
}
void APE::Tag::setTrack(uint i)
void APE::Tag::setTrack(unsigned int i)
{
if(i <= 0)
removeItem("TRACK");
@@ -175,14 +205,18 @@ void APE::Tag::setTrack(uint i)
addValue("TRACK", String::number(i), true);
}
// conversions of tag keys between what we use in PropertyMap and what's usual
// for APE tags
static const TagLib::uint keyConversionsSize = 5; //usual, APE
static const char *keyConversions[][2] = {{"TRACKNUMBER", "TRACK" },
{"DATE", "YEAR" },
{"ALBUMARTIST", "ALBUM ARTIST"},
{"DISCNUMBER", "DISC" },
{"REMIXER", "MIXARTIST" }};
namespace
{
// conversions of tag keys between what we use in PropertyMap and what's usual
// for APE tags
// usual, APE
const char *keyConversions[][2] = {{"TRACKNUMBER", "TRACK" },
{"DATE", "YEAR" },
{"ALBUMARTIST", "ALBUM ARTIST"},
{"DISCNUMBER", "DISC" },
{"REMIXER", "MIXARTIST" }};
const size_t keyConversionsSize = sizeof(keyConversions) / sizeof(keyConversions[0]);
}
PropertyMap APE::Tag::properties() const
{
@@ -192,14 +226,16 @@ PropertyMap APE::Tag::properties() const
String tagName = it->first.upper();
// if the item is Binary or Locator, or if the key is an invalid string,
// add to unsupportedData
if(it->second.type() != Item::Text || tagName.isNull())
if(it->second.type() != Item::Text || tagName.isEmpty()) {
properties.unsupportedData().append(it->first);
}
else {
// Some tags need to be handled specially
for(uint i = 0; i < keyConversionsSize; ++i)
for(size_t i = 0; i < keyConversionsSize; ++i) {
if(tagName == keyConversions[i][1])
tagName = keyConversions[i][0];
properties[tagName].append(it->second.toStringList());
}
properties[tagName].append(it->second.toStringList());
}
}
return properties;
@@ -217,7 +253,7 @@ PropertyMap APE::Tag::setProperties(const PropertyMap &origProps)
PropertyMap properties(origProps); // make a local copy that can be modified
// see comment in properties()
for(uint i = 0; i < keyConversionsSize; ++i)
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]);
@@ -229,11 +265,11 @@ PropertyMap APE::Tag::setProperties(const PropertyMap &origProps)
for(; remIt != itemListMap().end(); ++remIt) {
String key = remIt->first.upper();
// only remove if a) key is valid, b) type is text, c) key not contained in new properties
if(!key.isNull() && remIt->second.type() == APE::Item::Text && !properties.contains(key))
if(!key.isEmpty() && remIt->second.type() == APE::Item::Text && !properties.contains(key))
toRemove.append(remIt->first);
}
for (StringList::Iterator removeIt = toRemove.begin(); removeIt != toRemove.end(); removeIt++)
for(StringList::ConstIterator removeIt = toRemove.begin(); removeIt != toRemove.end(); removeIt++)
removeItem(*removeIt);
// now sync in the "forward direction"
@@ -244,7 +280,7 @@ PropertyMap APE::Tag::setProperties(const PropertyMap &origProps)
if(!checkKey(tagName))
invalid.insert(it->first, it->second);
else if(!(itemListMap().contains(tagName)) || !(itemListMap()[tagName].values() == it->second)) {
if(it->second.size() == 0)
if(it->second.isEmpty())
removeItem(tagName);
else {
StringList::ConstIterator valueIt = it->second.begin();
@@ -260,16 +296,11 @@ PropertyMap APE::Tag::setProperties(const PropertyMap &origProps)
bool APE::Tag::checkKey(const String &key)
{
if(key.size() < 2 || key.size() > 16)
return false;
for(String::ConstIterator it = key.begin(); it != key.end(); it++)
// only allow printable ASCII including space (32..127)
if (*it < 32 || *it >= 128)
return false;
String upperKey = key.upper();
if (upperKey=="ID3" || upperKey=="TAG" || upperKey=="OGGS" || upperKey=="MP+")
return false;
return true;
if(!key.isLatin1())
return false;
const std::string data = key.to8Bit(false);
return isKeyValid(data.c_str(), data.size());
}
APE::Footer *APE::Tag::footer() const
@@ -284,40 +315,46 @@ const APE::ItemListMap& APE::Tag::itemListMap() const
void APE::Tag::removeItem(const String &key)
{
Map<const String, Item>::Iterator it = d->itemListMap.find(key.upper());
if(it != d->itemListMap.end())
d->itemListMap.erase(it);
d->itemListMap.erase(key.upper());
}
void APE::Tag::addValue(const String &key, const String &value, bool replace)
{
if(replace)
removeItem(key);
if(!key.isEmpty() && !value.isEmpty()) {
if(!replace && d->itemListMap.contains(key)) {
// Text items may contain more than one value
if(APE::Item::Text == d->itemListMap.begin()->second.type())
d->itemListMap[key.upper()].appendValue(value);
// Binary or locator items may have only one value
else
setItem(key, Item(key, value));
}
else
setItem(key, Item(key, value));
}
if(value.isEmpty())
return;
// Text items may contain more than one value.
// Binary or locator items may have only one value, hence always replaced.
ItemListMap::Iterator it = d->itemListMap.find(key.upper());
if(it != d->itemListMap.end() && it->second.type() == Item::Text)
it->second.appendValue(value);
else
setItem(key, Item(key, value));
}
void APE::Tag::setData(const String &key, const ByteVector &value)
{
removeItem(key);
if(!key.isEmpty() && !value.isEmpty())
setItem(key, Item(key, value, true));
if(value.isEmpty())
return;
setItem(key, Item(key, value, true));
}
void APE::Tag::setItem(const String &key, const Item &item)
{
if(!key.isEmpty())
d->itemListMap.insert(key.upper(), item);
if(!checkKey(key)) {
debug("APE::Tag::setItem() - Couldn't set an item due to an invalid key.");
return;
}
d->itemListMap[key.upper()] = item;
}
bool APE::Tag::isEmpty() const
@@ -337,7 +374,7 @@ void APE::Tag::read()
d->footer.setData(d->file->readBlock(Footer::size()));
if(d->footer.tagSize() <= Footer::size() ||
d->footer.tagSize() > uint(d->file->length()))
d->footer.tagSize() > static_cast<unsigned long>(d->file->length()))
return;
d->file->seek(d->footerLocation + Footer::size() - d->footer.tagSize());
@@ -348,15 +385,11 @@ void APE::Tag::read()
ByteVector APE::Tag::render() const
{
ByteVector data;
uint itemCount = 0;
unsigned int itemCount = 0;
{
for(Map<const String, Item>::ConstIterator it = d->itemListMap.begin();
it != d->itemListMap.end(); ++it)
{
data.append(it->second.render());
itemCount++;
}
for(ItemListMap::ConstIterator it = d->itemListMap.begin(); it != d->itemListMap.end(); ++it) {
data.append(it->second.render());
itemCount++;
}
d->footer.setItemCount(itemCount);
@@ -368,16 +401,34 @@ ByteVector APE::Tag::render() const
void APE::Tag::parse(const ByteVector &data)
{
uint pos = 0;
// 11 bytes is the minimum size for an APE item
for(uint i = 0; i < d->footer.itemCount() && pos <= data.size() - 11; i++) {
APE::Item item;
item.parse(data.mid(pos));
if(data.size() < 11)
return;
d->itemListMap.insert(item.key().upper(), item);
unsigned int pos = 0;
pos += item.size();
for(unsigned int i = 0; i < d->footer.itemCount() && pos <= data.size() - 11; i++) {
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 unsigned int keyLength = nullPos - pos - 8;
const unsigned int valLegnth = data.toUInt(pos, false);
if(isKeyValid(&data[pos + 8], keyLength)){
APE::Item item;
item.parse(data.mid(pos));
d->itemListMap.insert(item.key().upper(), item);
}
else {
debug("APE::Tag::parse() - Skipped an item due to an invalid key.");
}
pos += keyLength + valLegnth + 9;
}
}

View File

@@ -92,16 +92,16 @@ namespace TagLib {
virtual String album() const;
virtual String comment() const;
virtual String genre() const;
virtual uint year() const;
virtual uint track() const;
virtual unsigned int year() const;
virtual unsigned int track() const;
virtual void setTitle(const String &s);
virtual void setArtist(const String &s);
virtual void setAlbum(const String &s);
virtual void setComment(const String &s);
virtual void setGenre(const String &s);
virtual void setYear(uint i);
virtual void setTrack(uint i);
virtual void setYear(unsigned int i);
virtual void setTrack(unsigned int i);
/*!
* Implements the unified tag dictionary interface -- export function.
@@ -143,7 +143,7 @@ namespace TagLib {
* Returns a reference to the item list map. This is an ItemListMap of
* all of the items in the tag.
*
* This is the most powerfull structure for accessing the items of the tag.
* This is the most powerful structure for accessing the items of the tag.
*
* APE tags are case-insensitive, all keys in this map have been converted
* to upper case.

View File

@@ -25,9 +25,11 @@
#include <taglib.h>
#include <tdebug.h>
#include "trefcounter.h"
#include <trefcounter.h>
#include "asfattribute.h"
#include "asffile.h"
#include "asfutils.h"
using namespace TagLib;
@@ -56,82 +58,86 @@ public:
// public members
////////////////////////////////////////////////////////////////////////////////
ASF::Attribute::Attribute()
ASF::Attribute::Attribute() :
d(new AttributePrivate())
{
d = new AttributePrivate;
d->type = UnicodeType;
}
ASF::Attribute::Attribute(const ASF::Attribute &other)
: d(other.d)
ASF::Attribute::Attribute(const ASF::Attribute &other) :
d(other.d)
{
d->ref();
}
ASF::Attribute::Attribute(const String &value) :
d(new AttributePrivate())
{
d->type = UnicodeType;
d->stringValue = value;
}
ASF::Attribute::Attribute(const ByteVector &value) :
d(new AttributePrivate())
{
d->type = BytesType;
d->byteVectorValue = value;
}
ASF::Attribute::Attribute(const ASF::Picture &value) :
d(new AttributePrivate())
{
d->type = BytesType;
d->pictureValue = value;
}
ASF::Attribute::Attribute(unsigned int value) :
d(new AttributePrivate())
{
d->type = DWordType;
d->intValue = value;
}
ASF::Attribute::Attribute(unsigned long long value) :
d(new AttributePrivate())
{
d->type = QWordType;
d->longLongValue = value;
}
ASF::Attribute::Attribute(unsigned short value) :
d(new AttributePrivate())
{
d->type = WordType;
d->shortValue = value;
}
ASF::Attribute::Attribute(bool value) :
d(new AttributePrivate())
{
d->type = BoolType;
d->boolValue = value;
}
ASF::Attribute &ASF::Attribute::operator=(const ASF::Attribute &other)
{
if(d->deref())
delete d;
d = other.d;
d->ref();
Attribute(other).swap(*this);
return *this;
}
void ASF::Attribute::swap(Attribute &other)
{
using std::swap;
swap(d, other.d);
}
ASF::Attribute::~Attribute()
{
if(d->deref())
delete d;
}
ASF::Attribute::Attribute(const String &value)
{
d = new AttributePrivate;
d->type = UnicodeType;
d->stringValue = value;
}
ASF::Attribute::Attribute(const ByteVector &value)
{
d = new AttributePrivate;
d->type = BytesType;
d->byteVectorValue = value;
}
ASF::Attribute::Attribute(const ASF::Picture &value)
{
d = new AttributePrivate;
d->type = BytesType;
d->pictureValue = value;
}
ASF::Attribute::Attribute(unsigned int value)
{
d = new AttributePrivate;
d->type = DWordType;
d->intValue = value;
}
ASF::Attribute::Attribute(unsigned long long value)
{
d = new AttributePrivate;
d->type = QWordType;
d->longLongValue = value;
}
ASF::Attribute::Attribute(unsigned short value)
{
d = new AttributePrivate;
d->type = WordType;
d->shortValue = value;
}
ASF::Attribute::Attribute(bool value)
{
d = new AttributePrivate;
d->type = BoolType;
d->boolValue = value;
}
ASF::Attribute::AttributeTypes ASF::Attribute::type() const
{
return d->type;
@@ -176,28 +182,28 @@ ASF::Picture ASF::Attribute::toPicture() const
String ASF::Attribute::parse(ASF::File &f, int kind)
{
uint size, nameLength;
unsigned int size, nameLength;
String name;
d->pictureValue = Picture::fromInvalid();
// extended content descriptor
if(kind == 0) {
nameLength = f.readWORD();
name = f.readString(nameLength);
d->type = ASF::Attribute::AttributeTypes(f.readWORD());
size = f.readWORD();
nameLength = readWORD(&f);
name = readString(&f, nameLength);
d->type = ASF::Attribute::AttributeTypes(readWORD(&f));
size = readWORD(&f);
}
// metadata & metadata library
else {
int temp = f.readWORD();
int temp = readWORD(&f);
// metadata library
if(kind == 2) {
d->language = temp;
}
d->stream = f.readWORD();
nameLength = f.readWORD();
d->type = ASF::Attribute::AttributeTypes(f.readWORD());
size = f.readDWORD();
name = f.readString(nameLength);
d->stream = readWORD(&f);
nameLength = readWORD(&f);
d->type = ASF::Attribute::AttributeTypes(readWORD(&f));
size = readDWORD(&f);
name = readString(&f, nameLength);
}
if(kind != 2 && size > 65535) {
@@ -206,28 +212,28 @@ String ASF::Attribute::parse(ASF::File &f, int kind)
switch(d->type) {
case WordType:
d->shortValue = f.readWORD();
d->shortValue = readWORD(&f);
break;
case BoolType:
if(kind == 0) {
d->boolValue = f.readDWORD() == 1;
d->boolValue = (readDWORD(&f) == 1);
}
else {
d->boolValue = f.readWORD() == 1;
d->boolValue = (readWORD(&f) == 1);
}
break;
case DWordType:
d->intValue = f.readDWORD();
d->intValue = readDWORD(&f);
break;
case QWordType:
d->longLongValue = f.readQWORD();
d->longLongValue = readQWORD(&f);
break;
case UnicodeType:
d->stringValue = f.readString(size);
d->stringValue = readString(&f, size);
break;
case BytesType:
@@ -295,7 +301,7 @@ ByteVector ASF::Attribute::render(const String &name, int kind) const
break;
case UnicodeType:
data.append(File::renderString(d->stringValue));
data.append(renderString(d->stringValue));
break;
case BytesType:
@@ -309,13 +315,13 @@ ByteVector ASF::Attribute::render(const String &name, int kind) const
}
if(kind == 0) {
data = File::renderString(name, true) +
data = renderString(name, true) +
ByteVector::fromShort((int)d->type, false) +
ByteVector::fromShort(data.size(), false) +
data;
}
else {
ByteVector nameData = File::renderString(name);
ByteVector nameData = renderString(name);
data = ByteVector::fromShort(kind == 2 ? d->language : 0, false) +
ByteVector::fromShort(d->stream, false) +
ByteVector::fromShort(nameData.size(), false) +
@@ -347,4 +353,3 @@ void ASF::Attribute::setStream(int value)
{
d->stream = value;
}

View File

@@ -115,6 +115,11 @@ namespace TagLib
*/
ASF::Attribute &operator=(const Attribute &other);
/*!
* Exchanges the content of the Attribute by the content of \a other.
*/
void swap(Attribute &other);
/*!
* Destroys the attribute.
*/

View File

@@ -27,206 +27,257 @@
#include <tbytevectorlist.h>
#include <tpropertymap.h>
#include <tstring.h>
#include "asffile.h"
#include "asftag.h"
#include "asfproperties.h"
#include "asfutils.h"
using namespace TagLib;
class ASF::File::FilePrivate
{
public:
class BaseObject;
class UnknownObject;
class FilePropertiesObject;
class StreamPropertiesObject;
class ContentDescriptionObject;
class ExtendedContentDescriptionObject;
class HeaderExtensionObject;
class CodecListObject;
class MetadataObject;
class MetadataLibraryObject;
FilePrivate():
size(0),
headerSize(0),
tag(0),
properties(0),
contentDescriptionObject(0),
extendedContentDescriptionObject(0),
headerExtensionObject(0),
metadataObject(0),
metadataLibraryObject(0) {}
unsigned long long size;
metadataLibraryObject(0)
{
objects.setAutoDelete(true);
}
~FilePrivate()
{
delete tag;
delete properties;
}
unsigned long long headerSize;
ASF::Tag *tag;
ASF::Properties *properties;
List<ASF::File::BaseObject *> objects;
ASF::File::ContentDescriptionObject *contentDescriptionObject;
ASF::File::ExtendedContentDescriptionObject *extendedContentDescriptionObject;
ASF::File::HeaderExtensionObject *headerExtensionObject;
ASF::File::MetadataObject *metadataObject;
ASF::File::MetadataLibraryObject *metadataLibraryObject;
List<BaseObject *> objects;
ContentDescriptionObject *contentDescriptionObject;
ExtendedContentDescriptionObject *extendedContentDescriptionObject;
HeaderExtensionObject *headerExtensionObject;
MetadataObject *metadataObject;
MetadataLibraryObject *metadataLibraryObject;
};
static ByteVector headerGuid("\x30\x26\xB2\x75\x8E\x66\xCF\x11\xA6\xD9\x00\xAA\x00\x62\xCE\x6C", 16);
static ByteVector filePropertiesGuid("\xA1\xDC\xAB\x8C\x47\xA9\xCF\x11\x8E\xE4\x00\xC0\x0C\x20\x53\x65", 16);
static ByteVector streamPropertiesGuid("\x91\x07\xDC\xB7\xB7\xA9\xCF\x11\x8E\xE6\x00\xC0\x0C\x20\x53\x65", 16);
static ByteVector contentDescriptionGuid("\x33\x26\xB2\x75\x8E\x66\xCF\x11\xA6\xD9\x00\xAA\x00\x62\xCE\x6C", 16);
static ByteVector extendedContentDescriptionGuid("\x40\xA4\xD0\xD2\x07\xE3\xD2\x11\x97\xF0\x00\xA0\xC9\x5E\xA8\x50", 16);
static ByteVector headerExtensionGuid("\xb5\x03\xbf_.\xa9\xcf\x11\x8e\xe3\x00\xc0\x0c Se", 16);
static ByteVector metadataGuid("\xEA\xCB\xF8\xC5\xAF[wH\204g\xAA\214D\xFAL\xCA", 16);
static ByteVector metadataLibraryGuid("\224\034#D\230\224\321I\241A\x1d\x13NEpT", 16);
static ByteVector contentEncryptionGuid("\xFB\xB3\x11\x22\x23\xBD\xD2\x11\xB4\xB7\x00\xA0\xC9\x55\xFC\x6E", 16);
static ByteVector extendedContentEncryptionGuid("\x14\xE6\x8A\x29\x22\x26 \x17\x4C\xB9\x35\xDA\xE0\x7E\xE9\x28\x9C", 16);
static ByteVector advancedContentEncryptionGuid("\xB6\x9B\x07\x7A\xA4\xDA\x12\x4E\xA5\xCA\x91\xD3\x8D\xC1\x1A\x8D", 16);
namespace
{
const ByteVector headerGuid("\x30\x26\xB2\x75\x8E\x66\xCF\x11\xA6\xD9\x00\xAA\x00\x62\xCE\x6C", 16);
const ByteVector filePropertiesGuid("\xA1\xDC\xAB\x8C\x47\xA9\xCF\x11\x8E\xE4\x00\xC0\x0C\x20\x53\x65", 16);
const ByteVector streamPropertiesGuid("\x91\x07\xDC\xB7\xB7\xA9\xCF\x11\x8E\xE6\x00\xC0\x0C\x20\x53\x65", 16);
const ByteVector contentDescriptionGuid("\x33\x26\xB2\x75\x8E\x66\xCF\x11\xA6\xD9\x00\xAA\x00\x62\xCE\x6C", 16);
const ByteVector extendedContentDescriptionGuid("\x40\xA4\xD0\xD2\x07\xE3\xD2\x11\x97\xF0\x00\xA0\xC9\x5E\xA8\x50", 16);
const ByteVector headerExtensionGuid("\xb5\x03\xbf_.\xa9\xcf\x11\x8e\xe3\x00\xc0\x0c Se", 16);
const ByteVector metadataGuid("\xEA\xCB\xF8\xC5\xAF[wH\204g\xAA\214D\xFAL\xCA", 16);
const ByteVector metadataLibraryGuid("\224\034#D\230\224\321I\241A\x1d\x13NEpT", 16);
const ByteVector codecListGuid("\x40\x52\xd1\x86\x1d\x31\xd0\x11\xa3\xa4\x00\xa0\xc9\x03\x48\xf6", 16);
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);
}
class ASF::File::BaseObject
class ASF::File::FilePrivate::BaseObject
{
public:
ByteVector data;
virtual ~BaseObject() {}
virtual ByteVector guid() = 0;
virtual ByteVector guid() const = 0;
virtual void parse(ASF::File *file, unsigned int size);
virtual ByteVector render(ASF::File *file);
};
class ASF::File::UnknownObject : public ASF::File::BaseObject
class ASF::File::FilePrivate::UnknownObject : public ASF::File::FilePrivate::BaseObject
{
ByteVector myGuid;
public:
UnknownObject(const ByteVector &guid);
ByteVector guid();
ByteVector guid() const;
};
class ASF::File::FilePropertiesObject : public ASF::File::BaseObject
class ASF::File::FilePrivate::FilePropertiesObject : public ASF::File::FilePrivate::BaseObject
{
public:
ByteVector guid();
void parse(ASF::File *file, uint size);
ByteVector guid() const;
void parse(ASF::File *file, unsigned int size);
};
class ASF::File::StreamPropertiesObject : public ASF::File::BaseObject
class ASF::File::FilePrivate::StreamPropertiesObject : public ASF::File::FilePrivate::BaseObject
{
public:
ByteVector guid();
void parse(ASF::File *file, uint size);
ByteVector guid() const;
void parse(ASF::File *file, unsigned int size);
};
class ASF::File::ContentDescriptionObject : public ASF::File::BaseObject
class ASF::File::FilePrivate::ContentDescriptionObject : public ASF::File::FilePrivate::BaseObject
{
public:
ByteVector guid();
void parse(ASF::File *file, uint size);
ByteVector guid() const;
void parse(ASF::File *file, unsigned int size);
ByteVector render(ASF::File *file);
};
class ASF::File::ExtendedContentDescriptionObject : public ASF::File::BaseObject
class ASF::File::FilePrivate::ExtendedContentDescriptionObject : public ASF::File::FilePrivate::BaseObject
{
public:
ByteVectorList attributeData;
ByteVector guid();
void parse(ASF::File *file, uint size);
ByteVector guid() const;
void parse(ASF::File *file, unsigned int size);
ByteVector render(ASF::File *file);
};
class ASF::File::MetadataObject : public ASF::File::BaseObject
class ASF::File::FilePrivate::MetadataObject : public ASF::File::FilePrivate::BaseObject
{
public:
ByteVectorList attributeData;
ByteVector guid();
void parse(ASF::File *file, uint size);
ByteVector guid() const;
void parse(ASF::File *file, unsigned int size);
ByteVector render(ASF::File *file);
};
class ASF::File::MetadataLibraryObject : public ASF::File::BaseObject
class ASF::File::FilePrivate::MetadataLibraryObject : public ASF::File::FilePrivate::BaseObject
{
public:
ByteVectorList attributeData;
ByteVector guid();
void parse(ASF::File *file, uint size);
ByteVector guid() const;
void parse(ASF::File *file, unsigned int size);
ByteVector render(ASF::File *file);
};
class ASF::File::HeaderExtensionObject : public ASF::File::BaseObject
class ASF::File::FilePrivate::HeaderExtensionObject : public ASF::File::FilePrivate::BaseObject
{
public:
List<ASF::File::BaseObject *> objects;
~HeaderExtensionObject();
ByteVector guid();
void parse(ASF::File *file, uint size);
List<ASF::File::FilePrivate::BaseObject *> objects;
HeaderExtensionObject();
ByteVector guid() const;
void parse(ASF::File *file, unsigned int size);
ByteVector render(ASF::File *file);
};
ASF::File::HeaderExtensionObject::~HeaderExtensionObject()
class ASF::File::FilePrivate::CodecListObject : public ASF::File::FilePrivate::BaseObject
{
for(unsigned int i = 0; i < objects.size(); i++) {
delete objects[i];
}
}
public:
ByteVector guid() const;
void parse(ASF::File *file, unsigned int size);
void ASF::File::BaseObject::parse(ASF::File *file, unsigned int size)
private:
enum CodecType
{
Video = 0x0001,
Audio = 0x0002,
Unknown = 0xFFFF
};
};
void ASF::File::FilePrivate::BaseObject::parse(ASF::File *file, unsigned int size)
{
data.clear();
if (size > 24 && size <= (unsigned int)(file->length()))
if(size > 24 && size <= (unsigned int)(file->length()))
data = file->readBlock(size - 24);
else
data = ByteVector::null;
data = ByteVector();
}
ByteVector ASF::File::BaseObject::render(ASF::File * /*file*/)
ByteVector ASF::File::FilePrivate::BaseObject::render(ASF::File * /*file*/)
{
return guid() + ByteVector::fromLongLong(data.size() + 24, false) + data;
}
ASF::File::UnknownObject::UnknownObject(const ByteVector &guid) : myGuid(guid)
ASF::File::FilePrivate::UnknownObject::UnknownObject(const ByteVector &guid) : myGuid(guid)
{
}
ByteVector ASF::File::UnknownObject::guid()
ByteVector ASF::File::FilePrivate::UnknownObject::guid() const
{
return myGuid;
}
ByteVector ASF::File::FilePropertiesObject::guid()
ByteVector ASF::File::FilePrivate::FilePropertiesObject::guid() const
{
return filePropertiesGuid;
}
void ASF::File::FilePropertiesObject::parse(ASF::File *file, uint size)
void ASF::File::FilePrivate::FilePropertiesObject::parse(ASF::File *file, unsigned int size)
{
BaseObject::parse(file, size);
file->d->properties->setLength(
(int)(data.toLongLong(40, false) / 10000000L - data.toLongLong(56, false) / 1000L));
if(data.size() < 64) {
debug("ASF::File::FilePrivate::FilePropertiesObject::parse() -- data is too short.");
return;
}
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));
}
ByteVector ASF::File::StreamPropertiesObject::guid()
ByteVector ASF::File::FilePrivate::StreamPropertiesObject::guid() const
{
return streamPropertiesGuid;
}
void ASF::File::StreamPropertiesObject::parse(ASF::File *file, uint size)
void ASF::File::FilePrivate::StreamPropertiesObject::parse(ASF::File *file, unsigned int size)
{
BaseObject::parse(file, size);
file->d->properties->setChannels(data.toShort(56, false));
if(data.size() < 70) {
debug("ASF::File::FilePrivate::StreamPropertiesObject::parse() -- data is too short.");
return;
}
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(data.toUInt(62, false) * 8 / 1000);
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::ContentDescriptionObject::guid()
ByteVector ASF::File::FilePrivate::ContentDescriptionObject::guid() const
{
return contentDescriptionGuid;
}
void ASF::File::ContentDescriptionObject::parse(ASF::File *file, uint /*size*/)
void ASF::File::FilePrivate::ContentDescriptionObject::parse(ASF::File *file, unsigned int /*size*/)
{
file->d->contentDescriptionObject = this;
int titleLength = file->readWORD();
int artistLength = file->readWORD();
int copyrightLength = file->readWORD();
int commentLength = file->readWORD();
int ratingLength = file->readWORD();
file->d->tag->setTitle(file->readString(titleLength));
file->d->tag->setArtist(file->readString(artistLength));
file->d->tag->setCopyright(file->readString(copyrightLength));
file->d->tag->setComment(file->readString(commentLength));
file->d->tag->setRating(file->readString(ratingLength));
const int titleLength = readWORD(file);
const int artistLength = readWORD(file);
const int copyrightLength = readWORD(file);
const int commentLength = readWORD(file);
const int ratingLength = readWORD(file);
file->d->tag->setTitle(readString(file,titleLength));
file->d->tag->setArtist(readString(file,artistLength));
file->d->tag->setCopyright(readString(file,copyrightLength));
file->d->tag->setComment(readString(file,commentLength));
file->d->tag->setRating(readString(file,ratingLength));
}
ByteVector ASF::File::ContentDescriptionObject::render(ASF::File *file)
ByteVector ASF::File::FilePrivate::ContentDescriptionObject::render(ASF::File *file)
{
ByteVector v1 = file->renderString(file->d->tag->title());
ByteVector v2 = file->renderString(file->d->tag->artist());
ByteVector v3 = file->renderString(file->d->tag->copyright());
ByteVector v4 = file->renderString(file->d->tag->comment());
ByteVector v5 = file->renderString(file->d->tag->rating());
const ByteVector v1 = renderString(file->d->tag->title());
const ByteVector v2 = renderString(file->d->tag->artist());
const ByteVector v3 = renderString(file->d->tag->copyright());
const ByteVector v4 = renderString(file->d->tag->comment());
const ByteVector v5 = renderString(file->d->tag->rating());
data.clear();
data.append(ByteVector::fromShort(v1.size(), false));
data.append(ByteVector::fromShort(v2.size(), false));
@@ -241,15 +292,15 @@ ByteVector ASF::File::ContentDescriptionObject::render(ASF::File *file)
return BaseObject::render(file);
}
ByteVector ASF::File::ExtendedContentDescriptionObject::guid()
ByteVector ASF::File::FilePrivate::ExtendedContentDescriptionObject::guid() const
{
return extendedContentDescriptionGuid;
}
void ASF::File::ExtendedContentDescriptionObject::parse(ASF::File *file, uint /*size*/)
void ASF::File::FilePrivate::ExtendedContentDescriptionObject::parse(ASF::File *file, unsigned int /*size*/)
{
file->d->extendedContentDescriptionObject = this;
int count = file->readWORD();
int count = readWORD(file);
while(count--) {
ASF::Attribute attribute;
String name = attribute.parse(*file);
@@ -257,23 +308,23 @@ void ASF::File::ExtendedContentDescriptionObject::parse(ASF::File *file, uint /*
}
}
ByteVector ASF::File::ExtendedContentDescriptionObject::render(ASF::File *file)
ByteVector ASF::File::FilePrivate::ExtendedContentDescriptionObject::render(ASF::File *file)
{
data.clear();
data.append(ByteVector::fromShort(attributeData.size(), false));
data.append(attributeData.toByteVector(ByteVector::null));
data.append(attributeData.toByteVector(""));
return BaseObject::render(file);
}
ByteVector ASF::File::MetadataObject::guid()
ByteVector ASF::File::FilePrivate::MetadataObject::guid() const
{
return metadataGuid;
}
void ASF::File::MetadataObject::parse(ASF::File *file, uint /*size*/)
void ASF::File::FilePrivate::MetadataObject::parse(ASF::File *file, unsigned int /*size*/)
{
file->d->metadataObject = this;
int count = file->readWORD();
int count = readWORD(file);
while(count--) {
ASF::Attribute attribute;
String name = attribute.parse(*file, 1);
@@ -281,23 +332,23 @@ void ASF::File::MetadataObject::parse(ASF::File *file, uint /*size*/)
}
}
ByteVector ASF::File::MetadataObject::render(ASF::File *file)
ByteVector ASF::File::FilePrivate::MetadataObject::render(ASF::File *file)
{
data.clear();
data.append(ByteVector::fromShort(attributeData.size(), false));
data.append(attributeData.toByteVector(ByteVector::null));
data.append(attributeData.toByteVector(""));
return BaseObject::render(file);
}
ByteVector ASF::File::MetadataLibraryObject::guid()
ByteVector ASF::File::FilePrivate::MetadataLibraryObject::guid() const
{
return metadataLibraryGuid;
}
void ASF::File::MetadataLibraryObject::parse(ASF::File *file, uint /*size*/)
void ASF::File::FilePrivate::MetadataLibraryObject::parse(ASF::File *file, unsigned int /*size*/)
{
file->d->metadataLibraryObject = this;
int count = file->readWORD();
int count = readWORD(file);
while(count--) {
ASF::Attribute attribute;
String name = attribute.parse(*file, 2);
@@ -305,24 +356,29 @@ void ASF::File::MetadataLibraryObject::parse(ASF::File *file, uint /*size*/)
}
}
ByteVector ASF::File::MetadataLibraryObject::render(ASF::File *file)
ByteVector ASF::File::FilePrivate::MetadataLibraryObject::render(ASF::File *file)
{
data.clear();
data.append(ByteVector::fromShort(attributeData.size(), false));
data.append(attributeData.toByteVector(ByteVector::null));
data.append(attributeData.toByteVector(""));
return BaseObject::render(file);
}
ByteVector ASF::File::HeaderExtensionObject::guid()
ASF::File::FilePrivate::HeaderExtensionObject::HeaderExtensionObject()
{
objects.setAutoDelete(true);
}
ByteVector ASF::File::FilePrivate::HeaderExtensionObject::guid() const
{
return headerExtensionGuid;
}
void ASF::File::HeaderExtensionObject::parse(ASF::File *file, uint /*size*/)
void ASF::File::FilePrivate::HeaderExtensionObject::parse(ASF::File *file, unsigned int /*size*/)
{
file->d->headerExtensionObject = this;
file->seek(18, File::Current);
long long dataSize = file->readDWORD();
long long dataSize = readDWORD(file);
long long dataPos = 0;
while(dataPos < dataSize) {
ByteVector guid = file->readBlock(16);
@@ -331,7 +387,7 @@ void ASF::File::HeaderExtensionObject::parse(ASF::File *file, uint /*size*/)
break;
}
bool ok;
long long size = file->readQWORD(&ok);
long long size = readQWORD(file, &ok);
if(!ok) {
file->setValid(false);
break;
@@ -352,47 +408,93 @@ void ASF::File::HeaderExtensionObject::parse(ASF::File *file, uint /*size*/)
}
}
ByteVector ASF::File::HeaderExtensionObject::render(ASF::File *file)
ByteVector ASF::File::FilePrivate::HeaderExtensionObject::render(ASF::File *file)
{
data.clear();
for(unsigned int i = 0; i < objects.size(); i++) {
data.append(objects[i]->render(file));
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::fromUInt(data.size(), false) + data;
return BaseObject::render(file);
}
ByteVector ASF::File::FilePrivate::CodecListObject::guid() const
{
return codecListGuid;
}
void ASF::File::FilePrivate::CodecListObject::parse(ASF::File *file, unsigned int size)
{
BaseObject::parse(file, size);
if(data.size() <= 20) {
debug("ASF::File::FilePrivate::CodecListObject::parse() -- data is too short.");
return;
}
unsigned int pos = 16;
const int count = data.toUInt(pos, false);
pos += 4;
for(int i = 0; i < count; ++i) {
if(pos >= data.size())
break;
const CodecType type = static_cast<CodecType>(data.toUShort(pos, false));
pos += 2;
int nameLength = data.toUShort(pos, false);
pos += 2;
const unsigned int namePos = pos;
pos += nameLength * 2;
const int descLength = data.toUShort(pos, false);
pos += 2;
const unsigned int descPos = pos;
pos += descLength * 2;
const int infoLength = data.toUShort(pos, false);
pos += 2 + infoLength * 2;
if(type == CodecListObject::Audio) {
// First audio codec found.
const String name(data.mid(namePos, nameLength * 2), String::UTF16LE);
file->d->properties->setCodecName(name.stripWhiteSpace());
const String desc(data.mid(descPos, descLength * 2), String::UTF16LE);
file->d->properties->setCodecDescription(desc.stripWhiteSpace());
break;
}
}
}
////////////////////////////////////////////////////////////////////////////////
// public members
////////////////////////////////////////////////////////////////////////////////
ASF::File::File(FileName file, bool readProperties, Properties::ReadStyle propertiesStyle)
: TagLib::File(file)
ASF::File::File(FileName file, bool, Properties::ReadStyle) :
TagLib::File(file),
d(new FilePrivate())
{
d = new FilePrivate;
if(isOpen())
read(readProperties, propertiesStyle);
read();
}
ASF::File::File(IOStream *stream, bool readProperties, Properties::ReadStyle propertiesStyle)
: TagLib::File(stream)
ASF::File::File(IOStream *stream, bool, Properties::ReadStyle) :
TagLib::File(stream),
d(new FilePrivate())
{
d = new FilePrivate;
if(isOpen())
read(readProperties, propertiesStyle);
read();
}
ASF::File::~File()
{
for(unsigned int i = 0; i < d->objects.size(); i++) {
delete d->objects[i];
}
if(d->tag) {
delete d->tag;
}
if(d->properties) {
delete d->properties;
}
delete d;
}
@@ -421,7 +523,95 @@ ASF::Properties *ASF::File::audioProperties() const
return d->properties;
}
void ASF::File::read(bool /*readProperties*/, Properties::ReadStyle /*propertiesStyle*/)
bool ASF::File::save()
{
if(readOnly()) {
debug("ASF::File::save() -- File is read only.");
return false;
}
if(!isValid()) {
debug("ASF::File::save() -- Trying to save invalid file.");
return false;
}
if(!d->contentDescriptionObject) {
d->contentDescriptionObject = new FilePrivate::ContentDescriptionObject();
d->objects.append(d->contentDescriptionObject);
}
if(!d->extendedContentDescriptionObject) {
d->extendedContentDescriptionObject = new FilePrivate::ExtendedContentDescriptionObject();
d->objects.append(d->extendedContentDescriptionObject);
}
if(!d->headerExtensionObject) {
d->headerExtensionObject = new FilePrivate::HeaderExtensionObject();
d->objects.append(d->headerExtensionObject);
}
if(!d->metadataObject) {
d->metadataObject = new FilePrivate::MetadataObject();
d->headerExtensionObject->objects.append(d->metadataObject);
}
if(!d->metadataLibraryObject) {
d->metadataLibraryObject = new FilePrivate::MetadataLibraryObject();
d->headerExtensionObject->objects.append(d->metadataLibraryObject);
}
d->extendedContentDescriptionObject->attributeData.clear();
d->metadataObject->attributeData.clear();
d->metadataLibraryObject->attributeData.clear();
const AttributeListMap allAttributes = d->tag->attributeListMap();
for(AttributeListMap::ConstIterator it = allAttributes.begin(); it != allAttributes.end(); ++it) {
const String &name = it->first;
const AttributeList &attributes = it->second;
bool inExtendedContentDescriptionObject = false;
bool inMetadataObject = false;
for(AttributeList::ConstIterator jt = attributes.begin(); jt != attributes.end(); ++jt) {
const Attribute &attribute = *jt;
const bool largeValue = (attribute.dataSize() > 65535);
const bool guid = (attribute.type() == Attribute::GuidType);
if(!inExtendedContentDescriptionObject && !guid && !largeValue && attribute.language() == 0 && attribute.stream() == 0) {
d->extendedContentDescriptionObject->attributeData.append(attribute.render(name));
inExtendedContentDescriptionObject = true;
}
else if(!inMetadataObject && !guid && !largeValue && attribute.language() == 0 && attribute.stream() != 0) {
d->metadataObject->attributeData.append(attribute.render(name, 1));
inMetadataObject = true;
}
else {
d->metadataLibraryObject->attributeData.append(attribute.render(name, 2));
}
}
}
ByteVector data;
for(List<FilePrivate::BaseObject *>::ConstIterator it = d->objects.begin(); it != d->objects.end(); ++it) {
data.append((*it)->render(this));
}
seek(16);
writeBlock(ByteVector::fromLongLong(data.size() + 30, false));
writeBlock(ByteVector::fromUInt(d->objects.size(), false));
writeBlock(ByteVector("\x01\x02", 2));
insert(data, 30, static_cast<unsigned long>(d->headerSize - 30));
d->headerSize = data.size() + 30;
return true;
}
////////////////////////////////////////////////////////////////////////////////
// private members
////////////////////////////////////////////////////////////////////////////////
void ASF::File::read()
{
if(!isValid())
return;
@@ -437,12 +627,12 @@ void ASF::File::read(bool /*readProperties*/, Properties::ReadStyle /*properties
d->properties = new ASF::Properties();
bool ok;
d->size = readQWORD(&ok);
d->headerSize = readQWORD(this, &ok);
if(!ok) {
setValid(false);
return;
}
int numObjects = readDWORD(&ok);
int numObjects = readDWORD(this, &ok);
if(!ok) {
setValid(false);
return;
@@ -450,31 +640,34 @@ void ASF::File::read(bool /*readProperties*/, Properties::ReadStyle /*properties
seek(2, Current);
for(int i = 0; i < numObjects; i++) {
ByteVector guid = readBlock(16);
guid = readBlock(16);
if(guid.size() != 16) {
setValid(false);
break;
}
long size = (long)readQWORD(&ok);
long size = (long)readQWORD(this, &ok);
if(!ok) {
setValid(false);
break;
}
BaseObject *obj;
FilePrivate::BaseObject *obj;
if(guid == filePropertiesGuid) {
obj = new FilePropertiesObject();
obj = new FilePrivate::FilePropertiesObject();
}
else if(guid == streamPropertiesGuid) {
obj = new StreamPropertiesObject();
obj = new FilePrivate::StreamPropertiesObject();
}
else if(guid == contentDescriptionGuid) {
obj = new ContentDescriptionObject();
obj = new FilePrivate::ContentDescriptionObject();
}
else if(guid == extendedContentDescriptionGuid) {
obj = new ExtendedContentDescriptionObject();
obj = new FilePrivate::ExtendedContentDescriptionObject();
}
else if(guid == headerExtensionGuid) {
obj = new HeaderExtensionObject();
obj = new FilePrivate::HeaderExtensionObject();
}
else if(guid == codecListGuid) {
obj = new FilePrivate::CodecListObject();
}
else {
if(guid == contentEncryptionGuid ||
@@ -482,149 +675,9 @@ void ASF::File::read(bool /*readProperties*/, Properties::ReadStyle /*properties
guid == advancedContentEncryptionGuid) {
d->properties->setEncrypted(true);
}
obj = new UnknownObject(guid);
obj = new FilePrivate::UnknownObject(guid);
}
obj->parse(this, size);
d->objects.append(obj);
}
}
bool ASF::File::save()
{
if(readOnly()) {
debug("ASF::File::save() -- File is read only.");
return false;
}
if(!isValid()) {
debug("ASF::File::save() -- Trying to save invalid file.");
return false;
}
if(!d->contentDescriptionObject) {
d->contentDescriptionObject = new ContentDescriptionObject();
d->objects.append(d->contentDescriptionObject);
}
if(!d->extendedContentDescriptionObject) {
d->extendedContentDescriptionObject = new ExtendedContentDescriptionObject();
d->objects.append(d->extendedContentDescriptionObject);
}
if(!d->headerExtensionObject) {
d->headerExtensionObject = new HeaderExtensionObject();
d->objects.append(d->headerExtensionObject);
}
if(!d->metadataObject) {
d->metadataObject = new MetadataObject();
d->headerExtensionObject->objects.append(d->metadataObject);
}
if(!d->metadataLibraryObject) {
d->metadataLibraryObject = new MetadataLibraryObject();
d->headerExtensionObject->objects.append(d->metadataLibraryObject);
}
ASF::AttributeListMap::ConstIterator it = d->tag->attributeListMap().begin();
for(; it != d->tag->attributeListMap().end(); it++) {
const String &name = it->first;
const AttributeList &attributes = it->second;
bool inExtendedContentDescriptionObject = false;
bool inMetadataObject = false;
for(unsigned int j = 0; j < attributes.size(); j++) {
const Attribute &attribute = attributes[j];
bool largeValue = attribute.dataSize() > 65535;
if(!inExtendedContentDescriptionObject && !largeValue && attribute.language() == 0 && attribute.stream() == 0) {
d->extendedContentDescriptionObject->attributeData.append(attribute.render(name));
inExtendedContentDescriptionObject = true;
}
else if(!inMetadataObject && !largeValue && attribute.language() == 0 && attribute.stream() != 0) {
d->metadataObject->attributeData.append(attribute.render(name, 1));
inMetadataObject = true;
}
else {
d->metadataLibraryObject->attributeData.append(attribute.render(name, 2));
}
}
}
ByteVector data;
for(unsigned int i = 0; i < d->objects.size(); i++) {
data.append(d->objects[i]->render(this));
}
data = headerGuid + ByteVector::fromLongLong(data.size() + 30, false) + ByteVector::fromUInt(d->objects.size(), false) + ByteVector("\x01\x02", 2) + data;
insert(data, 0, (TagLib::ulong)d->size);
return true;
}
////////////////////////////////////////////////////////////////////////////////
// protected members
////////////////////////////////////////////////////////////////////////////////
int ASF::File::readBYTE(bool *ok)
{
ByteVector v = readBlock(1);
if(v.size() != 1) {
if(ok) *ok = false;
return 0;
}
if(ok) *ok = true;
return v[0];
}
int ASF::File::readWORD(bool *ok)
{
ByteVector v = readBlock(2);
if(v.size() != 2) {
if(ok) *ok = false;
return 0;
}
if(ok) *ok = true;
return v.toUShort(false);
}
unsigned int ASF::File::readDWORD(bool *ok)
{
ByteVector v = readBlock(4);
if(v.size() != 4) {
if(ok) *ok = false;
return 0;
}
if(ok) *ok = true;
return v.toUInt(false);
}
long long ASF::File::readQWORD(bool *ok)
{
ByteVector v = readBlock(8);
if(v.size() != 8) {
if(ok) *ok = false;
return 0;
}
if(ok) *ok = true;
return v.toLongLong(false);
}
String ASF::File::readString(int length)
{
ByteVector data = readBlock(length);
unsigned int size = data.size();
while (size >= 2) {
if(data[size - 1] != '\0' || data[size - 2] != '\0') {
break;
}
size -= 2;
}
if(size != data.size()) {
data.resize(size);
}
return String(data, String::UTF16LE);
}
ByteVector ASF::File::renderString(const String &str, bool includeLength)
{
ByteVector data = str.data(String::UTF16LE) + ByteVector::fromShort(0, false);
if(includeLength) {
data = ByteVector::fromShort(data.size(), false) + data;
}
return data;
}

View File

@@ -54,7 +54,7 @@ namespace TagLib {
* \a propertiesStyle are ignored. The audio properties are always
* read.
*/
File(FileName file, bool readProperties = true,
File(FileName file, bool readProperties = true,
Properties::ReadStyle propertiesStyle = Properties::Average);
/*!
@@ -67,7 +67,7 @@ namespace TagLib {
* \note TagLib will *not* take ownership of the stream, the caller is
* responsible for deleting it after the File object.
*/
File(IOStream *stream, bool readProperties = true,
File(IOStream *stream, bool readProperties = true,
Properties::ReadStyle propertiesStyle = Properties::Average);
/*!
@@ -116,26 +116,7 @@ namespace TagLib {
virtual bool save();
private:
int readBYTE(bool *ok = 0);
int readWORD(bool *ok = 0);
unsigned int readDWORD(bool *ok = 0);
long long readQWORD(bool *ok = 0);
static ByteVector renderString(const String &str, bool includeLength = false);
String readString(int len);
void read(bool readProperties, Properties::ReadStyle propertiesStyle);
friend class Attribute;
friend class Picture;
class BaseObject;
class UnknownObject;
class FilePropertiesObject;
class StreamPropertiesObject;
class ContentDescriptionObject;
class ExtendedContentDescriptionObject;
class HeaderExtensionObject;
class MetadataObject;
class MetadataLibraryObject;
void read();
class FilePrivate;
FilePrivate *d;

View File

@@ -25,10 +25,12 @@
#include <taglib.h>
#include <tdebug.h>
#include "trefcounter.h"
#include <trefcounter.h>
#include "asfattribute.h"
#include "asffile.h"
#include "asfpicture.h"
#include "asfutils.h"
using namespace TagLib;
@@ -46,14 +48,14 @@ public:
// Picture class members
////////////////////////////////////////////////////////////////////////////////
ASF::Picture::Picture()
ASF::Picture::Picture() :
d(new PicturePrivate())
{
d = new PicturePrivate();
d->valid = true;
}
ASF::Picture::Picture(const Picture& other)
: d(other.d)
ASF::Picture::Picture(const Picture& other) :
d(other.d)
{
d->ref();
}
@@ -118,24 +120,27 @@ int ASF::Picture::dataSize() const
ASF::Picture& ASF::Picture::operator=(const ASF::Picture& other)
{
if(other.d != d) {
if(d->deref())
delete d;
d = other.d;
d->ref();
}
Picture(other).swap(*this);
return *this;
}
void ASF::Picture::swap(Picture &other)
{
using std::swap;
swap(d, other.d);
}
ByteVector ASF::Picture::render() const
{
if(!isValid())
return ByteVector::null;
return ByteVector();
return
ByteVector((char)d->type) +
ByteVector::fromUInt(d->picture.size(), false) +
ASF::File::renderString(d->mimeType) +
ASF::File::renderString(d->description) +
renderString(d->mimeType) +
renderString(d->description) +
d->picture;
}
@@ -146,7 +151,7 @@ void ASF::Picture::parse(const ByteVector& bytes)
return;
int pos = 0;
d->type = (Type)bytes[0]; ++pos;
const uint dataLen = bytes.toUInt(pos, false); pos+=4;
const unsigned int dataLen = bytes.toUInt(pos, false); pos+=4;
const ByteVector nullStringTerminator(2, 0);
@@ -176,4 +181,3 @@ ASF::Picture ASF::Picture::fromInvalid()
ret.d->valid = false;
return ret;
}

View File

@@ -117,6 +117,11 @@ namespace TagLib
*/
Picture& operator=(const Picture& other);
/*!
* Exchanges the content of the Picture by the content of \a other.
*/
void swap(Picture &other);
/*!
* Returns true if Picture stores valid picture
*/
@@ -205,8 +210,8 @@ namespace TagLib
/* THIS IS PRIVATE, DON'T TOUCH IT! */
void parse(const ByteVector& );
static Picture fromInvalid();
friend class Attribute;
#endif
private:
class PicturePrivate;
PicturePrivate *d;

View File

@@ -32,11 +32,23 @@ using namespace TagLib;
class ASF::Properties::PropertiesPrivate
{
public:
PropertiesPrivate(): length(0), bitrate(0), sampleRate(0), channels(0), encrypted(false) {}
PropertiesPrivate() :
length(0),
bitrate(0),
sampleRate(0),
channels(0),
bitsPerSample(0),
codec(ASF::Properties::Unknown),
encrypted(false) {}
int length;
int bitrate;
int sampleRate;
int channels;
int bitsPerSample;
ASF::Properties::Codec codec;
String codecName;
String codecDescription;
bool encrypted;
};
@@ -44,18 +56,28 @@ public:
// public members
////////////////////////////////////////////////////////////////////////////////
ASF::Properties::Properties() : AudioProperties(AudioProperties::Average)
ASF::Properties::Properties() :
AudioProperties(AudioProperties::Average),
d(new PropertiesPrivate())
{
d = new PropertiesPrivate;
}
ASF::Properties::~Properties()
{
if(d)
delete d;
delete d;
}
int ASF::Properties::length() const
{
return lengthInSeconds();
}
int ASF::Properties::lengthInSeconds() const
{
return d->length / 1000;
}
int ASF::Properties::lengthInMilliseconds() const
{
return d->length;
}
@@ -75,6 +97,26 @@ int ASF::Properties::channels() const
return d->channels;
}
int ASF::Properties::bitsPerSample() const
{
return d->bitsPerSample;
}
ASF::Properties::Codec ASF::Properties::codec() const
{
return d->codec;
}
String ASF::Properties::codecName() const
{
return d->codecName;
}
String ASF::Properties::codecDescription() const
{
return d->codecDescription;
}
bool ASF::Properties::isEncrypted() const
{
return d->encrypted;
@@ -84,28 +126,69 @@ bool ASF::Properties::isEncrypted() const
// private members
////////////////////////////////////////////////////////////////////////////////
void ASF::Properties::setLength(int length)
void ASF::Properties::setLength(int /*length*/)
{
d->length = length;
debug("ASF::Properties::setLength() -- This method is deprecated. Do not use.");
}
void ASF::Properties::setBitrate(int length)
void ASF::Properties::setLengthInMilliseconds(int value)
{
d->bitrate = length;
d->length = value;
}
void ASF::Properties::setSampleRate(int length)
void ASF::Properties::setBitrate(int value)
{
d->sampleRate = length;
d->bitrate = value;
}
void ASF::Properties::setChannels(int length)
void ASF::Properties::setSampleRate(int value)
{
d->channels = length;
d->sampleRate = value;
}
void ASF::Properties::setEncrypted(bool encrypted)
void ASF::Properties::setChannels(int value)
{
d->encrypted = encrypted;
d->channels = value;
}
void ASF::Properties::setBitsPerSample(int value)
{
d->bitsPerSample = value;
}
void ASF::Properties::setCodec(int value)
{
switch(value)
{
case 0x0160:
d->codec = WMA1;
break;
case 0x0161:
d->codec = WMA2;
break;
case 0x0162:
d->codec = WMA9Pro;
break;
case 0x0163:
d->codec = WMA9Lossless;
break;
default:
d->codec = Unknown;
break;
}
}
void ASF::Properties::setCodecName(const String &value)
{
d->codecName = value;
}
void ASF::Properties::setCodecDescription(const String &value)
{
d->codecDescription = value;
}
void ASF::Properties::setEncrypted(bool value)
{
d->encrypted = value;
}

View File

@@ -40,7 +40,38 @@ namespace TagLib {
public:
/*!
* Create an instance of ASF::Properties.
* Audio codec types can be used in ASF file.
*/
enum Codec
{
/*!
* Couldn't detect the codec.
*/
Unknown = 0,
/*!
* Windows Media Audio 1
*/
WMA1,
/*!
* Windows Media Audio 2 or above
*/
WMA2,
/*!
* Windows Media Audio 9 Professional
*/
WMA9Pro,
/*!
* Windows Media Audio 9 Lossless
*/
WMA9Lossless,
};
/*!
* Creates an instance of ASF::Properties.
*/
Properties();
@@ -49,18 +80,97 @@ namespace TagLib {
*/
virtual ~Properties();
// Reimplementations.
/*!
* Returns the length of the file in seconds. The length is rounded down to
* the nearest whole second.
*
* \note This method is just an alias of lengthInSeconds().
*
* \deprecated
*/
virtual int length() const;
/*!
* Returns the length of the file in seconds. The length is rounded down to
* the nearest whole second.
*
* \see lengthInMilliseconds()
*/
// BIC: make virtual
int lengthInSeconds() const;
/*!
* Returns the length of the file in milliseconds.
*
* \see lengthInSeconds()
*/
// BIC: make virtual
int lengthInMilliseconds() const;
/*!
* Returns the average bit rate of the file in kb/s.
*/
virtual int bitrate() const;
/*!
* Returns the sample rate in Hz.
*/
virtual int sampleRate() const;
/*!
* Returns the number of audio channels.
*/
virtual int channels() const;
/*!
* Returns the number of bits per audio sample.
*/
int bitsPerSample() const;
/*!
* Returns the codec used in the file.
*
* \see codecName()
* \see codecDescription()
*/
Codec codec() const;
/*!
* Returns the concrete codec name, for example "Windows Media Audio 9.1"
* used in the file if available, otherwise an empty string.
*
* \see codec()
* \see codecDescription()
*/
String codecName() const;
/*!
* Returns the codec description, typically contains the encoder settings,
* for example "VBR Quality 50, 44kHz, stereo 1-pass VBR" if available,
* otherwise an empty string.
*
* \see codec()
* \see codecName()
*/
String codecDescription() const;
/*!
* Returns whether or not the file is encrypted.
*/
bool isEncrypted() const;
#ifndef DO_NOT_DOCUMENT
// deprecated
void setLength(int value);
void setLengthInMilliseconds(int value);
void setBitrate(int value);
void setSampleRate(int value);
void setChannels(int value);
void setBitsPerSample(int value);
void setCodec(int value);
void setCodecName(const String &value);
void setCodecDescription(const String &value);
void setEncrypted(bool value);
#endif

View File

@@ -47,8 +47,7 @@ ASF::Tag::Tag()
ASF::Tag::~Tag()
{
if(d)
delete d;
delete d;
}
String ASF::Tag::title() const
@@ -65,7 +64,7 @@ String ASF::Tag::album() const
{
if(d->attributeListMap.contains("WM/AlbumTitle"))
return d->attributeListMap["WM/AlbumTitle"][0].toString();
return String::null;
return String();
}
String ASF::Tag::copyright() const
@@ -108,7 +107,7 @@ String ASF::Tag::genre() const
{
if(d->attributeListMap.contains("WM/Genre"))
return d->attributeListMap["WM/Genre"][0].toString();
return String::null;
return String();
}
void ASF::Tag::setTitle(const String &value)
@@ -146,12 +145,12 @@ void ASF::Tag::setGenre(const String &value)
setAttribute("WM/Genre", value);
}
void ASF::Tag::setYear(uint value)
void ASF::Tag::setYear(unsigned int value)
{
setAttribute("WM/Year", String::number(value));
}
void ASF::Tag::setTrack(uint value)
void ASF::Tag::setTrack(unsigned int value)
{
setAttribute("WM/TrackNumber", String::number(value));
}
@@ -161,11 +160,24 @@ ASF::AttributeListMap& ASF::Tag::attributeListMap()
return d->attributeListMap;
}
const ASF::AttributeListMap &ASF::Tag::attributeListMap() const
{
return d->attributeListMap;
}
bool ASF::Tag::contains(const String &key) const
{
return d->attributeListMap.contains(key);
}
void ASF::Tag::removeItem(const String &key)
{
AttributeListMap::Iterator it = d->attributeListMap.find(key);
if(it != d->attributeListMap.end())
d->attributeListMap.erase(it);
d->attributeListMap.erase(key);
}
ASF::AttributeList ASF::Tag::attribute(const String &name) const
{
return d->attributeListMap[name];
}
void ASF::Tag::setAttribute(const String &name, const Attribute &attribute)
@@ -175,6 +187,11 @@ void ASF::Tag::setAttribute(const String &name, const Attribute &attribute)
d->attributeListMap.insert(name, value);
}
void ASF::Tag::setAttribute(const String &name, const AttributeList &values)
{
d->attributeListMap.insert(name, values);
}
void ASF::Tag::addAttribute(const String &name, const Attribute &attribute)
{
if(d->attributeListMap.contains(name)) {
@@ -193,57 +210,64 @@ bool ASF::Tag::isEmpty() const
d->attributeListMap.isEmpty();
}
static const char *keyTranslation[][2] = {
{ "WM/AlbumTitle", "ALBUM" },
{ "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" },
};
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 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];
}
return String();
}
}
PropertyMap ASF::Tag::properties() const
{
static Map<String, String> keyMap;
if(keyMap.isEmpty()) {
int numKeys = sizeof(keyTranslation) / sizeof(keyTranslation[0]);
for(int i = 0; i < numKeys; i++) {
keyMap[keyTranslation[i][0]] = keyTranslation[i][1];
}
}
PropertyMap props;
if(!d->title.isEmpty()) {
@@ -261,8 +285,8 @@ PropertyMap ASF::Tag::properties() const
ASF::AttributeListMap::ConstIterator it = d->attributeListMap.begin();
for(; it != d->attributeListMap.end(); ++it) {
if(keyMap.contains(it->first)) {
String key = keyMap[it->first];
const String key = translateKey(it->first);
if(!key.isEmpty()) {
AttributeList::ConstIterator it2 = it->second.begin();
for(; it2 != it->second.end(); ++it2) {
if(key == "TRACKNUMBER") {
@@ -305,16 +329,16 @@ PropertyMap ASF::Tag::setProperties(const PropertyMap &props)
for(; it != origProps.end(); ++it) {
if(!props.contains(it->first) || props[it->first].isEmpty()) {
if(it->first == "TITLE") {
d->title = String::null;
d->title.clear();
}
else if(it->first == "ARTIST") {
d->artist = String::null;
d->artist.clear();
}
else if(it->first == "COMMENT") {
d->comment = String::null;
d->comment.clear();
}
else if(it->first == "COPYRIGHT") {
d->copyright = String::null;
d->copyright.clear();
}
else {
d->attributeListMap.erase(reverseKeyMap[it->first]);

View File

@@ -90,13 +90,13 @@ namespace TagLib {
/*!
* Returns the year; if there is no year set, this will return 0.
*/
virtual uint year() const;
virtual unsigned int year() const;
/*!
* Returns the track number; if there is no track number set, this will
* return 0.
*/
virtual uint track() const;
virtual unsigned int track() const;
/*!
* Sets the title to \a s.
@@ -137,12 +137,12 @@ namespace TagLib {
/*!
* Sets the year to \a i. If \a s is 0 then this value will be cleared.
*/
virtual void setYear(uint i);
virtual void setYear(unsigned int i);
/*!
* Sets the track to \a i. If \a s is 0 then this value will be cleared.
*/
virtual void setTrack(uint i);
virtual void setTrack(unsigned int i);
/*!
* Returns true if the tag does not contain any data. This should be
@@ -152,24 +152,43 @@ namespace TagLib {
virtual bool isEmpty() const;
/*!
* Returns a reference to the item list map. This is an AttributeListMap of
* all of the items in the tag.
*
* This is the most powerfull structure for accessing the items of the tag.
* \deprecated
*/
AttributeListMap &attributeListMap();
/*!
* Returns a reference to the item list map. This is an AttributeListMap of
* all of the items in the tag.
*/
const AttributeListMap &attributeListMap() const;
/*!
* \return True if a value for \a attribute is currently set.
*/
bool contains(const String &name) const;
/*!
* Removes the \a key attribute from the tag
*/
void removeItem(const String &name);
/*!
* \return The list of values for the key \a name, or an empty list if no
* values have been set.
*/
AttributeList attribute(const String &name) const;
/*!
* Sets the \a key attribute to the value of \a attribute. If an attribute
* with the \a key is already present, it will be replaced.
*/
void setAttribute(const String &name, const Attribute &attribute);
/*!
* Sets multiple \a values to the key \a name.
*/
void setAttribute(const String &name, const AttributeList &values);
/*!
* Sets the \a key attribute to the value of \a attribute. If an attribute
* with the \a key is already present, it will be added to the list.

104
taglib/asf/asfutils.h Normal file
View File

@@ -0,0 +1,104 @@
/***************************************************************************
copyright : (C) 2015 by Tsuda Kageyu
email : tsuda.kageyu@gmail.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_ASFUTILS_H
#define TAGLIB_ASFUTILS_H
// THIS FILE IS NOT A PART OF THE TAGLIB API
#ifndef DO_NOT_DOCUMENT // tell Doxygen not to document this header
namespace TagLib
{
namespace ASF
{
namespace
{
inline unsigned short readWORD(File *file, bool *ok = 0)
{
const ByteVector v = file->readBlock(2);
if(v.size() != 2) {
if(ok) *ok = false;
return 0;
}
if(ok) *ok = true;
return v.toUShort(false);
}
inline unsigned int readDWORD(File *file, bool *ok = 0)
{
const ByteVector v = file->readBlock(4);
if(v.size() != 4) {
if(ok) *ok = false;
return 0;
}
if(ok) *ok = true;
return v.toUInt(false);
}
inline long long readQWORD(File *file, bool *ok = 0)
{
const ByteVector v = file->readBlock(8);
if(v.size() != 8) {
if(ok) *ok = false;
return 0;
}
if(ok) *ok = true;
return v.toLongLong(false);
}
inline String readString(File *file, int length)
{
ByteVector data = file->readBlock(length);
unsigned int size = data.size();
while (size >= 2) {
if(data[size - 1] != '\0' || data[size - 2] != '\0') {
break;
}
size -= 2;
}
if(size != data.size()) {
data.resize(size);
}
return String(data, String::UTF16LE);
}
inline ByteVector renderString(const String &str, bool includeLength = false)
{
ByteVector data = str.data(String::UTF16LE) + ByteVector::fromShort(0, false);
if(includeLength) {
data = ByteVector::fromShort(data.size(), false) + data;
}
return data;
}
}
}
}
#endif
#endif

View File

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

View File

@@ -34,7 +34,7 @@ namespace TagLib {
/*!
* The values here are common to most audio formats. For more specific, codec
* dependant values, please see see the subclasses APIs. This is meant to
* dependent values, please see see the subclasses APIs. This is meant to
* compliment the TagLib::File and TagLib::Tag APIs in providing a simple
* interface that is sufficient for most applications.
*/
@@ -69,6 +69,23 @@ namespace TagLib {
*/
virtual int length() const = 0;
/*!
* Returns the length of the file in seconds. The length is rounded down to
* the nearest whole second.
*
* \see lengthInMilliseconds()
*/
// BIC: make virtual
int lengthInSeconds() const;
/*!
* Returns the length of the file in milliseconds.
*
* \see lengthInSeconds()
*/
// BIC: make virtual
int lengthInMilliseconds() const;
/*!
* Returns the most appropriate bit rate for the file in kb/s. For constant
* bitrate formats this is simply the bitrate of the file. For variable

View File

@@ -30,7 +30,7 @@
#include <tfile.h>
#include <tstring.h>
#include <tdebug.h>
#include "trefcounter.h"
#include <trefcounter.h>
#include "fileref.h"
#include "asffile.h"
@@ -54,41 +54,176 @@
using namespace TagLib;
namespace
{
typedef List<const FileRef::FileTypeResolver *> ResolverList;
ResolverList fileTypeResolvers;
// Templatized internal functions. T should be String or IOStream*.
template <typename T>
FileName toFileName(T arg)
{
debug("FileRef::toFileName<T>(): This version should never be called.");
return FileName(L"");
}
template <>
FileName toFileName<IOStream *>(IOStream *arg)
{
return arg->name();
}
template <>
FileName toFileName<FileName>(FileName arg)
{
return arg;
}
template <typename T>
File *resolveFileType(T arg, bool readProperties,
AudioProperties::ReadStyle style)
{
debug("FileRef::resolveFileType<T>(): This version should never be called.");
return 0;
}
template <>
File *resolveFileType<IOStream *>(IOStream *arg, bool readProperties,
AudioProperties::ReadStyle style)
{
return 0;
}
template <>
File *resolveFileType<FileName>(FileName arg, bool readProperties,
AudioProperties::ReadStyle style)
{
ResolverList::ConstIterator it = fileTypeResolvers.begin();
for(; it != fileTypeResolvers.end(); ++it) {
File *file = (*it)->createFile(arg, readProperties, style);
if(file)
return file;
}
return 0;
}
template <typename T>
File* createInternal(T arg, bool readAudioProperties,
AudioProperties::ReadStyle audioPropertiesStyle)
{
File *file = resolveFileType(arg, readAudioProperties, audioPropertiesStyle);
if(file)
return file;
#ifdef _WIN32
const String s = toFileName(arg).toString();
#else
const String s(toFileName(arg));
#endif
String ext;
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
// updated. However at some point that list should be created at the same time
// that a default file type resolver is created.
if(ext.isEmpty())
return 0;
if(ext == "MP3")
return new MPEG::File(arg, ID3v2::FrameFactory::instance(), readAudioProperties, audioPropertiesStyle);
if(ext == "OGG")
return new Ogg::Vorbis::File(arg, readAudioProperties, audioPropertiesStyle);
if(ext == "OGA") {
/* .oga can be any audio in the Ogg container. First try FLAC, then Vorbis. */
File *file = new Ogg::FLAC::File(arg, readAudioProperties, audioPropertiesStyle);
if(file->isValid())
return file;
delete file;
return new Ogg::Vorbis::File(arg, readAudioProperties, audioPropertiesStyle);
}
if(ext == "FLAC")
return new FLAC::File(arg, ID3v2::FrameFactory::instance(), readAudioProperties, audioPropertiesStyle);
if(ext == "MPC")
return new MPC::File(arg, readAudioProperties, audioPropertiesStyle);
if(ext == "WV")
return new WavPack::File(arg, readAudioProperties, audioPropertiesStyle);
if(ext == "SPX")
return new Ogg::Speex::File(arg, readAudioProperties, audioPropertiesStyle);
if(ext == "OPUS")
return new Ogg::Opus::File(arg, readAudioProperties, audioPropertiesStyle);
if(ext == "TTA")
return new TrueAudio::File(arg, readAudioProperties, audioPropertiesStyle);
if(ext == "M4A" || ext == "M4R" || ext == "M4B" || ext == "M4P" || ext == "MP4" || ext == "3G2" || ext == "M4V")
return new MP4::File(arg, readAudioProperties, audioPropertiesStyle);
if(ext == "WMA" || ext == "ASF")
return new ASF::File(arg, readAudioProperties, audioPropertiesStyle);
if(ext == "AIF" || ext == "AIFF" || ext == "AFC" || ext == "AIFC")
return new RIFF::AIFF::File(arg, readAudioProperties, audioPropertiesStyle);
if(ext == "WAV")
return new RIFF::WAV::File(arg, readAudioProperties, audioPropertiesStyle);
if(ext == "APE")
return new APE::File(arg, readAudioProperties, audioPropertiesStyle);
// module, nst and wow are possible but uncommon extensions
if(ext == "MOD" || ext == "MODULE" || ext == "NST" || ext == "WOW")
return new Mod::File(arg, readAudioProperties, audioPropertiesStyle);
if(ext == "S3M")
return new S3M::File(arg, readAudioProperties, audioPropertiesStyle);
if(ext == "IT")
return new IT::File(arg, readAudioProperties, audioPropertiesStyle);
if(ext == "XM")
return new XM::File(arg, readAudioProperties, audioPropertiesStyle);
return 0;
}
}
class FileRef::FileRefPrivate : public RefCounter
{
public:
FileRefPrivate(File *f) : RefCounter(), file(f) {}
FileRefPrivate(File *f) :
RefCounter(),
file(f) {}
~FileRefPrivate() {
delete file;
}
File *file;
static List<const FileTypeResolver *> fileTypeResolvers;
};
List<const FileRef::FileTypeResolver *> FileRef::FileRefPrivate::fileTypeResolvers;
////////////////////////////////////////////////////////////////////////////////
// public members
////////////////////////////////////////////////////////////////////////////////
FileRef::FileRef()
FileRef::FileRef() :
d(new FileRefPrivate(0))
{
d = new FileRefPrivate(0);
}
FileRef::FileRef(FileName fileName, bool readAudioProperties,
AudioProperties::ReadStyle audioPropertiesStyle)
AudioProperties::ReadStyle audioPropertiesStyle) :
d(new FileRefPrivate(createInternal(fileName, readAudioProperties, audioPropertiesStyle)))
{
d = new FileRefPrivate(create(fileName, readAudioProperties, audioPropertiesStyle));
}
FileRef::FileRef(File *file)
FileRef::FileRef(IOStream* stream, bool readAudioProperties, AudioProperties::ReadStyle audioPropertiesStyle) :
d(new FileRefPrivate(createInternal(stream, readAudioProperties, audioPropertiesStyle)))
{
d = new FileRefPrivate(file);
}
FileRef::FileRef(const FileRef &ref) : d(ref.d)
FileRef::FileRef(File *file) :
d(new FileRefPrivate(file))
{
}
FileRef::FileRef(const FileRef &ref) :
d(ref.d)
{
d->ref();
}
@@ -133,7 +268,7 @@ bool FileRef::save()
const FileRef::FileTypeResolver *FileRef::addFileTypeResolver(const FileRef::FileTypeResolver *resolver) // static
{
FileRefPrivate::fileTypeResolvers.prepend(resolver);
fileTypeResolvers.prepend(resolver);
return resolver;
}
@@ -155,6 +290,7 @@ StringList FileRef::defaultFileExtensions()
l.append("m4p");
l.append("3g2");
l.append("mp4");
l.append("m4v");
l.append("wma");
l.append("asf");
l.append("aif");
@@ -174,113 +310,34 @@ StringList FileRef::defaultFileExtensions()
bool FileRef::isNull() const
{
return !d->file || !d->file->isValid();
return (!d->file || !d->file->isValid());
}
FileRef &FileRef::operator=(const FileRef &ref)
{
if(&ref == this)
return *this;
if(d->deref())
delete d;
d = ref.d;
d->ref();
FileRef(ref).swap(*this);
return *this;
}
void FileRef::swap(FileRef &ref)
{
using std::swap;
swap(d, ref.d);
}
bool FileRef::operator==(const FileRef &ref) const
{
return ref.d->file == d->file;
return (ref.d->file == d->file);
}
bool FileRef::operator!=(const FileRef &ref) const
{
return ref.d->file != d->file;
return (ref.d->file != d->file);
}
File *FileRef::create(FileName fileName, bool readAudioProperties,
AudioProperties::ReadStyle audioPropertiesStyle) // static
{
List<const FileTypeResolver *>::ConstIterator it = FileRefPrivate::fileTypeResolvers.begin();
for(; it != FileRefPrivate::fileTypeResolvers.end(); ++it) {
File *file = (*it)->createFile(fileName, readAudioProperties, audioPropertiesStyle);
if(file)
return file;
}
// Ok, this is really dumb for now, but it works for testing.
String ext;
{
#ifdef _WIN32
String s = fileName.toString();
#else
String s = fileName;
#endif
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
// updated. However at some point that list should be created at the same time
// that a default file type resolver is created.
if(!ext.isEmpty()) {
if(ext == "MP3")
return new MPEG::File(fileName, readAudioProperties, audioPropertiesStyle);
if(ext == "OGG")
return new Ogg::Vorbis::File(fileName, readAudioProperties, audioPropertiesStyle);
if(ext == "OGA") {
/* .oga can be any audio in the Ogg container. First try FLAC, then Vorbis. */
File *file = new Ogg::FLAC::File(fileName, readAudioProperties, audioPropertiesStyle);
if (file->isValid())
return file;
delete file;
return new Ogg::Vorbis::File(fileName, readAudioProperties, audioPropertiesStyle);
}
if(ext == "FLAC")
return new FLAC::File(fileName, 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")
return new MP4::File(fileName, readAudioProperties, audioPropertiesStyle);
if(ext == "WMA" || ext == "ASF")
return new ASF::File(fileName, readAudioProperties, audioPropertiesStyle);
if(ext == "AIF" || ext == "AIFF")
return new RIFF::AIFF::File(fileName, readAudioProperties, audioPropertiesStyle);
if(ext == "WAV")
return new RIFF::WAV::File(fileName, readAudioProperties, audioPropertiesStyle);
if(ext == "APE")
return new APE::File(fileName, readAudioProperties, audioPropertiesStyle);
// module, nst and wow are possible but uncommon extensions
if(ext == "MOD" || ext == "MODULE" || ext == "NST" || ext == "WOW")
return new Mod::File(fileName, readAudioProperties, audioPropertiesStyle);
if(ext == "S3M")
return new S3M::File(fileName, readAudioProperties, audioPropertiesStyle);
if(ext == "IT")
return new IT::File(fileName, readAudioProperties, audioPropertiesStyle);
if(ext == "XM")
return new XM::File(fileName, readAudioProperties, audioPropertiesStyle);
}
return 0;
return createInternal(fileName, readAudioProperties, audioPropertiesStyle);
}

View File

@@ -72,7 +72,7 @@ namespace TagLib {
*
* class MyFileTypeResolver : FileTypeResolver
* {
* TagLib::File *createFile(TagLib::FileName *fileName, bool, AudioProperties::ReadStyle)
* TagLib::File *createFile(TagLib::FileName *fileName, bool, AudioProperties::ReadStyle) const
* {
* if(someCheckForAnMP3File(fileName))
* return new TagLib::MPEG::File(fileName);
@@ -128,7 +128,24 @@ namespace TagLib {
audioPropertiesStyle = AudioProperties::Average);
/*!
* Contruct a FileRef using \a file. The FileRef now takes ownership of the
* Construct a FileRef from an opened \a IOStream. If \a readAudioProperties
* is true then the audio properties will be read using \a audioPropertiesStyle.
* If \a readAudioProperties is false then \a audioPropertiesStyle will be
* ignored.
*
* Also see the note in the class documentation about why you may not want to
* use this method in your application.
*
* \note TagLib will *not* take ownership of the stream, the caller is
* responsible for deleting it after the File object.
*/
explicit FileRef(IOStream* stream,
bool readAudioProperties = true,
AudioProperties::ReadStyle
audioPropertiesStyle = AudioProperties::Average);
/*!
* Construct a FileRef using \a file. The FileRef now takes ownership of the
* pointer and will delete the File when it passes out of scope.
*/
explicit FileRef(File *file);
@@ -191,7 +208,7 @@ namespace TagLib {
* is tried.
*
* Returns a pointer to the added resolver (the same one that's passed in --
* this is mostly so that static inialializers have something to use for
* this is mostly so that static initializers have something to use for
* assignment).
*
* \see FileTypeResolver
@@ -209,7 +226,7 @@ namespace TagLib {
* by TagLib for resolution is case-insensitive.
*
* \note This does not account for any additional file type resolvers that
* are plugged in. Also note that this is not intended to replace a propper
* are plugged in. Also note that this is not intended to replace a proper
* mime-type resolution system, but is just here for reference.
*
* \see FileTypeResolver
@@ -226,6 +243,11 @@ namespace TagLib {
*/
FileRef &operator=(const FileRef &ref);
/*!
* Exchanges the content of the FileRef by the content of \a ref.
*/
void swap(FileRef &ref);
/*!
* Returns true if this FileRef and \a ref point to the same File object.
*/

View File

@@ -29,6 +29,7 @@
#include <tdebug.h>
#include <tagunion.h>
#include <tpropertymap.h>
#include <tagutils.h>
#include <id3v2header.h>
#include <id3v2tag.h>
@@ -44,93 +45,84 @@ using namespace TagLib;
namespace
{
typedef List<FLAC::MetadataBlock *> BlockList;
typedef BlockList::Iterator BlockIterator;
typedef BlockList::Iterator BlockConstIterator;
enum { FlacXiphIndex = 0, FlacID3v2Index = 1, FlacID3v1Index = 2 };
enum { MinPaddingLength = 4096 };
enum { LastBlockFlag = 0x80 };
const long MinPaddingLength = 4096;
const long MaxPaddingLegnth = 1024 * 1024;
const char LastBlockFlag = '\x80';
}
class FLAC::File::FilePrivate
{
public:
FilePrivate() :
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),
streamLength(0),
scanned(false),
hasXiphComment(false),
hasID3v2(false),
hasID3v1(false)
scanned(false)
{
blocks.setAutoDelete(true);
}
~FilePrivate()
{
uint size = blocks.size();
for(uint i = 0; i < size; i++) {
delete blocks[i];
}
delete properties;
}
const ID3v2::FrameFactory *ID3v2FrameFactory;
long ID3v2Location;
uint ID3v2OriginalSize;
long ID3v2OriginalSize;
long ID3v1Location;
TagUnion tag;
Properties *properties;
ByteVector streamInfoData;
ByteVector xiphCommentData;
List<MetadataBlock *> blocks;
BlockList blocks;
long flacStart;
long streamStart;
long streamLength;
bool scanned;
bool hasXiphComment;
bool hasID3v2;
bool hasID3v1;
};
////////////////////////////////////////////////////////////////////////////////
// public members
////////////////////////////////////////////////////////////////////////////////
FLAC::File::File(FileName file, bool readProperties,
Properties::ReadStyle propertiesStyle) :
TagLib::File(file)
FLAC::File::File(FileName file, bool readProperties, Properties::ReadStyle) :
TagLib::File(file),
d(new FilePrivate())
{
d = new FilePrivate;
if(isOpen())
read(readProperties, propertiesStyle);
read(readProperties);
}
FLAC::File::File(FileName file, ID3v2::FrameFactory *frameFactory,
bool readProperties, Properties::ReadStyle propertiesStyle) :
TagLib::File(file)
bool readProperties, Properties::ReadStyle) :
TagLib::File(file),
d(new FilePrivate(frameFactory))
{
d = new FilePrivate;
d->ID3v2FrameFactory = frameFactory;
if(isOpen())
read(readProperties, propertiesStyle);
read(readProperties);
}
FLAC::File::File(IOStream *stream, ID3v2::FrameFactory *frameFactory,
bool readProperties, Properties::ReadStyle propertiesStyle) :
TagLib::File(stream)
bool readProperties, Properties::ReadStyle) :
TagLib::File(stream),
d(new FilePrivate(frameFactory))
{
d = new FilePrivate;
d->ID3v2FrameFactory = frameFactory;
if(isOpen())
read(readProperties, propertiesStyle);
read(readProperties);
}
FLAC::File::~File()
@@ -145,30 +137,17 @@ TagLib::Tag *FLAC::File::tag() const
PropertyMap FLAC::File::properties() const
{
// once Tag::properties() is virtual, this case distinction could actually be done
// within TagUnion.
if(d->hasXiphComment)
return d->tag.access<Ogg::XiphComment>(FlacXiphIndex, false)->properties();
if(d->hasID3v2)
return d->tag.access<ID3v2::Tag>(FlacID3v2Index, false)->properties();
if(d->hasID3v1)
return d->tag.access<ID3v1::Tag>(FlacID3v1Index, false)->properties();
return PropertyMap();
return d->tag.properties();
}
void FLAC::File::removeUnsupportedProperties(const StringList &unsupported)
{
if(d->hasXiphComment)
d->tag.access<Ogg::XiphComment>(FlacXiphIndex, false)->removeUnsupportedProperties(unsupported);
if(d->hasID3v2)
d->tag.access<ID3v2::Tag>(FlacID3v2Index, false)->removeUnsupportedProperties(unsupported);
if(d->hasID3v1)
d->tag.access<ID3v1::Tag>(FlacID3v1Index, false)->removeUnsupportedProperties(unsupported);
d->tag.removeUnsupportedProperties(unsupported);
}
PropertyMap FLAC::File::setProperties(const PropertyMap &properties)
{
return d->tag.access<Ogg::XiphComment>(FlacXiphIndex, true)->setProperties(properties);
return xiphComment(true)->setProperties(properties);
}
FLAC::Properties *FLAC::File::audioProperties() const
@@ -176,7 +155,6 @@ FLAC::Properties *FLAC::File::audioProperties() const
return d->properties;
}
bool FLAC::File::save()
{
if(readOnly()) {
@@ -197,75 +175,123 @@ bool FLAC::File::save()
// Replace metadata blocks
bool foundVorbisCommentBlock = false;
List<MetadataBlock *> newBlocks;
for(uint i = 0; i < d->blocks.size(); i++) {
MetadataBlock *block = d->blocks[i];
if(block->code() == MetadataBlock::VorbisComment) {
for(BlockIterator it = d->blocks.begin(); it != d->blocks.end(); ++it) {
if((*it)->code() == MetadataBlock::VorbisComment) {
// Set the new Vorbis Comment block
delete block;
block = new UnknownMetadataBlock(MetadataBlock::VorbisComment, d->xiphCommentData);
foundVorbisCommentBlock = true;
delete *it;
d->blocks.erase(it);
break;
}
if(block->code() == MetadataBlock::Padding) {
delete block;
continue;
}
newBlocks.append(block);
}
if(!foundVorbisCommentBlock) {
newBlocks.append(new UnknownMetadataBlock(MetadataBlock::VorbisComment, d->xiphCommentData));
foundVorbisCommentBlock = true;
}
d->blocks = newBlocks;
d->blocks.append(new UnknownMetadataBlock(MetadataBlock::VorbisComment, d->xiphCommentData));
// Render data for the metadata blocks
ByteVector data;
for(uint i = 0; i < newBlocks.size(); i++) {
FLAC::MetadataBlock *block = newBlocks[i];
ByteVector blockData = block->render();
for(BlockConstIterator it = d->blocks.begin(); it != d->blocks.end(); ++it) {
ByteVector blockData = (*it)->render();
ByteVector blockHeader = ByteVector::fromUInt(blockData.size());
blockHeader[0] = block->code();
blockHeader[0] = (*it)->code();
data.append(blockHeader);
data.append(blockData);
}
// Adjust the padding block(s)
// Compute the amount of padding, and append that to data.
// TODO: Should be calculated in offset_t in taglib2.
long originalLength = d->streamStart - d->flacStart;
int paddingLength = originalLength - data.size() - 4;
if (paddingLength < 0) {
long paddingLength = originalLength - data.size() - 4;
if(paddingLength <= 0) {
paddingLength = MinPaddingLength;
}
ByteVector padding = ByteVector::fromUInt(paddingLength);
padding.resize(paddingLength + 4);
padding[0] = (char)(FLAC::MetadataBlock::Padding | LastBlockFlag);
data.append(padding);
else {
// Padding won't increase beyond 1% of the file size or 1MB.
long threshold = length() / 100;
threshold = std::max(threshold, MinPaddingLength);
threshold = std::min(threshold, MaxPaddingLegnth);
if(paddingLength > threshold)
paddingLength = MinPaddingLength;
}
ByteVector paddingHeader = ByteVector::fromUInt(paddingLength);
paddingHeader[0] = static_cast<char>(MetadataBlock::Padding | LastBlockFlag);
data.append(paddingHeader);
data.resize(static_cast<unsigned int>(data.size() + paddingLength));
// Write the data to the file
insert(data, d->flacStart, originalLength);
d->hasXiphComment = true;
d->streamStart += (static_cast<long>(data.size()) - originalLength);
if(d->ID3v1Location >= 0)
d->ID3v1Location += (static_cast<long>(data.size()) - originalLength);
// Update ID3 tags
if(ID3v2Tag()) {
if(d->hasID3v2) {
if(d->ID3v2Location < d->flacStart)
debug("FLAC::File::save() -- This can't be right -- an ID3v2 tag after the "
"start of the FLAC bytestream? Not writing the ID3v2 tag.");
else
insert(ID3v2Tag()->render(), d->ID3v2Location, d->ID3v2OriginalSize);
if(ID3v2Tag() && !ID3v2Tag()->isEmpty()) {
// ID3v2 tag is not empty. Update the old one or create a new one.
if(d->ID3v2Location < 0)
d->ID3v2Location = 0;
data = ID3v2Tag()->render();
insert(data, d->ID3v2Location, d->ID3v2OriginalSize);
d->flacStart += (static_cast<long>(data.size()) - d->ID3v2OriginalSize);
d->streamStart += (static_cast<long>(data.size()) - d->ID3v2OriginalSize);
if(d->ID3v1Location >= 0)
d->ID3v1Location += (static_cast<long>(data.size()) - d->ID3v2OriginalSize);
d->ID3v2OriginalSize = data.size();
}
else {
// ID3v2 tag is empty. Remove the old one.
if(d->ID3v2Location >= 0) {
removeBlock(d->ID3v2Location, d->ID3v2OriginalSize);
d->flacStart -= d->ID3v2OriginalSize;
d->streamStart -= d->ID3v2OriginalSize;
if(d->ID3v1Location >= 0)
d->ID3v1Location -= d->ID3v2OriginalSize;
d->ID3v2Location = -1;
d->ID3v2OriginalSize = 0;
}
else
insert(ID3v2Tag()->render(), 0, 0);
}
if(ID3v1Tag()) {
seek(-128, End);
if(ID3v1Tag() && !ID3v1Tag()->isEmpty()) {
// ID3v1 tag is not empty. Update the old one or create a new one.
if(d->ID3v1Location >= 0) {
seek(d->ID3v1Location);
}
else {
seek(0, End);
d->ID3v1Location = tell();
}
writeBlock(ID3v1Tag()->render());
}
else {
// ID3v1 tag is empty. Remove the old one.
if(d->ID3v1Location >= 0) {
truncate(d->ID3v1Location);
d->ID3v1Location = -1;
}
}
return true;
}
@@ -294,226 +320,23 @@ void FLAC::File::setID3v2FrameFactory(const ID3v2::FrameFactory *factory)
d->ID3v2FrameFactory = factory;
}
////////////////////////////////////////////////////////////////////////////////
// private members
////////////////////////////////////////////////////////////////////////////////
void FLAC::File::read(bool readProperties, Properties::ReadStyle propertiesStyle)
{
// Look for an ID3v2 tag
d->ID3v2Location = findID3v2();
if(d->ID3v2Location >= 0) {
d->tag.set(FlacID3v2Index, new ID3v2::Tag(this, d->ID3v2Location, d->ID3v2FrameFactory));
d->ID3v2OriginalSize = ID3v2Tag()->header()->completeTagSize();
if(ID3v2Tag()->header()->tagSize() <= 0)
d->tag.set(FlacID3v2Index, 0);
else
d->hasID3v2 = true;
}
// Look for an ID3v1 tag
d->ID3v1Location = findID3v1();
if(d->ID3v1Location >= 0) {
d->tag.set(FlacID3v1Index, new ID3v1::Tag(this, d->ID3v1Location));
d->hasID3v1 = true;
}
// Look for FLAC metadata, including vorbis comments
scan();
if(!isValid())
return;
if(d->hasXiphComment)
d->tag.set(FlacXiphIndex, new Ogg::XiphComment(xiphCommentData()));
else
d->tag.set(FlacXiphIndex, new Ogg::XiphComment);
if(readProperties)
d->properties = new Properties(streamInfoData(), streamLength(), propertiesStyle);
}
ByteVector FLAC::File::streamInfoData()
{
return isValid() ? d->streamInfoData : ByteVector();
}
ByteVector FLAC::File::xiphCommentData() const
{
return (isValid() && d->hasXiphComment) ? d->xiphCommentData : ByteVector();
debug("FLAC::File::streamInfoData() -- This function is obsolete. Returning an empty ByteVector.");
return ByteVector();
}
long FLAC::File::streamLength()
{
return d->streamLength;
}
void FLAC::File::scan()
{
// Scan the metadata pages
if(d->scanned)
return;
if(!isValid())
return;
long nextBlockOffset;
if(d->hasID3v2)
nextBlockOffset = find("fLaC", d->ID3v2Location + d->ID3v2OriginalSize);
else
nextBlockOffset = find("fLaC");
if(nextBlockOffset < 0) {
debug("FLAC::File::scan() -- FLAC stream not found");
setValid(false);
return;
}
nextBlockOffset += 4;
d->flacStart = nextBlockOffset;
seek(nextBlockOffset);
ByteVector header = readBlock(4);
// Header format (from spec):
// <1> Last-metadata-block flag
// <7> BLOCK_TYPE
// 0 : STREAMINFO
// 1 : PADDING
// ..
// 4 : VORBIS_COMMENT
// ..
// <24> Length of metadata to follow
char blockType = header[0] & 0x7f;
bool isLastBlock = (header[0] & 0x80) != 0;
uint length = header.toUInt(1U, 3U);
// First block should be the stream_info metadata
if(blockType != MetadataBlock::StreamInfo) {
debug("FLAC::File::scan() -- invalid FLAC stream");
setValid(false);
return;
}
d->streamInfoData = readBlock(length);
d->blocks.append(new UnknownMetadataBlock(blockType, d->streamInfoData));
nextBlockOffset += length + 4;
// Search through the remaining metadata
while(!isLastBlock) {
header = readBlock(4);
blockType = header[0] & 0x7f;
isLastBlock = (header[0] & 0x80) != 0;
length = header.toUInt(1U, 3U);
ByteVector data = readBlock(length);
if(data.size() != length || length == 0) {
debug("FLAC::File::scan() -- FLAC stream corrupted");
setValid(false);
return;
}
MetadataBlock *block = 0;
// Found the vorbis-comment
if(blockType == MetadataBlock::VorbisComment) {
if(!d->hasXiphComment) {
d->xiphCommentData = data;
d->hasXiphComment = true;
}
else {
debug("FLAC::File::scan() -- multiple Vorbis Comment blocks found, using the first one");
}
}
else if(blockType == MetadataBlock::Picture) {
FLAC::Picture *picture = new FLAC::Picture();
if(picture->parse(data)) {
block = picture;
}
else {
debug("FLAC::File::scan() -- invalid picture found, discarting");
delete picture;
}
}
if(!block) {
block = new UnknownMetadataBlock(blockType, data);
}
if(block->code() != MetadataBlock::Padding) {
d->blocks.append(block);
}
else {
delete block;
}
nextBlockOffset += length + 4;
if(nextBlockOffset >= File::length()) {
debug("FLAC::File::scan() -- FLAC stream corrupted");
setValid(false);
return;
}
seek(nextBlockOffset);
}
// End of metadata, now comes the datastream
d->streamStart = nextBlockOffset;
d->streamLength = File::length() - d->streamStart;
if(d->hasID3v1)
d->streamLength -= 128;
d->scanned = true;
}
long FLAC::File::findID3v1()
{
if(!isValid())
return -1;
seek(-128, End);
long p = tell();
if(readBlock(3) == ID3v1::Tag::fileIdentifier())
return p;
return -1;
}
long FLAC::File::findID3v2()
{
if(!isValid())
return -1;
seek(0);
if(readBlock(3) == ID3v2::Header::fileIdentifier())
return 0;
return -1;
debug("FLAC::File::streamLength() -- This function is obsolete. Returning zero.");
return 0;
}
List<FLAC::Picture *> FLAC::File::pictureList()
{
List<Picture *> pictures;
for(uint i = 0; i < d->blocks.size(); i++) {
Picture *picture = dynamic_cast<Picture *>(d->blocks[i]);
for(BlockConstIterator it = d->blocks.begin(); it != d->blocks.end(); ++it) {
Picture *picture = dynamic_cast<Picture *>(*it);
if(picture) {
pictures.append(picture);
}
@@ -528,8 +351,7 @@ void FLAC::File::addPicture(Picture *picture)
void FLAC::File::removePicture(Picture *picture, bool del)
{
MetadataBlock *block = picture;
List<MetadataBlock *>::Iterator it = d->blocks.find(block);
BlockIterator it = d->blocks.find(picture);
if(it != d->blocks.end())
d->blocks.erase(it);
@@ -539,30 +361,206 @@ void FLAC::File::removePicture(Picture *picture, bool del)
void FLAC::File::removePictures()
{
List<MetadataBlock *> newBlocks;
for(uint i = 0; i < d->blocks.size(); i++) {
Picture *picture = dynamic_cast<Picture *>(d->blocks[i]);
if(picture) {
delete picture;
for(BlockIterator it = d->blocks.begin(); it != d->blocks.end(); ) {
if(dynamic_cast<Picture *>(*it)) {
delete *it;
it = d->blocks.erase(it);
}
else {
newBlocks.append(d->blocks[i]);
++it;
}
}
d->blocks = newBlocks;
}
void FLAC::File::strip(int tags)
{
if(tags & ID3v1)
d->tag.set(FlacID3v1Index, 0);
if(tags & ID3v2)
d->tag.set(FlacID3v2Index, 0);
if(tags & XiphComment) {
xiphComment()->removeAllFields();
xiphComment()->removeAllPictures();
}
}
bool FLAC::File::hasXiphComment() const
{
return d->hasXiphComment;
return !d->xiphCommentData.isEmpty();
}
bool FLAC::File::hasID3v1Tag() const
{
return d->hasID3v1;
return (d->ID3v1Location >= 0);
}
bool FLAC::File::hasID3v2Tag() const
{
return d->hasID3v2;
return (d->ID3v2Location >= 0);
}
////////////////////////////////////////////////////////////////////////////////
// private members
////////////////////////////////////////////////////////////////////////////////
void FLAC::File::read(bool readProperties)
{
// Look for an ID3v2 tag
d->ID3v2Location = Utils::findID3v2(this);
if(d->ID3v2Location >= 0) {
d->tag.set(FlacID3v2Index, new ID3v2::Tag(this, d->ID3v2Location, d->ID3v2FrameFactory));
d->ID3v2OriginalSize = ID3v2Tag()->header()->completeTagSize();
}
// Look for an ID3v1 tag
d->ID3v1Location = Utils::findID3v1(this);
if(d->ID3v1Location >= 0)
d->tag.set(FlacID3v1Index, new ID3v1::Tag(this, d->ID3v1Location));
// Look for FLAC metadata, including vorbis comments
scan();
if(!isValid())
return;
if(!d->xiphCommentData.isEmpty())
d->tag.set(FlacXiphIndex, new Ogg::XiphComment(d->xiphCommentData));
else
d->tag.set(FlacXiphIndex, new Ogg::XiphComment());
if(readProperties) {
// First block should be the stream_info metadata
const ByteVector infoData = d->blocks.front()->render();
long streamLength;
if(d->ID3v1Location >= 0)
streamLength = d->ID3v1Location - d->streamStart;
else
streamLength = length() - d->streamStart;
d->properties = new Properties(infoData, streamLength);
}
}
void FLAC::File::scan()
{
// Scan the metadata pages
if(d->scanned)
return;
if(!isValid())
return;
long nextBlockOffset;
if(d->ID3v2Location >= 0)
nextBlockOffset = find("fLaC", d->ID3v2Location + d->ID3v2OriginalSize);
else
nextBlockOffset = find("fLaC");
if(nextBlockOffset < 0) {
debug("FLAC::File::scan() -- FLAC stream not found");
setValid(false);
return;
}
nextBlockOffset += 4;
d->flacStart = nextBlockOffset;
while(true) {
seek(nextBlockOffset);
const ByteVector header = readBlock(4);
// Header format (from spec):
// <1> Last-metadata-block flag
// <7> BLOCK_TYPE
// 0 : STREAMINFO
// 1 : PADDING
// ..
// 4 : VORBIS_COMMENT
// ..
// 6 : PICTURE
// ..
// <24> Length of metadata to follow
const char blockType = header[0] & ~LastBlockFlag;
const bool isLastBlock = (header[0] & LastBlockFlag) != 0;
const unsigned int blockLength = header.toUInt(1U, 3U);
// First block should be the stream_info metadata
if(d->blocks.isEmpty() && blockType != MetadataBlock::StreamInfo) {
debug("FLAC::File::scan() -- First block should be the stream_info metadata");
setValid(false);
return;
}
if(blockLength == 0 && blockType != MetadataBlock::Padding) {
debug("FLAC::File::scan() -- Zero-sized metadata block found");
setValid(false);
return;
}
const ByteVector data = readBlock(blockLength);
if(data.size() != blockLength) {
debug("FLAC::File::scan() -- Failed to read a metadata block");
setValid(false);
return;
}
MetadataBlock *block = 0;
// Found the vorbis-comment
if(blockType == MetadataBlock::VorbisComment) {
if(d->xiphCommentData.isEmpty()) {
d->xiphCommentData = data;
block = new UnknownMetadataBlock(MetadataBlock::VorbisComment, data);
}
else {
debug("FLAC::File::scan() -- multiple Vorbis Comment blocks found, discarding");
}
}
else if(blockType == MetadataBlock::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 = new UnknownMetadataBlock(blockType, data);
}
if(block)
d->blocks.append(block);
nextBlockOffset += blockLength + 4;
if(isLastBlock)
break;
}
// End of metadata, now comes the datastream
d->streamStart = nextBlockOffset;
d->scanned = true;
}

View File

@@ -46,7 +46,7 @@ namespace TagLib {
/*!
* This is implementation of FLAC metadata for non-Ogg FLAC files. At some
* point when Ogg / FLAC is more common there will be a similar implementation
* under the Ogg hiearchy.
* under the Ogg hierarchy.
*
* This supports ID3v1, ID3v2 and Xiph style comments as well as reading stream
* properties from the file.
@@ -66,6 +66,23 @@ namespace TagLib {
class TAGLIB_EXPORT File : public TagLib::File
{
public:
/*!
* This set of flags is used for various operations and is suitable for
* being OR-ed together.
*/
enum TagTypes {
//! Empty set. Matches no tag types.
NoTags = 0x0000,
//! Matches Vorbis comments.
XiphComment = 0x0001,
//! Matches ID3v1 tags.
ID3v1 = 0x0002,
//! Matches ID3v2 tags.
ID3v2 = 0x0004,
//! Matches all tag types.
AllTags = 0xffff
};
/*!
* Constructs a FLAC file from \a file. If \a readProperties is true the
* file's audio properties will also be read.
@@ -79,7 +96,7 @@ namespace TagLib {
Properties::ReadStyle propertiesStyle = Properties::Average);
/*!
* Constructs an APE file from \a file. If \a readProperties is true the
* Constructs an FLAC file from \a file. If \a readProperties is true the
* file's audio properties will also be read.
*
* If this file contains and ID3v2 tag the frames will be created using
@@ -165,8 +182,8 @@ namespace TagLib {
* if there is no valid ID3v2 tag. If \a create is true it will create
* an ID3v2 tag if one does not exist and returns a valid pointer.
*
* \note This may return a valid pointer regardless of whether or not the
* file on disk has an ID3v2 tag. Use hasID3v2Tag() to check if the file
* \note This may return a valid pointer regardless of whether or not the
* file on disk has an ID3v2 tag. Use hasID3v2Tag() to check if the file
* on disk actually has an ID3v2 tag.
*
* \note The Tag <b>is still</b> owned by the MPEG::File and should not be
@@ -184,8 +201,8 @@ namespace TagLib {
* if there is no valid APE tag. If \a create is true it will create
* an APE tag if one does not exist and returns a valid pointer.
*
* \note This may return a valid pointer regardless of whether or not the
* file on disk has an ID3v1 tag. Use hasID3v1Tag() to check if the file
* \note This may return a valid pointer regardless of whether or not the
* file on disk has an ID3v1 tag. Use hasID3v1Tag() to check if the file
* on disk actually has an ID3v1 tag.
*
* \note The Tag <b>is still</b> owned by the MPEG::File and should not be
@@ -203,10 +220,10 @@ namespace TagLib {
* if there is no valid XiphComment. If \a create is true it will create
* a XiphComment if one does not exist and returns a valid pointer.
*
* \note This may return a valid pointer regardless of whether or not the
* file on disk has a XiphComment. Use hasXiphComment() to check if the
* \note This may return a valid pointer regardless of whether or not the
* file on disk has a XiphComment. Use hasXiphComment() to check if the
* file on disk actually has a XiphComment.
*
*
* \note The Tag <b>is still</b> owned by the FLAC::File and should not be
* deleted by the user. It will be deleted when the file (object) is
* destroyed.
@@ -221,6 +238,7 @@ namespace TagLib {
* when
*
* \see ID3v2FrameFactory
* \deprecated This value should be passed in via the constructor
*/
void setID3v2FrameFactory(const ID3v2::FrameFactory *factory);
@@ -228,7 +246,7 @@ namespace TagLib {
* Returns the block of data used by FLAC::Properties for parsing the
* stream properties.
*
* \deprecated This method will not be public in a future release.
* \deprecated Always returns an empty vector.
*/
ByteVector streamInfoData(); // BIC: remove
@@ -236,7 +254,7 @@ namespace TagLib {
* Returns the length of the audio-stream, used by FLAC::Properties for
* calculating the bitrate.
*
* \deprecated This method will not be public in a future release.
* \deprecated Always returns zero.
*/
long streamLength(); // BIC: remove
@@ -264,6 +282,21 @@ namespace TagLib {
*/
void addPicture(Picture *picture);
/*!
* This will remove the tags that match the OR-ed together TagTypes from
* the file. By default it removes all tags.
*
* \warning This will also invalidate pointers to the tags as their memory
* will be freed.
*
* \note In order to make the removal permanent save() still needs to be
* called.
*
* \note This won't remove the Vorbis comment block completely. The
* vendor ID will be preserved.
*/
void strip(int tags = AllTags);
/*!
* Returns whether or not the file on disk actually has a XiphComment.
*
@@ -289,12 +322,8 @@ namespace TagLib {
File(const File &);
File &operator=(const File &);
void read(bool readProperties, Properties::ReadStyle propertiesStyle);
void read(bool readProperties);
void scan();
long findID3v2();
long findID3v1();
ByteVector xiphCommentData() const;
long findPaddingBreak(long nextPageOffset, long targetOffset, bool *isLast);
class FilePrivate;
FilePrivate *d;

View File

@@ -78,10 +78,10 @@ bool FLAC::Picture::parse(const ByteVector &data)
return false;
}
uint pos = 0;
unsigned int pos = 0;
d->type = FLAC::Picture::Type(data.toUInt(pos));
pos += 4;
uint mimeTypeLength = data.toUInt(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;
uint descriptionLength = data.toUInt(pos);
unsigned int descriptionLength = data.toUInt(pos);
pos += 4;
if(pos + descriptionLength + 20 > data.size()) {
debug("Invalid picture block.");
@@ -105,7 +105,7 @@ bool FLAC::Picture::parse(const ByteVector &data)
pos += 4;
d->numColors = data.toUInt(pos);
pos += 4;
uint dataLength = data.toUInt(pos);
unsigned int dataLength = data.toUInt(pos);
pos += 4;
if(pos + dataLength > data.size()) {
debug("Invalid picture block.");

View File

@@ -34,24 +34,18 @@ using namespace TagLib;
class FLAC::Properties::PropertiesPrivate
{
public:
PropertiesPrivate(ByteVector d, long st, ReadStyle s) :
data(d),
streamLength(st),
style(s),
PropertiesPrivate() :
length(0),
bitrate(0),
sampleRate(0),
sampleWidth(0),
bitsPerSample(0),
channels(0),
sampleFrames(0) {}
ByteVector data;
long streamLength;
ReadStyle style;
int length;
int bitrate;
int sampleRate;
int sampleWidth;
int bitsPerSample;
int channels;
unsigned long long sampleFrames;
ByteVector signature;
@@ -61,16 +55,18 @@ public:
// public members
////////////////////////////////////////////////////////////////////////////////
FLAC::Properties::Properties(ByteVector data, long streamLength, ReadStyle style) : AudioProperties(style)
FLAC::Properties::Properties(ByteVector data, long streamLength, ReadStyle style) :
AudioProperties(style),
d(new PropertiesPrivate())
{
d = new PropertiesPrivate(data, streamLength, style);
read();
read(data, streamLength);
}
FLAC::Properties::Properties(File *file, ReadStyle style) : AudioProperties(style)
FLAC::Properties::Properties(File *, ReadStyle style) :
AudioProperties(style),
d(new PropertiesPrivate())
{
d = new PropertiesPrivate(file->streamInfoData(), file->streamLength(), style);
read();
debug("FLAC::Properties::Properties() - This constructor is no longer used.");
}
FLAC::Properties::~Properties()
@@ -79,6 +75,16 @@ FLAC::Properties::~Properties()
}
int FLAC::Properties::length() const
{
return lengthInSeconds();
}
int FLAC::Properties::lengthInSeconds() const
{
return d->length / 1000;
}
int FLAC::Properties::lengthInMilliseconds() const
{
return d->length;
}
@@ -93,9 +99,14 @@ int FLAC::Properties::sampleRate() const
return d->sampleRate;
}
int FLAC::Properties::bitsPerSample() const
{
return d->bitsPerSample;
}
int FLAC::Properties::sampleWidth() const
{
return d->sampleWidth;
return bitsPerSample();
}
int FLAC::Properties::channels() const
@@ -117,14 +128,14 @@ ByteVector FLAC::Properties::signature() const
// private members
////////////////////////////////////////////////////////////////////////////////
void FLAC::Properties::read()
void FLAC::Properties::read(const ByteVector &data, long streamLength)
{
if(d->data.size() < 18) {
if(data.size() < 18) {
debug("FLAC::Properties::read() - FLAC properties must contain at least 18 bytes.");
return;
}
uint pos = 0;
unsigned int pos = 0;
// Minimum block size (in samples)
pos += 2;
@@ -138,32 +149,28 @@ void FLAC::Properties::read()
// Maximum frame size (in bytes)
pos += 3;
uint flags = d->data.toUInt(pos, true);
const unsigned int flags = data.toUInt(pos, true);
pos += 4;
d->sampleRate = flags >> 12;
d->channels = ((flags >> 9) & 7) + 1;
d->sampleWidth = ((flags >> 4) & 31) + 1;
d->sampleRate = flags >> 12;
d->channels = ((flags >> 9) & 7) + 1;
d->bitsPerSample = ((flags >> 4) & 31) + 1;
// The last 4 bits are the most significant 4 bits for the 36 bit
// stream length in samples. (Audio files measured in days)
unsigned long long hi = flags & 0xf;
unsigned long long lo = d->data.toUInt(pos, true);
const unsigned long long hi = flags & 0xf;
const unsigned long long lo = data.toUInt(pos, true);
pos += 4;
d->sampleFrames = (hi << 32) | lo;
if(d->sampleRate > 0)
d->length = int(d->sampleFrames / d->sampleRate);
if(d->sampleFrames > 0 && d->sampleRate > 0) {
const double length = d->sampleFrames * 1000.0 / d->sampleRate;
d->length = static_cast<int>(length + 0.5);
d->bitrate = static_cast<int>(streamLength * 8.0 / length + 0.5);
}
// Uncompressed bitrate:
//d->bitrate = ((d->sampleRate * d->channels) / 1000) * d->sampleWidth;
// Real bitrate:
d->bitrate = d->length > 0 ? ((d->streamLength * 8UL) / d->length) / 1000 : 0;
d->signature = d->data.mid(pos, 32);
if(data.size() >= pos + 16)
d->signature = data.mid(pos, 16);
}

View File

@@ -64,27 +64,72 @@ namespace TagLib {
*/
virtual ~Properties();
// Reimplementations.
/*!
* Returns the length of the file in seconds. The length is rounded down to
* the nearest whole second.
*
* \note This method is just an alias of lengthInSeconds().
*
* \deprecated
*/
virtual int length() const;
/*!
* Returns the length of the file in seconds. The length is rounded down to
* the nearest whole second.
*
* \see lengthInMilliseconds()
*/
// BIC: make virtual
int lengthInSeconds() const;
/*!
* Returns the length of the file in milliseconds.
*
* \see lengthInSeconds()
*/
// BIC: make virtual
int lengthInMilliseconds() const;
/*!
* Returns the average bit rate of the file in kb/s.
*/
virtual int bitrate() const;
/*!
* Returns the sample rate in Hz.
*/
virtual int sampleRate() const;
/*!
* Returns the number of audio channels.
*/
virtual int channels() const;
/*!
* Returns the number of bits per audio sample as read from the FLAC
* identification header.
*/
int bitsPerSample() const;
/*!
* Returns the sample width as read from the FLAC identification
* header.
*
* \note This method is just an alias of bitsPerSample().
*
* \deprecated
*/
int sampleWidth() const;
/*!
* Return the number of sample frames
* Return the number of sample frames.
*/
unsigned long long sampleFrames() const;
/*!
* Returns the MD5 signature of the uncompressed audio stream as read
* from the stream info header header.
* from the stream info header.
*/
ByteVector signature() const;
@@ -92,7 +137,7 @@ namespace TagLib {
Properties(const Properties &);
Properties &operator=(const Properties &);
void read();
void read(const ByteVector &data, long streamLength);
class PropertiesPrivate;
PropertiesPrivate *d;

View File

@@ -5,7 +5,7 @@
/***************************************************************************
* This library is free software; you can redistribute it and/or modify *
* it under the terms of the GNU Lesser General Public License version *
* 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 *
@@ -15,10 +15,15 @@
* *
* 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 *
* 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 "tstringlist.h"
#include "itfile.h"
#include "tdebug.h"
@@ -96,9 +101,9 @@ bool IT::File::save()
seek(2, Current);
ushort length = 0;
ushort instrumentCount = 0;
ushort sampleCount = 0;
unsigned short length = 0;
unsigned short instrumentCount = 0;
unsigned short sampleCount = 0;
if(!readU16L(length) || !readU16L(instrumentCount) || !readU16L(sampleCount))
return false;
@@ -107,9 +112,9 @@ bool IT::File::save()
// write comment as instrument and sample names:
StringList lines = d->tag.comment().split("\n");
for(ushort i = 0; i < instrumentCount; ++ i) {
for(unsigned short i = 0; i < instrumentCount; ++ i) {
seek(192L + length + ((long)i << 2));
ulong instrumentOffset = 0;
unsigned long instrumentOffset = 0;
if(!readU32L(instrumentOffset))
return false;
@@ -118,28 +123,28 @@ bool IT::File::save()
if(i < lines.size())
writeString(lines[i], 25);
else
writeString(String::null, 25);
writeString(String(), 25);
writeByte(0);
}
for(ushort i = 0; i < sampleCount; ++ i) {
for(unsigned short i = 0; i < sampleCount; ++ i) {
seek(192L + length + ((long)instrumentCount << 2) + ((long)i << 2));
ulong sampleOffset = 0;
unsigned long sampleOffset = 0;
if(!readU32L(sampleOffset))
return false;
seek(sampleOffset + 20);
if((TagLib::uint)(i + instrumentCount) < lines.size())
if((unsigned int)(i + instrumentCount) < lines.size())
writeString(lines[i + instrumentCount], 25);
else
writeString(String::null, 25);
writeString(String(), 25);
writeByte(0);
}
// write rest as message:
StringList messageLines;
for(uint i = instrumentCount + sampleCount; i < lines.size(); ++ i)
for(unsigned int i = instrumentCount + sampleCount; i < lines.size(); ++ i)
messageLines.append(lines[i]);
ByteVector message = messageLines.toString("\r").data(String::Latin1);
@@ -149,15 +154,15 @@ bool IT::File::save()
message.resize(7999);
message.append((char)0);
ushort special = 0;
ushort messageLength = 0;
ulong messageOffset = 0;
unsigned short special = 0;
unsigned short messageLength = 0;
unsigned long messageOffset = 0;
seek(46);
if(!readU16L(special))
return false;
ulong fileSize = File::length();
unsigned long fileSize = File::length();
if(special & Properties::MessageAttached) {
seek(54);
if(!readU16L(messageLength) || !readU32L(messageOffset))
@@ -259,8 +264,8 @@ void IT::File::read(bool)
d->properties.setChannels(channels);
// real length might be shorter because of skips and terminator
ushort realLength = 0;
for(ushort i = 0; i < length; ++ i) {
unsigned short realLength = 0;
for(unsigned short i = 0; i < length; ++ i) {
READ_BYTE_AS(order);
if(order == 255) break;
if(order != 254) ++ realLength;
@@ -274,7 +279,7 @@ void IT::File::read(bool)
// Currently I just discard anything after a nil, but
// e.g. VLC seems to interprete a nil as a space. I
// don't know what is the proper behaviour.
for(ushort i = 0; i < instrumentCount; ++ i) {
for(unsigned short i = 0; i < instrumentCount; ++ i) {
seek(192L + length + ((long)i << 2));
READ_U32L_AS(instrumentOffset);
seek(instrumentOffset);
@@ -290,7 +295,7 @@ void IT::File::read(bool)
comment.append(instrumentName);
}
for(ushort i = 0; i < sampleCount; ++ i) {
for(unsigned short i = 0; i < sampleCount; ++ i) {
seek(192L + length + ((long)instrumentCount << 2) + ((long)i << 2));
READ_U32L_AS(sampleOffset);

View File

@@ -5,7 +5,7 @@
/***************************************************************************
* This library is free software; you can redistribute it and/or modify *
* it under the terms of the GNU Lesser General Public License version *
* 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 *
@@ -15,10 +15,15 @@
* *
* 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 *
* 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 "itproperties.h"
using namespace TagLib;
@@ -46,21 +51,21 @@ public:
{
}
int channels;
ushort lengthInPatterns;
ushort instrumentCount;
ushort sampleCount;
ushort patternCount;
ushort version;
ushort compatibleVersion;
ushort flags;
ushort special;
uchar globalVolume;
uchar mixVolume;
uchar tempo;
uchar bpmSpeed;
uchar panningSeparation;
uchar pitchWheelDepth;
int channels;
unsigned short lengthInPatterns;
unsigned short instrumentCount;
unsigned short sampleCount;
unsigned short patternCount;
unsigned short version;
unsigned short compatibleVersion;
unsigned short flags;
unsigned short special;
unsigned char globalVolume;
unsigned char mixVolume;
unsigned char tempo;
unsigned char bpmSpeed;
unsigned char panningSeparation;
unsigned char pitchWheelDepth;
};
IT::Properties::Properties(AudioProperties::ReadStyle propertiesStyle) :
@@ -79,6 +84,16 @@ int IT::Properties::length() const
return 0;
}
int IT::Properties::lengthInSeconds() const
{
return 0;
}
int IT::Properties::lengthInMilliseconds() const
{
return 0;
}
int IT::Properties::bitrate() const
{
return 0;
@@ -94,7 +109,7 @@ int IT::Properties::channels() const
return d->channels;
}
TagLib::ushort IT::Properties::lengthInPatterns() const
unsigned short IT::Properties::lengthInPatterns() const
{
return d->lengthInPatterns;
}
@@ -104,67 +119,67 @@ bool IT::Properties::stereo() const
return d->flags & Stereo;
}
TagLib::ushort IT::Properties::instrumentCount() const
unsigned short IT::Properties::instrumentCount() const
{
return d->instrumentCount;
}
TagLib::ushort IT::Properties::sampleCount() const
unsigned short IT::Properties::sampleCount() const
{
return d->sampleCount;
}
TagLib::ushort IT::Properties::patternCount() const
unsigned short IT::Properties::patternCount() const
{
return d->patternCount;
}
TagLib::ushort IT::Properties::version() const
unsigned short IT::Properties::version() const
{
return d->version;
}
TagLib::ushort IT::Properties::compatibleVersion() const
unsigned short IT::Properties::compatibleVersion() const
{
return d->compatibleVersion;
}
TagLib::ushort IT::Properties::flags() const
unsigned short IT::Properties::flags() const
{
return d->flags;
}
TagLib::ushort IT::Properties::special() const
unsigned short IT::Properties::special() const
{
return d->special;
}
uchar IT::Properties::globalVolume() const
unsigned char IT::Properties::globalVolume() const
{
return d->globalVolume;
}
uchar IT::Properties::mixVolume() const
unsigned char IT::Properties::mixVolume() const
{
return d->mixVolume;
}
uchar IT::Properties::tempo() const
unsigned char IT::Properties::tempo() const
{
return d->tempo;
}
uchar IT::Properties::bpmSpeed() const
unsigned char IT::Properties::bpmSpeed() const
{
return d->bpmSpeed;
}
uchar IT::Properties::panningSeparation() const
unsigned char IT::Properties::panningSeparation() const
{
return d->panningSeparation;
}
uchar IT::Properties::pitchWheelDepth() const
unsigned char IT::Properties::pitchWheelDepth() const
{
return d->pitchWheelDepth;
}
@@ -174,72 +189,72 @@ void IT::Properties::setChannels(int channels)
d->channels = channels;
}
void IT::Properties::setLengthInPatterns(ushort lengthInPatterns)
void IT::Properties::setLengthInPatterns(unsigned short lengthInPatterns)
{
d->lengthInPatterns = lengthInPatterns;
}
void IT::Properties::setInstrumentCount(ushort instrumentCount)
void IT::Properties::setInstrumentCount(unsigned short instrumentCount)
{
d->instrumentCount = instrumentCount;
}
void IT::Properties::setSampleCount(ushort sampleCount)
void IT::Properties::setSampleCount(unsigned short sampleCount)
{
d->sampleCount = sampleCount;
}
void IT::Properties::setPatternCount(ushort patternCount)
void IT::Properties::setPatternCount(unsigned short patternCount)
{
d->patternCount = patternCount;
}
void IT::Properties::setFlags(ushort flags)
void IT::Properties::setFlags(unsigned short flags)
{
d->flags = flags;
}
void IT::Properties::setSpecial(ushort special)
void IT::Properties::setSpecial(unsigned short special)
{
d->special = special;
}
void IT::Properties::setCompatibleVersion(ushort compatibleVersion)
void IT::Properties::setCompatibleVersion(unsigned short compatibleVersion)
{
d->compatibleVersion = compatibleVersion;
}
void IT::Properties::setVersion(ushort version)
void IT::Properties::setVersion(unsigned short version)
{
d->version = version;
}
void IT::Properties::setGlobalVolume(uchar globalVolume)
void IT::Properties::setGlobalVolume(unsigned char globalVolume)
{
d->globalVolume = globalVolume;
}
void IT::Properties::setMixVolume(uchar mixVolume)
void IT::Properties::setMixVolume(unsigned char mixVolume)
{
d->mixVolume = mixVolume;
}
void IT::Properties::setTempo(uchar tempo)
void IT::Properties::setTempo(unsigned char tempo)
{
d->tempo = tempo;
}
void IT::Properties::setBpmSpeed(uchar bpmSpeed)
void IT::Properties::setBpmSpeed(unsigned char bpmSpeed)
{
d->bpmSpeed = bpmSpeed;
}
void IT::Properties::setPanningSeparation(uchar panningSeparation)
void IT::Properties::setPanningSeparation(unsigned char panningSeparation)
{
d->panningSeparation = panningSeparation;
}
void IT::Properties::setPitchWheelDepth(uchar pitchWheelDepth)
void IT::Properties::setPitchWheelDepth(unsigned char pitchWheelDepth)
{
d->pitchWheelDepth = pitchWheelDepth;
}

View File

@@ -5,7 +5,7 @@
/***************************************************************************
* This library is free software; you can redistribute it and/or modify *
* it under the terms of the GNU Lesser General Public License version *
* 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 *
@@ -15,8 +15,12 @@
* *
* 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 *
* 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_ITPROPERTIES_H
@@ -51,42 +55,44 @@ namespace TagLib {
Properties(AudioProperties::ReadStyle propertiesStyle);
virtual ~Properties();
int length() const;
int bitrate() const;
int sampleRate() const;
int channels() const;
int length() const;
int lengthInSeconds() const;
int lengthInMilliseconds() const;
int bitrate() const;
int sampleRate() const;
int channels() const;
ushort lengthInPatterns() const;
bool stereo() const;
ushort instrumentCount() const;
ushort sampleCount() const;
ushort patternCount() const;
ushort version() const;
ushort compatibleVersion() const;
ushort flags() const;
ushort special() const;
uchar globalVolume() const;
uchar mixVolume() const;
uchar tempo() const;
uchar bpmSpeed() const;
uchar panningSeparation() const;
uchar pitchWheelDepth() const;
unsigned short lengthInPatterns() const;
bool stereo() const;
unsigned short instrumentCount() const;
unsigned short sampleCount() const;
unsigned short patternCount() const;
unsigned short version() const;
unsigned short compatibleVersion() const;
unsigned short flags() const;
unsigned short special() const;
unsigned char globalVolume() const;
unsigned char mixVolume() const;
unsigned char tempo() const;
unsigned char bpmSpeed() const;
unsigned char panningSeparation() const;
unsigned char pitchWheelDepth() const;
void setChannels(int channels);
void setLengthInPatterns(ushort lengthInPatterns);
void setInstrumentCount(ushort instrumentCount);
void setSampleCount (ushort sampleCount);
void setPatternCount(ushort patternCount);
void setVersion (ushort version);
void setCompatibleVersion(ushort compatibleVersion);
void setFlags (ushort flags);
void setSpecial (ushort special);
void setGlobalVolume(uchar globalVolume);
void setMixVolume (uchar mixVolume);
void setTempo (uchar tempo);
void setBpmSpeed (uchar bpmSpeed);
void setPanningSeparation(uchar panningSeparation);
void setPitchWheelDepth (uchar pitchWheelDepth);
void setLengthInPatterns(unsigned short lengthInPatterns);
void setInstrumentCount(unsigned short instrumentCount);
void setSampleCount (unsigned short sampleCount);
void setPatternCount(unsigned short patternCount);
void setVersion (unsigned short version);
void setCompatibleVersion(unsigned short compatibleVersion);
void setFlags (unsigned short flags);
void setSpecial (unsigned short special);
void setGlobalVolume(unsigned char globalVolume);
void setMixVolume (unsigned char mixVolume);
void setTempo (unsigned char tempo);
void setBpmSpeed (unsigned char bpmSpeed);
void setPanningSeparation(unsigned char panningSeparation);
void setPitchWheelDepth (unsigned char pitchWheelDepth);
private:
Properties(const Properties&);

View File

@@ -5,7 +5,7 @@
/***************************************************************************
* This library is free software; you can redistribute it and/or modify *
* it under the terms of the GNU Lesser General Public License version *
* 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 *
@@ -15,10 +15,15 @@
* *
* 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 *
* 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 "modfile.h"
#include "tstringlist.h"
#include "tdebug.h"
@@ -92,14 +97,14 @@ bool Mod::File::save()
seek(0);
writeString(d->tag.title(), 20);
StringList lines = d->tag.comment().split("\n");
uint n = std::min(lines.size(), d->properties.instrumentCount());
for(uint 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(uint i = n; i < d->properties.instrumentCount(); ++ i) {
writeString(String::null, 22);
for(unsigned int i = n; i < d->properties.instrumentCount(); ++ i) {
writeString(String(), 22);
seek(8, Current);
}
return true;
@@ -114,8 +119,8 @@ void Mod::File::read(bool)
ByteVector modId = readBlock(4);
READ_ASSERT(modId.size() == 4);
int channels = 4;
uint instruments = 31;
int channels = 4;
unsigned int instruments = 31;
if(modId == "M.K." || modId == "M!K!" || modId == "M&K!" || modId == "N.T.") {
d->tag.setTrackerName("ProTracker");
channels = 4;
@@ -159,7 +164,7 @@ void Mod::File::read(bool)
READ_STRING(d->tag.setTitle, 20);
StringList comment;
for(uint i = 0; i < instruments; ++ i) {
for(unsigned int i = 0; i < instruments; ++ i) {
READ_STRING_AS(instrumentName, 22);
// value in words, * 2 (<< 1) for bytes:
READ_U16B_AS(sampleLength);

View File

@@ -5,7 +5,7 @@
/***************************************************************************
* This library is free software; you can redistribute it and/or modify *
* it under the terms of the GNU Lesser General Public License version *
* 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 *
@@ -15,8 +15,12 @@
* *
* 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 *
* 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_MODFILE_H

View File

@@ -5,7 +5,7 @@
/***************************************************************************
* This library is free software; you can redistribute it and/or modify *
* it under the terms of the GNU Lesser General Public License version *
* 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 *
@@ -15,10 +15,15 @@
* *
* 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 *
* 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 "modfilebase.h"
@@ -33,14 +38,14 @@ Mod::FileBase::FileBase(IOStream *stream) : TagLib::File(stream)
{
}
void Mod::FileBase::writeString(const String &s, ulong 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, ulong size)
bool Mod::FileBase::readString(String &s, unsigned long size)
{
ByteVector data(readBlock(size));
if(data.size() < size) return false;
@@ -49,39 +54,39 @@ bool Mod::FileBase::readString(String &s, ulong size)
{
data.resize(index);
}
data.replace((char) 0xff, ' ');
data.replace('\xff', ' ');
s = data;
return true;
}
void Mod::FileBase::writeByte(uchar byte)
void Mod::FileBase::writeByte(unsigned char byte)
{
ByteVector data(1, byte);
writeBlock(data);
}
void Mod::FileBase::writeU16L(ushort number)
void Mod::FileBase::writeU16L(unsigned short number)
{
writeBlock(ByteVector::fromShort(number, false));
}
void Mod::FileBase::writeU32L(ulong number)
void Mod::FileBase::writeU32L(unsigned long number)
{
writeBlock(ByteVector::fromUInt(number, false));
}
void Mod::FileBase::writeU16B(ushort number)
void Mod::FileBase::writeU16B(unsigned short number)
{
writeBlock(ByteVector::fromShort(number, true));
}
void Mod::FileBase::writeU32B(ulong number)
void Mod::FileBase::writeU32B(unsigned long number)
{
writeBlock(ByteVector::fromUInt(number, true));
}
bool Mod::FileBase::readByte(uchar &byte)
bool Mod::FileBase::readByte(unsigned char &byte)
{
ByteVector data(readBlock(1));
if(data.size() < 1) return false;
@@ -89,7 +94,7 @@ bool Mod::FileBase::readByte(uchar &byte)
return true;
}
bool Mod::FileBase::readU16L(ushort &number)
bool Mod::FileBase::readU16L(unsigned short &number)
{
ByteVector data(readBlock(2));
if(data.size() < 2) return false;
@@ -97,14 +102,14 @@ bool Mod::FileBase::readU16L(ushort &number)
return true;
}
bool Mod::FileBase::readU32L(ulong &number) {
bool Mod::FileBase::readU32L(unsigned long &number) {
ByteVector data(readBlock(4));
if(data.size() < 4) return false;
number = data.toUInt(false);
return true;
}
bool Mod::FileBase::readU16B(ushort &number)
bool Mod::FileBase::readU16B(unsigned short &number)
{
ByteVector data(readBlock(2));
if(data.size() < 2) return false;
@@ -112,7 +117,7 @@ bool Mod::FileBase::readU16B(ushort &number)
return true;
}
bool Mod::FileBase::readU32B(ulong &number) {
bool Mod::FileBase::readU32B(unsigned long &number) {
ByteVector data(readBlock(4));
if(data.size() < 4) return false;
number = data.toUInt(true);

View File

@@ -5,7 +5,7 @@
/***************************************************************************
* This library is free software; you can redistribute it and/or modify *
* it under the terms of the GNU Lesser General Public License version *
* 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 *
@@ -15,8 +15,12 @@
* *
* 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 *
* 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_MODFILEBASE_H
@@ -40,19 +44,19 @@ namespace TagLib {
FileBase(FileName file);
FileBase(IOStream *stream);
void writeString(const String &s, ulong size, char padding = 0);
void writeByte(uchar byte);
void writeU16L(ushort number);
void writeU32L(ulong number);
void writeU16B(ushort number);
void writeU32B(ulong number);
void writeString(const String &s, unsigned long size, char padding = 0);
void writeByte(unsigned char byte);
void writeU16L(unsigned short number);
void writeU32L(unsigned long number);
void writeU16B(unsigned short number);
void writeU32B(unsigned long number);
bool readString(String &s, ulong size);
bool readByte(uchar &byte);
bool readU16L(ushort &number);
bool readU32L(ulong &number);
bool readU16B(ushort &number);
bool readU32B(ulong &number);
bool readString(String &s, unsigned long size);
bool readByte(unsigned char &byte);
bool readU16L(unsigned short &number);
bool readU32L(unsigned long &number);
bool readU16B(unsigned short &number);
bool readU32B(unsigned long &number);
};
}

View File

@@ -37,11 +37,11 @@
setter(number); \
}
#define READ_BYTE(setter) READ(setter,uchar,readByte)
#define READ_U16L(setter) READ(setter,ushort,readU16L)
#define READ_U32L(setter) READ(setter,ulong,readU32L)
#define READ_U16B(setter) READ(setter,ushort,readU16B)
#define READ_U32B(setter) READ(setter,ulong,readU32B)
#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 long,readU32L)
#define READ_U16B(setter) READ(setter,unsigned short,readU16B)
#define READ_U32B(setter) READ(setter,unsigned long,readU32B)
#define READ_STRING(setter,size) \
{ \
@@ -54,11 +54,11 @@
type name = 0; \
READ_ASSERT(read(name));
#define READ_BYTE_AS(name) READ_AS(uchar,name,readByte)
#define READ_U16L_AS(name) READ_AS(ushort,name,readU16L)
#define READ_U32L_AS(name) READ_AS(ulong,name,readU32L)
#define READ_U16B_AS(name) READ_AS(ushort,name,readU16B)
#define READ_U32B_AS(name) READ_AS(ulong,name,readU32B)
#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 long,name,readU32L)
#define READ_U16B_AS(name) READ_AS(unsigned short,name,readU16B)
#define READ_U32B_AS(name) READ_AS(unsigned long,name,readU32B)
#define READ_STRING_AS(name,size) \
String name; \

View File

@@ -5,7 +5,7 @@
/***************************************************************************
* This library is free software; you can redistribute it and/or modify *
* it under the terms of the GNU Lesser General Public License version *
* 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 *
@@ -15,10 +15,15 @@
* *
* 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 *
* 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 "modproperties.h"
using namespace TagLib;
@@ -34,9 +39,9 @@ public:
{
}
int channels;
uint instrumentCount;
uchar lengthInPatterns;
int channels;
unsigned int instrumentCount;
unsigned char lengthInPatterns;
};
Mod::Properties::Properties(AudioProperties::ReadStyle propertiesStyle) :
@@ -55,6 +60,16 @@ int Mod::Properties::length() const
return 0;
}
int Mod::Properties::lengthInSeconds() const
{
return 0;
}
int Mod::Properties::lengthInMilliseconds() const
{
return 0;
}
int Mod::Properties::bitrate() const
{
return 0;
@@ -70,12 +85,12 @@ int Mod::Properties::channels() const
return d->channels;
}
TagLib::uint Mod::Properties::instrumentCount() const
unsigned int Mod::Properties::instrumentCount() const
{
return d->instrumentCount;
}
uchar Mod::Properties::lengthInPatterns() const
unsigned char Mod::Properties::lengthInPatterns() const
{
return d->lengthInPatterns;
}
@@ -85,12 +100,12 @@ void Mod::Properties::setChannels(int channels)
d->channels = channels;
}
void Mod::Properties::setInstrumentCount(uint instrumentCount)
void Mod::Properties::setInstrumentCount(unsigned int instrumentCount)
{
d->instrumentCount = instrumentCount;
}
void Mod::Properties::setLengthInPatterns(uchar lengthInPatterns)
void Mod::Properties::setLengthInPatterns(unsigned char lengthInPatterns)
{
d->lengthInPatterns = lengthInPatterns;
}

View File

@@ -5,7 +5,7 @@
/***************************************************************************
* This library is free software; you can redistribute it and/or modify *
* it under the terms of the GNU Lesser General Public License version *
* 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 *
@@ -15,8 +15,12 @@
* *
* 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 *
* 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_MODPROPERTIES_H
@@ -35,18 +39,20 @@ namespace TagLib {
Properties(AudioProperties::ReadStyle propertiesStyle);
virtual ~Properties();
int length() const;
int bitrate() const;
int sampleRate() const;
int channels() const;
int length() const;
int lengthInSeconds() const;
int lengthInMilliseconds() const;
int bitrate() const;
int sampleRate() const;
int channels() const;
uint instrumentCount() const;
uchar lengthInPatterns() const;
unsigned int instrumentCount() const;
unsigned char lengthInPatterns() const;
void setChannels(int channels);
void setInstrumentCount(uint sampleCount);
void setLengthInPatterns(uchar lengthInPatterns);
void setInstrumentCount(unsigned int sampleCount);
void setLengthInPatterns(unsigned char lengthInPatterns);
private:
friend class File;

View File

@@ -5,7 +5,7 @@
/***************************************************************************
* This library is free software; you can redistribute it and/or modify *
* it under the terms of the GNU Lesser General Public License version *
* 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 *
@@ -15,10 +15,15 @@
* *
* 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 *
* 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 "modtag.h"
#include "tstringlist.h"
#include "tpropertymap.h"
@@ -55,12 +60,12 @@ String Mod::Tag::title() const
String Mod::Tag::artist() const
{
return String::null;
return String();
}
String Mod::Tag::album() const
{
return String::null;
return String();
}
String Mod::Tag::comment() const
@@ -70,15 +75,15 @@ String Mod::Tag::comment() const
String Mod::Tag::genre() const
{
return String::null;
return String();
}
TagLib::uint Mod::Tag::year() const
unsigned int Mod::Tag::year() const
{
return 0;
}
TagLib::uint Mod::Tag::track() const
unsigned int Mod::Tag::track() const
{
return 0;
}
@@ -110,11 +115,11 @@ void Mod::Tag::setGenre(const String &)
{
}
void Mod::Tag::setYear(uint)
void Mod::Tag::setYear(unsigned int)
{
}
void Mod::Tag::setTrack(uint)
void Mod::Tag::setTrack(unsigned int)
{
}
@@ -128,7 +133,7 @@ PropertyMap Mod::Tag::properties() const
PropertyMap properties;
properties["TITLE"] = d->title;
properties["COMMENT"] = d->comment;
if(!(d->trackerName.isNull()))
if(!(d->trackerName.isEmpty()))
properties["TRACKERNAME"] = d->trackerName;
return properties;
}
@@ -142,23 +147,23 @@ PropertyMap Mod::Tag::setProperties(const PropertyMap &origProps)
d->title = properties["TITLE"].front();
oneValueSet.append("TITLE");
} else
d->title = String::null;
d->title.clear();
if(properties.contains("COMMENT")) {
d->comment = properties["COMMENT"].front();
oneValueSet.append("COMMENT");
} else
d->comment = String::null;
d->comment.clear();
if(properties.contains("TRACKERNAME")) {
d->trackerName = properties["TRACKERNAME"].front();
oneValueSet.append("TRACKERNAME");
} else
d->trackerName = String::null;
d->trackerName.clear();
// for each tag that has been set above, remove the first entry in the corresponding
// value list. The others will be returned as unsupported by this format.
for(StringList::Iterator it = oneValueSet.begin(); it != oneValueSet.end(); ++it) {
for(StringList::ConstIterator it = oneValueSet.begin(); it != oneValueSet.end(); ++it) {
if(properties[*it].size() == 1)
properties.erase(*it);
else

View File

@@ -5,7 +5,7 @@
/***************************************************************************
* This library is free software; you can redistribute it and/or modify *
* it under the terms of the GNU Lesser General Public License version *
* 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 *
@@ -15,8 +15,12 @@
* *
* 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 *
* 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_MODTAG_H
@@ -50,39 +54,39 @@ namespace TagLib {
* Returns the track name; if no track name is present in the tag
* String::null will be returned.
*/
String title() const;
virtual String title() const;
/*!
* Not supported by module files. Therefore always returns String::null.
*/
String artist() const;
virtual String artist() const;
/*!
* Not supported by module files. Therefore always returns String::null.
*/
String album() const;
virtual String album() const;
/*!
* Returns the track comment derived from the instrument/sample/pattern
* names; if no comment is present in the tag String::null will be
* returned.
*/
String comment() const;
virtual String comment() const;
/*!
* Not supported by module files. Therefore always returns String::null.
*/
String genre() const;
virtual String genre() const;
/*!
* Not supported by module files. Therefore always returns 0.
*/
uint year() const;
virtual unsigned int year() const;
/*!
* Not supported by module files. Therefore always returns 0.
*/
uint track() const;
virtual unsigned int track() const;
/*!
* Returns the name of the tracker used to create/edit the module file.
@@ -97,21 +101,21 @@ namespace TagLib {
* Sets the title to \a title. If \a title is String::null then this
* value will be cleared.
*
* The length limits per file type are (1 characetr = 1 byte):
* The length limits per file type are (1 character = 1 byte):
* Mod 20 characters, S3M 27 characters, IT 25 characters and XM 20
* characters.
*/
void setTitle(const String &title);
virtual void setTitle(const String &title);
/*!
* Not supported by module files and therefore ignored.
*/
void setArtist(const String &artist);
virtual void setArtist(const String &artist);
/*!
* Not supported by module files and therefore ignored.
*/
void setAlbum(const String &album);
virtual void setAlbum(const String &album);
/*!
* Sets the comment to \a comment. If \a comment is String::null then
@@ -126,26 +130,26 @@ namespace TagLib {
* an thus the line length in comments are limited. Too big comments
* will be truncated.
*
* The line length limits per file type are (1 characetr = 1 byte):
* The line length limits per file type are (1 character = 1 byte):
* Mod 22 characters, S3M 27 characters, IT 25 characters and XM 22
* characters.
*/
void setComment(const String &comment);
virtual void setComment(const String &comment);
/*!
* Not supported by module files and therefore ignored.
*/
void setGenre(const String &genre);
virtual void setGenre(const String &genre);
/*!
* Not supported by module files and therefore ignored.
*/
void setYear(uint year);
virtual void setYear(unsigned int year);
/*!
* Not supported by module files and therefore ignored.
*/
void setTrack(uint track);
virtual void setTrack(unsigned int track);
/*!
* Sets the tracker name to \a trackerName. If \a trackerName is
@@ -169,7 +173,7 @@ namespace TagLib {
* Implements the unified property interface -- import function.
* Because of the limitations of the module file tag, any tags besides
* COMMENT, TITLE and, if it is an XM file, TRACKERNAME, will be
* returened. Additionally, if the map contains tags with multiple values,
* returned. Additionally, if the map contains tags with multiple values,
* all but the first will be contained in the returned map of unsupported
* properties.
*/

View File

@@ -23,6 +23,8 @@
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#include <climits>
#include <tdebug.h>
#include <tstring.h>
#include "mp4atom.h"
@@ -37,9 +39,11 @@ const char *MP4::Atom::containers[11] = {
MP4::Atom::Atom(File *file)
{
children.setAutoDelete(true);
offset = file->tell();
ByteVector header = file->readBlock(8);
if (header.size() != 8) {
if(header.size() != 8) {
// The atom header must be 8 bytes long, otherwise there is either
// trailing garbage or the file is truncated
debug("MP4: Couldn't read 8 bytes of data for atom header");
@@ -50,20 +54,25 @@ MP4::Atom::Atom(File *file)
length = header.toUInt();
if (length == 1) {
if(length == 1) {
const long long longLength = file->readBlock(8).toLongLong();
if (longLength >= 8 && longLength <= 0xFFFFFFFF) {
// The atom has a 64-bit length, but it's actually a 32-bit value
length = (long)longLength;
if(sizeof(long) == sizeof(long long)) {
length = static_cast<long>(longLength);
}
else {
if(longLength <= LONG_MAX) {
// The atom has a 64-bit length, but it's actually a 31-bit value
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) {
if(length < 8) {
debug("MP4: Invalid atom size");
length = 0;
file->seek(0, File::End);
@@ -83,7 +92,7 @@ MP4::Atom::Atom(File *file)
while(file->tell() < offset + length) {
MP4::Atom *child = new MP4::Atom(file);
children.append(child);
if (child->length == 0)
if(child->length == 0)
return;
}
return;
@@ -95,10 +104,6 @@ MP4::Atom::Atom(File *file)
MP4::Atom::~Atom()
{
for(unsigned int i = 0; i < children.size(); i++) {
delete children[i];
}
children.clear();
}
MP4::Atom *
@@ -107,9 +112,9 @@ MP4::Atom::find(const char *name1, const char *name2, const char *name3, const c
if(name1 == 0) {
return this;
}
for(unsigned int i = 0; i < children.size(); i++) {
if(children[i]->name == name1) {
return children[i]->find(name2, name3, name4);
for(AtomList::ConstIterator it = children.begin(); it != children.end(); ++it) {
if((*it)->name == name1) {
return (*it)->find(name2, name3, name4);
}
}
return 0;
@@ -119,12 +124,12 @@ MP4::AtomList
MP4::Atom::findall(const char *name, bool recursive)
{
MP4::AtomList result;
for(unsigned int i = 0; i < children.size(); i++) {
if(children[i]->name == name) {
result.append(children[i]);
for(AtomList::ConstIterator it = children.begin(); it != children.end(); ++it) {
if((*it)->name == name) {
result.append(*it);
}
if(recursive) {
result.append(children[i]->findall(name, recursive));
result.append((*it)->findall(name, recursive));
}
}
return result;
@@ -137,9 +142,9 @@ MP4::Atom::path(MP4::AtomList &path, const char *name1, const char *name2, const
if(name1 == 0) {
return true;
}
for(unsigned int i = 0; i < children.size(); i++) {
if(children[i]->name == name1) {
return children[i]->path(path, name2, name3);
for(AtomList::ConstIterator it = children.begin(); it != children.end(); ++it) {
if((*it)->name == name1) {
return (*it)->path(path, name2, name3);
}
}
return false;
@@ -147,6 +152,8 @@ MP4::Atom::path(MP4::AtomList &path, const char *name1, const char *name2, const
MP4::Atoms::Atoms(File *file)
{
atoms.setAutoDelete(true);
file->seek(0, File::End);
long end = file->tell();
file->seek(0);
@@ -160,18 +167,14 @@ MP4::Atoms::Atoms(File *file)
MP4::Atoms::~Atoms()
{
for(unsigned int i = 0; i < atoms.size(); i++) {
delete atoms[i];
}
atoms.clear();
}
MP4::Atom *
MP4::Atoms::find(const char *name1, const char *name2, const char *name3, const char *name4)
{
for(unsigned int i = 0; i < atoms.size(); i++) {
if(atoms[i]->name == name1) {
return atoms[i]->find(name2, name3, name4);
for(AtomList::ConstIterator it = atoms.begin(); it != atoms.end(); ++it) {
if((*it)->name == name1) {
return (*it)->find(name2, name3, name4);
}
}
return 0;
@@ -181,9 +184,9 @@ MP4::AtomList
MP4::Atoms::path(const char *name1, const char *name2, const char *name3, const char *name4)
{
MP4::AtomList path;
for(unsigned int i = 0; i < atoms.size(); i++) {
if(atoms[i]->name == name1) {
if(!atoms[i]->path(path, name2, name3, name4)) {
for(AtomList::ConstIterator it = atoms.begin(); it != atoms.end(); ++it) {
if((*it)->name == name1) {
if(!(*it)->path(path, name2, name3, name4)) {
path.clear();
}
return path;
@@ -191,4 +194,3 @@ MP4::Atoms::path(const char *name1, const char *name2, const char *name3, const
}
return path;
}

View File

@@ -59,7 +59,7 @@ namespace TagLib {
TypeDateTime = 17, // in UTC, counting seconds since midnight, January 1, 1904; 32 or 64-bits
TypeGenred = 18, // a list of enumerated values
TypeInteger = 21, // a signed big-endian integer with length one of { 1,2,3,4,8 } bytes
TypeRIAAPA = 24, // RIAA parental advisory; { -1=no, 1=yes, 0=unspecified }, 8-bit ingteger
TypeRIAAPA = 24, // RIAA parental advisory; { -1=no, 1=yes, 0=unspecified }, 8-bit integer
TypeUPC = 25, // Universal Product Code, in text UTF-8 format (valid as an ID)
TypeBMP = 27, // Windows bitmap image
TypeUndefined = 255 // undefined
@@ -77,29 +77,29 @@ namespace TagLib {
class Atom
{
public:
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 offset;
long length;
TagLib::ByteVector name;
AtomList children;
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 offset;
long length;
TagLib::ByteVector name;
AtomList children;
private:
static const int numContainers = 11;
static const char *containers[11];
static const int numContainers = 11;
static const char *containers[11];
};
//! Root-level atoms
class Atoms
{
public:
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);
AtomList atoms;
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);
AtomList atoms;
};
}

View File

@@ -33,20 +33,27 @@ using namespace TagLib;
class MP4::CoverArt::CoverArtPrivate : public RefCounter
{
public:
CoverArtPrivate() : RefCounter(), format(MP4::CoverArt::JPEG) {}
CoverArtPrivate() :
RefCounter(),
format(MP4::CoverArt::JPEG) {}
Format format;
ByteVector data;
};
MP4::CoverArt::CoverArt(Format format, const ByteVector &data)
////////////////////////////////////////////////////////////////////////////////
// public members
////////////////////////////////////////////////////////////////////////////////
MP4::CoverArt::CoverArt(Format format, const ByteVector &data) :
d(new CoverArtPrivate())
{
d = new CoverArtPrivate;
d->format = format;
d->data = data;
}
MP4::CoverArt::CoverArt(const CoverArt &item) : d(item.d)
MP4::CoverArt::CoverArt(const CoverArt &item) :
d(item.d)
{
d->ref();
}
@@ -54,14 +61,18 @@ MP4::CoverArt::CoverArt(const CoverArt &item) : d(item.d)
MP4::CoverArt &
MP4::CoverArt::operator=(const CoverArt &item)
{
if(d->deref()) {
delete d;
}
d = item.d;
d->ref();
CoverArt(item).swap(*this);
return *this;
}
void
MP4::CoverArt::swap(CoverArt &item)
{
using std::swap;
swap(d, item.d);
}
MP4::CoverArt::~CoverArt()
{
if(d->deref()) {
@@ -80,4 +91,3 @@ MP4::CoverArt::data() const
{
return d->data;
}

View File

@@ -53,8 +53,17 @@ namespace TagLib {
~CoverArt();
CoverArt(const CoverArt &item);
/*!
* Copies the contents of \a item into this CoverArt.
*/
CoverArt &operator=(const CoverArt &item);
/*!
* Exchanges the content of the CoverArt by the content of \a item.
*/
void swap(CoverArt &item);
//! Format of the image
Format format() const;

View File

@@ -32,48 +32,57 @@
using namespace TagLib;
namespace
{
bool checkValid(const MP4::AtomList &list)
{
for(MP4::AtomList::ConstIterator it = list.begin(); it != list.end(); ++it) {
if((*it)->length == 0)
return false;
if(!checkValid((*it)->children))
return false;
}
return true;
}
}
class MP4::File::FilePrivate
{
public:
FilePrivate() : tag(0), atoms(0), properties(0)
{
}
FilePrivate() :
tag(0),
atoms(0),
properties(0) {}
~FilePrivate()
{
if(atoms) {
delete atoms;
atoms = 0;
}
if(tag) {
delete tag;
tag = 0;
}
if(properties) {
delete properties;
properties = 0;
}
delete atoms;
delete tag;
delete properties;
}
MP4::Tag *tag;
MP4::Atoms *atoms;
MP4::Tag *tag;
MP4::Atoms *atoms;
MP4::Properties *properties;
};
MP4::File::File(FileName file, bool readProperties, AudioProperties::ReadStyle audioPropertiesStyle)
: TagLib::File(file)
MP4::File::File(FileName file, bool readProperties, AudioProperties::ReadStyle) :
TagLib::File(file),
d(new FilePrivate())
{
d = new FilePrivate;
if(isOpen())
read(readProperties, audioPropertiesStyle);
read(readProperties);
}
MP4::File::File(IOStream *stream, bool readProperties, AudioProperties::ReadStyle audioPropertiesStyle)
: TagLib::File(stream)
MP4::File::File(IOStream *stream, bool readProperties, AudioProperties::ReadStyle) :
TagLib::File(stream),
d(new FilePrivate())
{
d = new FilePrivate;
if(isOpen())
read(readProperties, audioPropertiesStyle);
read(readProperties);
}
MP4::File::~File()
@@ -108,40 +117,27 @@ MP4::File::audioProperties() const
return d->properties;
}
bool
MP4::File::checkValid(const MP4::AtomList &list)
{
for(uint i = 0; i < list.size(); i++) {
if(list[i]->length == 0)
return false;
if(!checkValid(list[i]->children))
return false;
}
return true;
}
void
MP4::File::read(bool readProperties, Properties::ReadStyle audioPropertiesStyle)
MP4::File::read(bool readProperties)
{
if(!isValid())
return;
d->atoms = new Atoms(this);
if (!checkValid(d->atoms->atoms)) {
if(!checkValid(d->atoms->atoms)) {
setValid(false);
return;
}
// must have a moov atom, otherwise consider it invalid
MP4::Atom *moov = d->atoms->find("moov");
if(!moov) {
if(!d->atoms->find("moov")) {
setValid(false);
return;
}
d->tag = new Tag(this, d->atoms);
if(readProperties) {
d->properties = new Properties(this, d->atoms, audioPropertiesStyle);
d->properties = new Properties(this, d->atoms);
}
}
@@ -161,3 +157,8 @@ MP4::File::save()
return d->tag->save();
}
bool
MP4::File::hasMP4Tag() const
{
return (d->atoms->find("moov", "udta", "meta", "ilst") != 0);
}

View File

@@ -54,7 +54,7 @@ namespace TagLib {
*
* \note In the current implementation, \a propertiesStyle is ignored.
*/
File(FileName file, bool readProperties = true,
File(FileName file, bool readProperties = true,
Properties::ReadStyle audioPropertiesStyle = Properties::Average);
/*!
@@ -66,7 +66,7 @@ namespace TagLib {
*
* \note In the current implementation, \a propertiesStyle is ignored.
*/
File(IOStream *stream, bool readProperties = true,
File(IOStream *stream, bool readProperties = true,
Properties::ReadStyle audioPropertiesStyle = Properties::Average);
/*!
@@ -114,10 +114,14 @@ namespace TagLib {
*/
bool save();
private:
/*!
* Returns whether or not the file on disk actually has an MP4 tag, or the
* file has a Metadata Item List (ilst) atom.
*/
bool hasMP4Tag() const;
void read(bool readProperties, Properties::ReadStyle audioPropertiesStyle);
bool checkValid(const MP4::AtomList &list);
private:
void read(bool readProperties);
class FilePrivate;
FilePrivate *d;

View File

@@ -33,7 +33,10 @@ using namespace TagLib;
class MP4::Item::ItemPrivate : public RefCounter
{
public:
ItemPrivate() : RefCounter(), valid(true), atomDataType(TypeUndefined) {}
ItemPrivate() :
RefCounter(),
valid(true),
atomDataType(TypeUndefined) {}
bool valid;
AtomDataType atomDataType;
@@ -41,8 +44,8 @@ public:
bool m_bool;
int m_int;
IntPair m_intPair;
uchar m_byte;
uint m_uint;
unsigned char m_byte;
unsigned int m_uint;
long long m_longlong;
};
StringList m_stringList;
@@ -50,13 +53,14 @@ public:
MP4::CoverArtList m_coverArtList;
};
MP4::Item::Item()
MP4::Item::Item() :
d(new ItemPrivate())
{
d = new ItemPrivate;
d->valid = false;
}
MP4::Item::Item(const Item &item) : d(item.d)
MP4::Item::Item(const Item &item) :
d(item.d)
{
d->ref();
}
@@ -64,73 +68,76 @@ MP4::Item::Item(const Item &item) : d(item.d)
MP4::Item &
MP4::Item::operator=(const Item &item)
{
if(d->deref()) {
delete d;
}
d = item.d;
d->ref();
Item(item).swap(*this);
return *this;
}
void
MP4::Item::swap(Item &item)
{
using std::swap;
swap(d, item.d);
}
MP4::Item::~Item()
{
if(d->deref()) {
if(d->deref())
delete d;
}
}
MP4::Item::Item(bool value)
MP4::Item::Item(bool value) :
d(new ItemPrivate())
{
d = new ItemPrivate;
d->m_bool = value;
}
MP4::Item::Item(int value)
MP4::Item::Item(int value) :
d(new ItemPrivate())
{
d = new ItemPrivate;
d->m_int = value;
}
MP4::Item::Item(uchar value)
MP4::Item::Item(unsigned char value) :
d(new ItemPrivate())
{
d = new ItemPrivate;
d->m_byte = value;
}
MP4::Item::Item(uint value)
MP4::Item::Item(unsigned int value) :
d(new ItemPrivate())
{
d = new ItemPrivate;
d->m_uint = value;
}
MP4::Item::Item(long long value)
MP4::Item::Item(long long value) :
d(new ItemPrivate())
{
d = new ItemPrivate;
d->m_longlong = value;
}
MP4::Item::Item(int value1, int value2)
MP4::Item::Item(int value1, int value2) :
d(new ItemPrivate())
{
d = new ItemPrivate;
d->m_intPair.first = value1;
d->m_intPair.second = value2;
}
MP4::Item::Item(const ByteVectorList &value)
MP4::Item::Item(const ByteVectorList &value) :
d(new ItemPrivate())
{
d = new ItemPrivate;
d->m_byteVectorList = value;
}
MP4::Item::Item(const StringList &value)
MP4::Item::Item(const StringList &value) :
d(new ItemPrivate())
{
d = new ItemPrivate;
d->m_stringList = value;
}
MP4::Item::Item(const MP4::CoverArtList &value)
MP4::Item::Item(const MP4::CoverArtList &value) :
d(new ItemPrivate())
{
d = new ItemPrivate;
d->m_coverArtList = value;
}
@@ -156,13 +163,13 @@ MP4::Item::toInt() const
return d->m_int;
}
uchar
unsigned char
MP4::Item::toByte() const
{
return d->m_byte;
}
TagLib::uint
unsigned int
MP4::Item::toUInt() const
{
return d->m_uint;
@@ -203,4 +210,3 @@ MP4::Item::isValid() const
{
return d->valid;
}

View File

@@ -43,12 +43,22 @@ namespace TagLib {
Item();
Item(const Item &item);
/*!
* Copies the contents of \a item into this Item.
*/
Item &operator=(const Item &item);
/*!
* Exchanges the content of the Item by the content of \a item.
*/
void swap(Item &item);
~Item();
Item(int value);
Item(uchar value);
Item(uint value);
Item(unsigned char value);
Item(unsigned int value);
Item(long long value);
Item(bool value);
Item(int first, int second);
@@ -60,8 +70,8 @@ namespace TagLib {
AtomDataType atomDataType() const;
int toInt() const;
uchar toByte() const;
uint toUInt() const;
unsigned char toByte() const;
unsigned int toUInt() const;
long long toLongLong() const;
bool toBool() const;
IntPair toIntPair() const;

View File

@@ -34,7 +34,14 @@ using namespace TagLib;
class MP4::Properties::PropertiesPrivate
{
public:
PropertiesPrivate() : length(0), bitrate(0), sampleRate(0), channels(0), bitsPerSample(0), encrypted(false), codec(MP4::Properties::Unknown) {}
PropertiesPrivate() :
length(0),
bitrate(0),
sampleRate(0),
channels(0),
bitsPerSample(0),
encrypted(false),
codec(MP4::Properties::Unknown) {}
int length;
int bitrate;
@@ -45,110 +52,15 @@ public:
Codec codec;
};
MP4::Properties::Properties(File *file, MP4::Atoms *atoms, ReadStyle style)
: AudioProperties(style)
////////////////////////////////////////////////////////////////////////////////
// public members
////////////////////////////////////////////////////////////////////////////////
MP4::Properties::Properties(File *file, MP4::Atoms *atoms, ReadStyle style) :
AudioProperties(style),
d(new PropertiesPrivate())
{
d = new PropertiesPrivate;
MP4::Atom *moov = atoms->find("moov");
if(!moov) {
debug("MP4: Atom 'moov' not found");
return;
}
MP4::Atom *trak = 0;
ByteVector data;
MP4::AtomList trakList = moov->findall("trak");
for (unsigned int i = 0; i < trakList.size(); i++) {
trak = trakList[i];
MP4::Atom *hdlr = trak->find("mdia", "hdlr");
if(!hdlr) {
debug("MP4: Atom 'trak.mdia.hdlr' not found");
return;
}
file->seek(hdlr->offset);
data = file->readBlock(hdlr->length);
if(data.mid(16, 4) == "soun") {
break;
}
trak = 0;
}
if (!trak) {
debug("MP4: No audio tracks");
return;
}
MP4::Atom *mdhd = trak->find("mdia", "mdhd");
if(!mdhd) {
debug("MP4: Atom 'trak.mdia.mdhd' not found");
return;
}
file->seek(mdhd->offset);
data = file->readBlock(mdhd->length);
uint version = data[8];
if(version == 1) {
if (data.size() < 36 + 8) {
debug("MP4: Atom 'trak.mdia.mdhd' is smaller than expected");
return;
}
const long long unit = data.toLongLong(28U);
const long long length = data.toLongLong(36U);
d->length = unit ? int(length / unit) : 0;
}
else {
if (data.size() < 24 + 4) {
debug("MP4: Atom 'trak.mdia.mdhd' is smaller than expected");
return;
}
const unsigned int unit = data.toUInt(20U);
const unsigned int length = data.toUInt(24U);
d->length = unit ? length / unit : 0;
}
MP4::Atom *atom = trak->find("mdia", "minf", "stbl", "stsd");
if(!atom) {
return;
}
file->seek(atom->offset);
data = file->readBlock(atom->length);
if(data.mid(20, 4) == "mp4a") {
d->codec = AAC;
d->channels = data.toShort(40U);
d->bitsPerSample = data.toShort(42U);
d->sampleRate = data.toUInt(46U);
if(data.mid(56, 4) == "esds" && data[64] == 0x03) {
uint pos = 65;
if(data.mid(pos, 3) == "\x80\x80\x80") {
pos += 3;
}
pos += 4;
if(data[pos] == 0x04) {
pos += 1;
if(data.mid(pos, 3) == "\x80\x80\x80") {
pos += 3;
}
pos += 10;
d->bitrate = (data.toUInt(pos) + 500) / 1000;
}
}
}
else if (data.mid(20, 4) == "alac") {
if (atom->length == 88 && data.mid(56, 4) == "alac") {
d->codec = ALAC;
d->bitsPerSample = data.at(69);
d->channels = data.at(73);
d->bitrate = data.toUInt(80U) / 1000;
d->sampleRate = data.toUInt(84U);
}
}
MP4::Atom *drms = atom->find("drms");
if(drms) {
d->encrypted = true;
}
read(file, atoms);
}
MP4::Properties::~Properties()
@@ -170,6 +82,18 @@ MP4::Properties::sampleRate() const
int
MP4::Properties::length() const
{
return lengthInSeconds();
}
int
MP4::Properties::lengthInSeconds() const
{
return d->length / 1000;
}
int
MP4::Properties::lengthInMilliseconds() const
{
return d->length;
}
@@ -192,8 +116,119 @@ MP4::Properties::isEncrypted() const
return d->encrypted;
}
MP4::Properties::Codec MP4::Properties::codec() const
MP4::Properties::Codec
MP4::Properties::codec() const
{
return d->codec;
}
////////////////////////////////////////////////////////////////////////////////
// private members
////////////////////////////////////////////////////////////////////////////////
void
MP4::Properties::read(File *file, Atoms *atoms)
{
MP4::Atom *moov = atoms->find("moov");
if(!moov) {
debug("MP4: Atom 'moov' not found");
return;
}
MP4::Atom *trak = 0;
ByteVector data;
const MP4::AtomList trakList = moov->findall("trak");
for(MP4::AtomList::ConstIterator it = trakList.begin(); it != trakList.end(); ++it) {
trak = *it;
MP4::Atom *hdlr = trak->find("mdia", "hdlr");
if(!hdlr) {
debug("MP4: Atom 'trak.mdia.hdlr' not found");
return;
}
file->seek(hdlr->offset);
data = file->readBlock(hdlr->length);
if(data.containsAt("soun", 16)) {
break;
}
trak = 0;
}
if(!trak) {
debug("MP4: No audio tracks");
return;
}
MP4::Atom *mdhd = trak->find("mdia", "mdhd");
if(!mdhd) {
debug("MP4: Atom 'trak.mdia.mdhd' not found");
return;
}
file->seek(mdhd->offset);
data = file->readBlock(mdhd->length);
const unsigned int version = data[8];
long long unit;
long long length;
if(version == 1) {
if(data.size() < 36 + 8) {
debug("MP4: Atom 'trak.mdia.mdhd' is smaller than expected");
return;
}
unit = data.toLongLong(28U);
length = data.toLongLong(36U);
}
else {
if(data.size() < 24 + 4) {
debug("MP4: Atom 'trak.mdia.mdhd' is smaller than expected");
return;
}
unit = data.toUInt(20U);
length = data.toUInt(24U);
}
if(unit > 0 && length > 0)
d->length = static_cast<int>(length * 1000.0 / unit + 0.5);
MP4::Atom *atom = trak->find("mdia", "minf", "stbl", "stsd");
if(!atom) {
return;
}
file->seek(atom->offset);
data = file->readBlock(atom->length);
if(data.containsAt("mp4a", 20)) {
d->codec = AAC;
d->channels = data.toShort(40U);
d->bitsPerSample = data.toShort(42U);
d->sampleRate = data.toUInt(46U);
if(data.containsAt("esds", 56) && data[64] == 0x03) {
unsigned int pos = 65;
if(data.containsAt("\x80\x80\x80", pos)) {
pos += 3;
}
pos += 4;
if(data[pos] == 0x04) {
pos += 1;
if(data.containsAt("\x80\x80\x80", pos)) {
pos += 3;
}
pos += 10;
d->bitrate = static_cast<int>((data.toUInt(pos) + 500) / 1000.0 + 0.5);
}
}
}
else if(data.containsAt("alac", 20)) {
if(atom->length == 88 && data.containsAt("alac", 56)) {
d->codec = ALAC;
d->bitsPerSample = data.at(69);
d->channels = data.at(73);
d->bitrate = static_cast<int>(data.toUInt(80U) / 1000.0 + 0.5);
d->sampleRate = data.toUInt(84U);
}
}
MP4::Atom *drms = atom->find("drms");
if(drms) {
d->encrypted = true;
}
}

View File

@@ -49,17 +49,66 @@ namespace TagLib {
Properties(File *file, Atoms *atoms, ReadStyle style = Average);
virtual ~Properties();
/*!
* Returns the length of the file in seconds. The length is rounded down to
* the nearest whole second.
*
* \note This method is just an alias of lengthInSeconds().
*
* \deprecated
*/
virtual int length() const;
/*!
* Returns the length of the file in seconds. The length is rounded down to
* the nearest whole second.
*
* \see lengthInMilliseconds()
*/
// BIC: make virtual
int lengthInSeconds() const;
/*!
* Returns the length of the file in milliseconds.
*
* \see lengthInSeconds()
*/
// BIC: make virtual
int lengthInMilliseconds() const;
/*!
* Returns the average bit rate of the file in kb/s.
*/
virtual int bitrate() const;
/*!
* Returns the sample rate in Hz.
*/
virtual int sampleRate() const;
/*!
* Returns the number of audio channels.
*/
virtual int channels() const;
/*!
* Returns the number of bits per audio sample.
*/
virtual int bitsPerSample() const;
/*!
* Returns whether or not the file is encrypted.
*/
bool isEncrypted() const;
//! Audio codec used in the MP4 file
/*!
* Returns the codec used in the file.
*/
Codec codec() const;
private:
void read(File *file, Atoms *atoms);
class PropertiesPrivate;
PropertiesPrivate *d;
};

View File

@@ -35,21 +35,23 @@ using namespace TagLib;
class MP4::Tag::TagPrivate
{
public:
TagPrivate() : file(0), atoms(0) {}
~TagPrivate() {}
TagPrivate() :
file(0),
atoms(0) {}
TagLib::File *file;
Atoms *atoms;
ItemListMap items;
ItemMap items;
};
MP4::Tag::Tag()
MP4::Tag::Tag() :
d(new TagPrivate())
{
d = new TagPrivate;
}
MP4::Tag::Tag(TagLib::File *file, MP4::Atoms *atoms)
MP4::Tag::Tag(TagLib::File *file, MP4::Atoms *atoms) :
d(new TagPrivate())
{
d = new TagPrivate;
d->file = file;
d->atoms = atoms;
@@ -59,40 +61,40 @@ MP4::Tag::Tag(TagLib::File *file, MP4::Atoms *atoms)
return;
}
for(unsigned int i = 0; i < ilst->children.size(); i++) {
MP4::Atom *atom = ilst->children[i];
for(AtomList::ConstIterator it = ilst->children.begin(); it != ilst->children.end(); ++it) {
MP4::Atom *atom = *it;
file->seek(atom->offset + 8);
if(atom->name == "----") {
parseFreeForm(atom, file);
parseFreeForm(atom);
}
else if(atom->name == "trkn" || atom->name == "disk") {
parseIntPair(atom, file);
parseIntPair(atom);
}
else if(atom->name == "cpil" || atom->name == "pgap" || atom->name == "pcst" ||
atom->name == "hdvd") {
parseBool(atom, file);
parseBool(atom);
}
else if(atom->name == "tmpo") {
parseInt(atom, file);
parseInt(atom);
}
else if(atom->name == "tvsn" || atom->name == "tves" || atom->name == "cnID" ||
atom->name == "sfID" || atom->name == "atID" || atom->name == "geID") {
parseUInt(atom, file);
parseUInt(atom);
}
else if(atom->name == "plID") {
parseLongLong(atom, file);
parseLongLong(atom);
}
else if(atom->name == "stik" || atom->name == "rtng" || atom->name == "akID") {
parseByte(atom, file);
parseByte(atom);
}
else if(atom->name == "gnre") {
parseGnre(atom, file);
parseGnre(atom);
}
else if(atom->name == "covr") {
parseCovr(atom, file);
parseCovr(atom);
}
else {
parseText(atom, file);
parseText(atom);
}
}
}
@@ -103,15 +105,20 @@ MP4::Tag::~Tag()
}
MP4::AtomDataList
MP4::Tag::parseData2(MP4::Atom *atom, TagLib::File *file, int expectedFlags, bool freeForm)
MP4::Tag::parseData2(const MP4::Atom *atom, int expectedFlags, bool freeForm)
{
AtomDataList result;
ByteVector data = file->readBlock(atom->length - 8);
ByteVector data = d->file->readBlock(atom->length - 8);
int i = 0;
unsigned int pos = 0;
while(pos < data.size()) {
const int length = static_cast<int>(data.toUInt(pos));
ByteVector name = data.mid(pos + 4, 4);
if(length < 12) {
debug("MP4: Too short atom");
return result;
}
const ByteVector name = data.mid(pos + 4, 4);
const int flags = static_cast<int>(data.toUInt(pos + 8));
if(freeForm && i < 2) {
if(i == 0 && name != "mean") {
@@ -140,57 +147,57 @@ MP4::Tag::parseData2(MP4::Atom *atom, TagLib::File *file, int expectedFlags, boo
}
ByteVectorList
MP4::Tag::parseData(MP4::Atom *atom, TagLib::File *file, int expectedFlags, bool freeForm)
MP4::Tag::parseData(const MP4::Atom *atom, int expectedFlags, bool freeForm)
{
AtomDataList data = parseData2(atom, file, expectedFlags, freeForm);
AtomDataList data = parseData2(atom, expectedFlags, freeForm);
ByteVectorList result;
for(uint i = 0; i < data.size(); i++) {
result.append(data[i].data);
for(AtomDataList::ConstIterator it = data.begin(); it != data.end(); ++it) {
result.append(it->data);
}
return result;
}
void
MP4::Tag::parseInt(MP4::Atom *atom, TagLib::File *file)
MP4::Tag::parseInt(const MP4::Atom *atom)
{
ByteVectorList data = parseData(atom, file);
if(data.size()) {
ByteVectorList data = parseData(atom);
if(!data.isEmpty()) {
addItem(atom->name, (int)data[0].toShort());
}
}
void
MP4::Tag::parseUInt(MP4::Atom *atom, TagLib::File *file)
MP4::Tag::parseUInt(const MP4::Atom *atom)
{
ByteVectorList data = parseData(atom, file);
if(data.size()) {
ByteVectorList data = parseData(atom);
if(!data.isEmpty()) {
addItem(atom->name, data[0].toUInt());
}
}
void
MP4::Tag::parseLongLong(MP4::Atom *atom, TagLib::File *file)
MP4::Tag::parseLongLong(const MP4::Atom *atom)
{
ByteVectorList data = parseData(atom, file);
if(data.size()) {
ByteVectorList data = parseData(atom);
if(!data.isEmpty()) {
addItem(atom->name, data[0].toLongLong());
}
}
void
MP4::Tag::parseByte(MP4::Atom *atom, TagLib::File *file)
MP4::Tag::parseByte(const MP4::Atom *atom)
{
ByteVectorList data = parseData(atom, file);
if(data.size()) {
addItem(atom->name, (uchar)data[0].at(0));
ByteVectorList data = parseData(atom);
if(!data.isEmpty()) {
addItem(atom->name, static_cast<unsigned char>(data[0].at(0)));
}
}
void
MP4::Tag::parseGnre(MP4::Atom *atom, TagLib::File *file)
MP4::Tag::parseGnre(const MP4::Atom *atom)
{
ByteVectorList data = parseData(atom, file);
if(data.size()) {
ByteVectorList data = parseData(atom);
if(!data.isEmpty()) {
int idx = (int)data[0].toShort();
if(idx > 0) {
addItem("\251gen", StringList(ID3v1::genre(idx - 1)));
@@ -199,10 +206,10 @@ MP4::Tag::parseGnre(MP4::Atom *atom, TagLib::File *file)
}
void
MP4::Tag::parseIntPair(MP4::Atom *atom, TagLib::File *file)
MP4::Tag::parseIntPair(const MP4::Atom *atom)
{
ByteVectorList data = parseData(atom, file);
if(data.size()) {
ByteVectorList data = parseData(atom);
if(!data.isEmpty()) {
const int a = data[0].toShort(2U);
const int b = data[0].toShort(4U);
addItem(atom->name, MP4::Item(a, b));
@@ -210,45 +217,52 @@ MP4::Tag::parseIntPair(MP4::Atom *atom, TagLib::File *file)
}
void
MP4::Tag::parseBool(MP4::Atom *atom, TagLib::File *file)
MP4::Tag::parseBool(const MP4::Atom *atom)
{
ByteVectorList data = parseData(atom, file);
if(data.size()) {
ByteVectorList data = parseData(atom);
if(!data.isEmpty()) {
bool value = data[0].size() ? data[0][0] != '\0' : false;
addItem(atom->name, value);
}
}
void
MP4::Tag::parseText(MP4::Atom *atom, TagLib::File *file, int expectedFlags)
MP4::Tag::parseText(const MP4::Atom *atom, int expectedFlags)
{
ByteVectorList data = parseData(atom, file, expectedFlags);
if(data.size()) {
ByteVectorList data = parseData(atom, expectedFlags);
if(!data.isEmpty()) {
StringList value;
for(unsigned int i = 0; i < data.size(); i++) {
value.append(String(data[i], String::UTF8));
for(ByteVectorList::ConstIterator it = data.begin(); it != data.end(); ++it) {
value.append(String(*it, String::UTF8));
}
addItem(atom->name, value);
}
}
void
MP4::Tag::parseFreeForm(MP4::Atom *atom, TagLib::File *file)
MP4::Tag::parseFreeForm(const MP4::Atom *atom)
{
AtomDataList data = parseData2(atom, file, -1, true);
AtomDataList data = parseData2(atom, -1, true);
if(data.size() > 2) {
String name = "----:" + String(data[0].data, String::UTF8) + ':' + String(data[1].data, String::UTF8);
AtomDataType type = data[2].type;
for(uint i = 2; i < data.size(); i++) {
if(data[i].type != type) {
AtomDataList::ConstIterator itBegin = data.begin();
String name = "----:";
name += String((itBegin++)->data, String::UTF8); // data[0].data
name += ':';
name += String((itBegin++)->data, String::UTF8); // data[1].data
AtomDataType type = itBegin->type; // data[2].type
for(AtomDataList::ConstIterator it = itBegin; it != data.end(); ++it) {
if(it->type != type) {
debug("MP4: We currently don't support values with multiple types");
break;
}
}
if(type == TypeUTF8) {
StringList value;
for(uint i = 2; i < data.size(); i++) {
value.append(String(data[i].data, String::UTF8));
for(AtomDataList::ConstIterator it = itBegin; it != data.end(); ++it) {
value.append(String(it->data, String::UTF8));
}
Item item(value);
item.setAtomDataType(type);
@@ -256,8 +270,8 @@ MP4::Tag::parseFreeForm(MP4::Atom *atom, TagLib::File *file)
}
else {
ByteVectorList value;
for(uint i = 2; i < data.size(); i++) {
value.append(data[i].data);
for(AtomDataList::ConstIterator it = itBegin; it != data.end(); ++it) {
value.append(it->data);
}
Item item(value);
item.setAtomDataType(type);
@@ -267,20 +281,26 @@ MP4::Tag::parseFreeForm(MP4::Atom *atom, TagLib::File *file)
}
void
MP4::Tag::parseCovr(MP4::Atom *atom, TagLib::File *file)
MP4::Tag::parseCovr(const MP4::Atom *atom)
{
MP4::CoverArtList value;
ByteVector data = file->readBlock(atom->length - 8);
ByteVector data = d->file->readBlock(atom->length - 8);
unsigned int pos = 0;
while(pos < data.size()) {
const int length = static_cast<int>(data.toUInt(pos));
ByteVector name = data.mid(pos + 4, 4);
if(length < 12) {
debug("MP4: Too short atom");
break;;
}
const ByteVector name = data.mid(pos + 4, 4);
const int flags = static_cast<int>(data.toUInt(pos + 8));
if(name != "data") {
debug("MP4: Unexpected atom \"" + name + "\", expecting \"data\"");
break;
}
if(flags == TypeJPEG || flags == TypePNG || flags == TypeBMP || flags == TypeGIF || flags == TypeImplicit) {
if(flags == TypeJPEG || flags == TypePNG || flags == TypeBMP ||
flags == TypeGIF || flags == TypeImplicit) {
value.append(MP4::CoverArt(MP4::CoverArt::Format(flags),
data.mid(pos + 16, length - 16)));
}
@@ -289,37 +309,37 @@ MP4::Tag::parseCovr(MP4::Atom *atom, TagLib::File *file)
}
pos += length;
}
if(value.size() > 0)
if(!value.isEmpty())
addItem(atom->name, value);
}
ByteVector
MP4::Tag::padIlst(const ByteVector &data, int length)
MP4::Tag::padIlst(const ByteVector &data, int length) const
{
if (length == -1) {
if(length == -1) {
length = ((data.size() + 1023) & ~1023) - data.size();
}
return renderAtom("free", ByteVector(length, '\1'));
}
ByteVector
MP4::Tag::renderAtom(const ByteVector &name, const ByteVector &data)
MP4::Tag::renderAtom(const ByteVector &name, const ByteVector &data) const
{
return ByteVector::fromUInt(data.size() + 8) + name + data;
}
ByteVector
MP4::Tag::renderData(const ByteVector &name, int flags, const ByteVectorList &data)
MP4::Tag::renderData(const ByteVector &name, int flags, const ByteVectorList &data) const
{
ByteVector result;
for(unsigned int i = 0; i < data.size(); i++) {
result.append(renderAtom("data", ByteVector::fromUInt(flags) + ByteVector(4, '\0') + data[i]));
for(ByteVectorList::ConstIterator it = data.begin(); it != data.end(); ++it) {
result.append(renderAtom("data", ByteVector::fromUInt(flags) + ByteVector(4, '\0') + *it));
}
return renderAtom(name, result);
}
ByteVector
MP4::Tag::renderBool(const ByteVector &name, MP4::Item &item)
MP4::Tag::renderBool(const ByteVector &name, const MP4::Item &item) const
{
ByteVectorList data;
data.append(ByteVector(1, item.toBool() ? '\1' : '\0'));
@@ -327,7 +347,7 @@ MP4::Tag::renderBool(const ByteVector &name, MP4::Item &item)
}
ByteVector
MP4::Tag::renderInt(const ByteVector &name, MP4::Item &item)
MP4::Tag::renderInt(const ByteVector &name, const MP4::Item &item) const
{
ByteVectorList data;
data.append(ByteVector::fromShort(item.toInt()));
@@ -335,7 +355,7 @@ MP4::Tag::renderInt(const ByteVector &name, MP4::Item &item)
}
ByteVector
MP4::Tag::renderUInt(const ByteVector &name, MP4::Item &item)
MP4::Tag::renderUInt(const ByteVector &name, const MP4::Item &item) const
{
ByteVectorList data;
data.append(ByteVector::fromUInt(item.toUInt()));
@@ -343,7 +363,7 @@ MP4::Tag::renderUInt(const ByteVector &name, MP4::Item &item)
}
ByteVector
MP4::Tag::renderLongLong(const ByteVector &name, MP4::Item &item)
MP4::Tag::renderLongLong(const ByteVector &name, const MP4::Item &item) const
{
ByteVectorList data;
data.append(ByteVector::fromLongLong(item.toLongLong()));
@@ -351,7 +371,7 @@ MP4::Tag::renderLongLong(const ByteVector &name, MP4::Item &item)
}
ByteVector
MP4::Tag::renderByte(const ByteVector &name, MP4::Item &item)
MP4::Tag::renderByte(const ByteVector &name, const MP4::Item &item) const
{
ByteVectorList data;
data.append(ByteVector(1, item.toByte()));
@@ -359,7 +379,7 @@ MP4::Tag::renderByte(const ByteVector &name, MP4::Item &item)
}
ByteVector
MP4::Tag::renderIntPair(const ByteVector &name, MP4::Item &item)
MP4::Tag::renderIntPair(const ByteVector &name, const MP4::Item &item) const
{
ByteVectorList data;
data.append(ByteVector(2, '\0') +
@@ -370,7 +390,7 @@ MP4::Tag::renderIntPair(const ByteVector &name, MP4::Item &item)
}
ByteVector
MP4::Tag::renderIntPairNoTrailing(const ByteVector &name, MP4::Item &item)
MP4::Tag::renderIntPairNoTrailing(const ByteVector &name, const MP4::Item &item) const
{
ByteVectorList data;
data.append(ByteVector(2, '\0') +
@@ -380,35 +400,35 @@ MP4::Tag::renderIntPairNoTrailing(const ByteVector &name, MP4::Item &item)
}
ByteVector
MP4::Tag::renderText(const ByteVector &name, MP4::Item &item, int flags)
MP4::Tag::renderText(const ByteVector &name, const MP4::Item &item, int flags) const
{
ByteVectorList data;
StringList value = item.toStringList();
for(unsigned int i = 0; i < value.size(); i++) {
data.append(value[i].data(String::UTF8));
for(StringList::ConstIterator it = value.begin(); it != value.end(); ++it) {
data.append(it->data(String::UTF8));
}
return renderData(name, flags, data);
}
ByteVector
MP4::Tag::renderCovr(const ByteVector &name, MP4::Item &item)
MP4::Tag::renderCovr(const ByteVector &name, const MP4::Item &item) const
{
ByteVector data;
MP4::CoverArtList value = item.toCoverArtList();
for(unsigned int i = 0; i < value.size(); i++) {
data.append(renderAtom("data", ByteVector::fromUInt(value[i].format()) +
ByteVector(4, '\0') + value[i].data()));
for(MP4::CoverArtList::ConstIterator it = value.begin(); it != value.end(); ++it) {
data.append(renderAtom("data", ByteVector::fromUInt(it->format()) +
ByteVector(4, '\0') + it->data()));
}
return renderAtom(name, data);
}
ByteVector
MP4::Tag::renderFreeForm(const String &name, MP4::Item &item)
MP4::Tag::renderFreeForm(const String &name, const MP4::Item &item) const
{
StringList header = StringList::split(name, ":");
if (header.size() != 3) {
if(header.size() != 3) {
debug("MP4: Invalid free-form item name \"" + name + "\"");
return ByteVector::null;
return ByteVector();
}
ByteVector data;
data.append(renderAtom("mean", ByteVector::fromUInt(0) + header[1].data(String::UTF8)));
@@ -424,14 +444,14 @@ MP4::Tag::renderFreeForm(const String &name, MP4::Item &item)
}
if(type == TypeUTF8) {
StringList value = item.toStringList();
for(unsigned int i = 0; i < value.size(); i++) {
data.append(renderAtom("data", ByteVector::fromUInt(type) + ByteVector(4, '\0') + value[i].data(String::UTF8)));
for(StringList::ConstIterator it = value.begin(); it != value.end(); ++it) {
data.append(renderAtom("data", ByteVector::fromUInt(type) + ByteVector(4, '\0') + it->data(String::UTF8)));
}
}
else {
ByteVectorList value = item.toByteVectorList();
for(unsigned int i = 0; i < value.size(); i++) {
data.append(renderAtom("data", ByteVector::fromUInt(type) + ByteVector(4, '\0') + value[i]));
for(ByteVectorList::ConstIterator it = value.begin(); it != value.end(); ++it) {
data.append(renderAtom("data", ByteVector::fromUInt(type) + ByteVector(4, '\0') + *it));
}
}
return renderAtom("----", data);
@@ -441,38 +461,38 @@ bool
MP4::Tag::save()
{
ByteVector data;
for(MP4::ItemListMap::Iterator i = d->items.begin(); i != d->items.end(); i++) {
const String name = i->first;
for(MP4::ItemMap::ConstIterator it = d->items.begin(); it != d->items.end(); ++it) {
const String name = it->first;
if(name.startsWith("----")) {
data.append(renderFreeForm(name, i->second));
data.append(renderFreeForm(name, it->second));
}
else if(name == "trkn") {
data.append(renderIntPair(name.data(String::Latin1), i->second));
data.append(renderIntPair(name.data(String::Latin1), it->second));
}
else if(name == "disk") {
data.append(renderIntPairNoTrailing(name.data(String::Latin1), i->second));
data.append(renderIntPairNoTrailing(name.data(String::Latin1), it->second));
}
else if(name == "cpil" || name == "pgap" || name == "pcst" || name == "hdvd") {
data.append(renderBool(name.data(String::Latin1), i->second));
data.append(renderBool(name.data(String::Latin1), it->second));
}
else if(name == "tmpo") {
data.append(renderInt(name.data(String::Latin1), i->second));
data.append(renderInt(name.data(String::Latin1), it->second));
}
else if(name == "tvsn" || name == "tves" || name == "cnID" ||
name == "sfID" || name == "atID" || name == "geID") {
data.append(renderUInt(name.data(String::Latin1), i->second));
data.append(renderUInt(name.data(String::Latin1), it->second));
}
else if(name == "plID") {
data.append(renderLongLong(name.data(String::Latin1), i->second));
data.append(renderLongLong(name.data(String::Latin1), it->second));
}
else if(name == "stik" || name == "rtng" || name == "akID") {
data.append(renderByte(name.data(String::Latin1), i->second));
data.append(renderByte(name.data(String::Latin1), it->second));
}
else if(name == "covr") {
data.append(renderCovr(name.data(String::Latin1), i->second));
data.append(renderCovr(name.data(String::Latin1), it->second));
}
else if(name.size() == 4){
data.append(renderText(name.data(String::Latin1), i->second));
data.append(renderText(name.data(String::Latin1), it->second));
}
else {
debug("MP4: Unknown item name \"" + name + "\"");
@@ -492,22 +512,28 @@ MP4::Tag::save()
}
void
MP4::Tag::updateParents(AtomList &path, long delta, int ignore)
MP4::Tag::updateParents(const AtomList &path, long delta, int ignore)
{
for(unsigned int i = 0; i < path.size() - ignore; i++) {
d->file->seek(path[i]->offset);
if(static_cast<int>(path.size()) <= ignore)
return;
AtomList::ConstIterator itEnd = path.end();
std::advance(itEnd, 0 - ignore);
for(AtomList::ConstIterator it = path.begin(); it != itEnd; ++it) {
d->file->seek((*it)->offset);
long size = d->file->readBlock(4).toUInt();
// 64-bit
if (size == 1) {
d->file->seek(4, File::Current); // Skip name
long long longSize = d->file->readBlock(8).toLongLong();
// Seek the offset of the 64-bit size
d->file->seek(path[i]->offset + 8);
d->file->seek((*it)->offset + 8);
d->file->writeBlock(ByteVector::fromLongLong(longSize + delta));
}
// 32-bit
else {
d->file->seek(path[i]->offset);
d->file->seek((*it)->offset);
d->file->writeBlock(ByteVector::fromUInt(size + delta));
}
}
@@ -519,8 +545,8 @@ MP4::Tag::updateOffsets(long delta, long offset)
MP4::Atom *moov = d->atoms->find("moov");
if(moov) {
MP4::AtomList stco = moov->findall("stco", true);
for(unsigned int i = 0; i < stco.size(); i++) {
MP4::Atom *atom = stco[i];
for(MP4::AtomList::ConstIterator it = stco.begin(); it != stco.end(); ++it) {
MP4::Atom *atom = *it;
if(atom->offset > offset) {
atom->offset += delta;
}
@@ -528,7 +554,7 @@ MP4::Tag::updateOffsets(long delta, long offset)
ByteVector data = d->file->readBlock(atom->length - 12);
unsigned int count = data.toUInt();
d->file->seek(atom->offset + 16);
uint pos = 4;
unsigned int pos = 4;
while(count--) {
long o = static_cast<long>(data.toUInt(pos));
if(o > offset) {
@@ -540,8 +566,8 @@ MP4::Tag::updateOffsets(long delta, long offset)
}
MP4::AtomList co64 = moov->findall("co64", true);
for(unsigned int i = 0; i < co64.size(); i++) {
MP4::Atom *atom = co64[i];
for(MP4::AtomList::ConstIterator it = co64.begin(); it != co64.end(); ++it) {
MP4::Atom *atom = *it;
if(atom->offset > offset) {
atom->offset += delta;
}
@@ -549,7 +575,7 @@ MP4::Tag::updateOffsets(long delta, long offset)
ByteVector data = d->file->readBlock(atom->length - 12);
unsigned int count = data.toUInt();
d->file->seek(atom->offset + 16);
uint pos = 4;
unsigned int pos = 4;
while(count--) {
long long o = data.toLongLong(pos);
if(o > offset) {
@@ -564,8 +590,8 @@ MP4::Tag::updateOffsets(long delta, long offset)
MP4::Atom *moof = d->atoms->find("moof");
if(moof) {
MP4::AtomList tfhd = moof->findall("tfhd", true);
for(unsigned int i = 0; i < tfhd.size(); i++) {
MP4::Atom *atom = tfhd[i];
for(MP4::AtomList::ConstIterator it = tfhd.begin(); it != tfhd.end(); ++it) {
MP4::Atom *atom = *it;
if(atom->offset > offset) {
atom->offset += delta;
}
@@ -585,10 +611,11 @@ MP4::Tag::updateOffsets(long delta, long offset)
}
void
MP4::Tag::saveNew(ByteVector &data)
MP4::Tag::saveNew(ByteVector data)
{
data = renderAtom("meta", TagLib::ByteVector(4, '\0') +
renderAtom("hdlr", TagLib::ByteVector(8, '\0') + TagLib::ByteVector("mdirappl") + TagLib::ByteVector(9, '\0')) +
data = renderAtom("meta", ByteVector(4, '\0') +
renderAtom("hdlr", ByteVector(8, '\0') + ByteVector("mdirappl") +
ByteVector(9, '\0')) +
data + padIlst(data));
AtomList path = d->atoms->path("moov", "udta");
@@ -597,26 +624,33 @@ MP4::Tag::saveNew(ByteVector &data)
data = renderAtom("udta", data);
}
long offset = path[path.size() - 1]->offset + 8;
long offset = path.back()->offset + 8;
d->file->insert(data, offset, 0);
updateParents(path, data.size());
updateOffsets(data.size(), offset);
// Insert the newly created atoms into the tree to keep it up-to-date.
d->file->seek(offset);
path.back()->children.prepend(new Atom(d->file));
}
void
MP4::Tag::saveExisting(ByteVector &data, AtomList &path)
MP4::Tag::saveExisting(ByteVector data, const AtomList &path)
{
MP4::Atom *ilst = path[path.size() - 1];
AtomList::ConstIterator it = path.end();
MP4::Atom *ilst = *(--it);
long offset = ilst->offset;
long length = ilst->length;
MP4::Atom *meta = path[path.size() - 2];
AtomList::Iterator index = meta->children.find(ilst);
MP4::Atom *meta = *(--it);
AtomList::ConstIterator index = meta->children.find(ilst);
// check if there is an atom before 'ilst', and possibly use it as padding
if(index != meta->children.begin()) {
AtomList::Iterator prevIndex = index;
AtomList::ConstIterator prevIndex = index;
prevIndex--;
MP4::Atom *prev = *prevIndex;
if(prev->name == "free") {
@@ -625,7 +659,7 @@ MP4::Tag::saveExisting(ByteVector &data, AtomList &path)
}
}
// check if there is an atom after 'ilst', and possibly use it as padding
AtomList::Iterator nextIndex = index;
AtomList::ConstIterator nextIndex = index;
nextIndex++;
if(nextIndex != meta->children.end()) {
MP4::Atom *next = *nextIndex;
@@ -657,7 +691,7 @@ MP4::Tag::title() const
{
if(d->items.contains("\251nam"))
return d->items["\251nam"].toStringList().toString(", ");
return String::null;
return String();
}
String
@@ -665,7 +699,7 @@ MP4::Tag::artist() const
{
if(d->items.contains("\251ART"))
return d->items["\251ART"].toStringList().toString(", ");
return String::null;
return String();
}
String
@@ -673,7 +707,7 @@ MP4::Tag::album() const
{
if(d->items.contains("\251alb"))
return d->items["\251alb"].toStringList().toString(", ");
return String::null;
return String();
}
String
@@ -681,7 +715,7 @@ MP4::Tag::comment() const
{
if(d->items.contains("\251cmt"))
return d->items["\251cmt"].toStringList().toString(", ");
return String::null;
return String();
}
String
@@ -689,7 +723,7 @@ MP4::Tag::genre() const
{
if(d->items.contains("\251gen"))
return d->items["\251gen"].toStringList().toString(", ");
return String::null;
return String();
}
unsigned int
@@ -739,87 +773,122 @@ MP4::Tag::setGenre(const String &value)
}
void
MP4::Tag::setYear(uint value)
MP4::Tag::setYear(unsigned int value)
{
d->items["\251day"] = StringList(String::number(value));
}
void
MP4::Tag::setTrack(uint value)
MP4::Tag::setTrack(unsigned int value)
{
d->items["trkn"] = MP4::Item(value, 0);
}
MP4::ItemListMap &
MP4::Tag::itemListMap()
bool MP4::Tag::isEmpty() const
{
return d->items.isEmpty();
}
MP4::ItemMap &MP4::Tag::itemListMap()
{
return d->items;
}
static const char *keyTranslation[][2] = {
{ "\251nam", "TITLE" },
{ "\251ART", "ARTIST" },
{ "\251alb", "ALBUM" },
{ "\251cmt", "COMMENT" },
{ "\251gen", "GENRE" },
{ "\251day", "DATE" },
{ "\251wrt", "COMPOSER" },
{ "\251grp", "GROUPING" },
{ "trkn", "TRACKNUMBER" },
{ "disk", "DISCNUMBER" },
{ "cpil", "COMPILATION" },
{ "tmpo", "BPM" },
{ "cprt", "COPYRIGHT" },
{ "\251lyr", "LYRICS" },
{ "\251too", "ENCODEDBY" },
{ "soal", "ALBUMSORT" },
{ "soaa", "ALBUMARTISTSORT" },
{ "soar", "ARTISTSORT" },
{ "sonm", "TITLESORT" },
{ "soco", "COMPOSERSORT" },
{ "sosn", "SHOWSORT" },
{ "----:com.apple.iTunes:MusicBrainz Track Id", "MUSICBRAINZ_TRACKID" },
{ "----:com.apple.iTunes:MusicBrainz Artist Id", "MUSICBRAINZ_ARTISTID" },
{ "----:com.apple.iTunes:MusicBrainz Album Id", "MUSICBRAINZ_ALBUMID" },
{ "----:com.apple.iTunes:MusicBrainz Album Artist Id", "MUSICBRAINZ_ALBUMARTISTID" },
{ "----:com.apple.iTunes:MusicBrainz Release Group Id", "MUSICBRAINZ_RELEASEGROUPID" },
{ "----:com.apple.iTunes:MusicBrainz Work Id", "MUSICBRAINZ_WORKID" },
{ "----:com.apple.iTunes:ASIN", "ASIN" },
{ "----:com.apple.iTunes:LABEL", "LABEL" },
{ "----:com.apple.iTunes:LYRICIST", "LYRICIST" },
{ "----:com.apple.iTunes:CONDUCTOR", "CONDUCTOR" },
{ "----:com.apple.iTunes:REMIXER", "REMIXER" },
{ "----:com.apple.iTunes:ENGINEER", "ENGINEER" },
{ "----:com.apple.iTunes:PRODUCER", "PRODUCER" },
{ "----:com.apple.iTunes:DJMIXER", "DJMIXER" },
{ "----:com.apple.iTunes:MIXER", "MIXER" },
{ "----:com.apple.iTunes:SUBTITLE", "SUBTITLE" },
{ "----:com.apple.iTunes:DISCSUBTITLE", "DISCSUBTITLE" },
{ "----:com.apple.iTunes:MOOD", "MOOD" },
{ "----:com.apple.iTunes:ISRC", "ISRC" },
{ "----:com.apple.iTunes:CATALOGNUMBER", "CATALOGNUMBER" },
{ "----:com.apple.iTunes:BARCODE", "BARCODE" },
{ "----:com.apple.iTunes:SCRIPT", "SCRIPT" },
{ "----:com.apple.iTunes:LANGUAGE", "LANGUAGE" },
{ "----:com.apple.iTunes:LICENSE", "LICENSE" },
{ "----:com.apple.iTunes:MEDIA", "MEDIA" },
};
const MP4::ItemMap &MP4::Tag::itemMap() const
{
return d->items;
}
MP4::Item MP4::Tag::item(const String &key) const
{
return d->items[key];
}
void MP4::Tag::setItem(const String &key, const Item &value)
{
d->items[key] = value;
}
void MP4::Tag::removeItem(const String &key)
{
d->items.erase(key);
}
bool MP4::Tag::contains(const String &key) const
{
return d->items.contains(key);
}
namespace
{
const char *keyTranslation[][2] = {
{ "\251nam", "TITLE" },
{ "\251ART", "ARTIST" },
{ "\251alb", "ALBUM" },
{ "\251cmt", "COMMENT" },
{ "\251gen", "GENRE" },
{ "\251day", "DATE" },
{ "\251wrt", "COMPOSER" },
{ "\251grp", "GROUPING" },
{ "aART", "ALBUMARTIST" },
{ "trkn", "TRACKNUMBER" },
{ "disk", "DISCNUMBER" },
{ "cpil", "COMPILATION" },
{ "tmpo", "BPM" },
{ "cprt", "COPYRIGHT" },
{ "\251lyr", "LYRICS" },
{ "\251too", "ENCODEDBY" },
{ "soal", "ALBUMSORT" },
{ "soaa", "ALBUMARTISTSORT" },
{ "soar", "ARTISTSORT" },
{ "sonm", "TITLESORT" },
{ "soco", "COMPOSERSORT" },
{ "sosn", "SHOWSORT" },
{ "----:com.apple.iTunes:MusicBrainz Track Id", "MUSICBRAINZ_TRACKID" },
{ "----:com.apple.iTunes:MusicBrainz Artist Id", "MUSICBRAINZ_ARTISTID" },
{ "----:com.apple.iTunes:MusicBrainz Album Id", "MUSICBRAINZ_ALBUMID" },
{ "----:com.apple.iTunes:MusicBrainz Album Artist Id", "MUSICBRAINZ_ALBUMARTISTID" },
{ "----:com.apple.iTunes:MusicBrainz Release Group Id", "MUSICBRAINZ_RELEASEGROUPID" },
{ "----:com.apple.iTunes:MusicBrainz Work Id", "MUSICBRAINZ_WORKID" },
{ "----:com.apple.iTunes:ASIN", "ASIN" },
{ "----:com.apple.iTunes:LABEL", "LABEL" },
{ "----:com.apple.iTunes:LYRICIST", "LYRICIST" },
{ "----:com.apple.iTunes:CONDUCTOR", "CONDUCTOR" },
{ "----:com.apple.iTunes:REMIXER", "REMIXER" },
{ "----:com.apple.iTunes:ENGINEER", "ENGINEER" },
{ "----:com.apple.iTunes:PRODUCER", "PRODUCER" },
{ "----:com.apple.iTunes:DJMIXER", "DJMIXER" },
{ "----:com.apple.iTunes:MIXER", "MIXER" },
{ "----:com.apple.iTunes:SUBTITLE", "SUBTITLE" },
{ "----:com.apple.iTunes:DISCSUBTITLE", "DISCSUBTITLE" },
{ "----:com.apple.iTunes:MOOD", "MOOD" },
{ "----:com.apple.iTunes:ISRC", "ISRC" },
{ "----:com.apple.iTunes:CATALOGNUMBER", "CATALOGNUMBER" },
{ "----:com.apple.iTunes:BARCODE", "BARCODE" },
{ "----:com.apple.iTunes:SCRIPT", "SCRIPT" },
{ "----:com.apple.iTunes:LANGUAGE", "LANGUAGE" },
{ "----:com.apple.iTunes:LICENSE", "LICENSE" },
{ "----:com.apple.iTunes:MEDIA", "MEDIA" },
};
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];
}
return String();
}
}
PropertyMap MP4::Tag::properties() const
{
static Map<String, String> keyMap;
if(keyMap.isEmpty()) {
int numKeys = sizeof(keyTranslation) / sizeof(keyTranslation[0]);
for(int i = 0; i < numKeys; i++) {
keyMap[keyTranslation[i][0]] = keyTranslation[i][1];
}
}
PropertyMap props;
MP4::ItemListMap::ConstIterator it = d->items.begin();
for(; it != d->items.end(); ++it) {
if(keyMap.contains(it->first)) {
String key = keyMap[it->first];
for(MP4::ItemMap::ConstIterator it = d->items.begin(); it != d->items.end(); ++it) {
const String key = translateKey(it->first);
if(!key.isEmpty()) {
if(key == "TRACKNUMBER" || key == "DISCNUMBER") {
MP4::Item::IntPair ip = it->second.toIntPair();
String value = String::number(ip.first);
@@ -847,8 +916,7 @@ PropertyMap MP4::Tag::properties() const
void MP4::Tag::removeUnsupportedProperties(const StringList &props)
{
StringList::ConstIterator it = props.begin();
for(; it != props.end(); ++it)
for(StringList::ConstIterator it = props.begin(); it != props.end(); ++it)
d->items.erase(*it);
}
@@ -863,22 +931,20 @@ PropertyMap MP4::Tag::setProperties(const PropertyMap &props)
}
PropertyMap origProps = properties();
PropertyMap::ConstIterator it = origProps.begin();
for(; it != origProps.end(); ++it) {
for(PropertyMap::ConstIterator it = origProps.begin(); it != origProps.end(); ++it) {
if(!props.contains(it->first) || props[it->first].isEmpty()) {
d->items.erase(reverseKeyMap[it->first]);
}
}
PropertyMap ignoredProps;
it = props.begin();
for(; it != props.end(); ++it) {
for(PropertyMap::ConstIterator it = props.begin(); it != props.end(); ++it) {
if(reverseKeyMap.contains(it->first)) {
String name = reverseKeyMap[it->first];
if(it->first == "TRACKNUMBER" || it->first == "DISCNUMBER") {
if((it->first == "TRACKNUMBER" || it->first == "DISCNUMBER") && !it->second.isEmpty()) {
int first = 0, second = 0;
StringList parts = StringList::split(it->second.front(), "/");
if(parts.size() > 0) {
if(!parts.isEmpty()) {
first = parts[0].toInt();
if(parts.size() > 1) {
second = parts[1].toInt();
@@ -886,11 +952,11 @@ PropertyMap MP4::Tag::setProperties(const PropertyMap &props)
d->items[name] = MP4::Item(first, second);
}
}
else if(it->first == "BPM") {
else if(it->first == "BPM" && !it->second.isEmpty()) {
int value = it->second.front().toInt();
d->items[name] = MP4::Item(value);
}
else if(it->first == "COMPILATION") {
else if(it->first == "COMPILATION" && !it->second.isEmpty()) {
bool value = (it->second.front().toInt() != 0);
d->items[name] = MP4::Item(value);
}

View File

@@ -39,71 +39,110 @@ namespace TagLib {
namespace MP4 {
/*!
* \deprecated
*/
typedef TagLib::Map<String, Item> ItemListMap;
typedef TagLib::Map<String, Item> ItemMap;
class TAGLIB_EXPORT Tag: public TagLib::Tag
{
public:
Tag();
Tag(TagLib::File *file, Atoms *atoms);
~Tag();
virtual ~Tag();
bool save();
String title() const;
String artist() const;
String album() const;
String comment() const;
String genre() const;
uint year() const;
uint track() const;
virtual String title() const;
virtual String artist() const;
virtual String album() const;
virtual String comment() const;
virtual String genre() const;
virtual unsigned int year() const;
virtual unsigned int track() const;
void setTitle(const String &value);
void setArtist(const String &value);
void setAlbum(const String &value);
void setComment(const String &value);
void setGenre(const String &value);
void setYear(uint value);
void setTrack(uint value);
virtual void setTitle(const String &value);
virtual void setArtist(const String &value);
virtual void setAlbum(const String &value);
virtual void setComment(const String &value);
virtual void setGenre(const String &value);
virtual void setYear(unsigned int value);
virtual void setTrack(unsigned int value);
ItemListMap &itemListMap();
virtual bool isEmpty() const;
/*!
* \deprecated Use the item() and setItem() API instead
*/
ItemMap &itemListMap();
/*!
* Returns a string-keyed map of the MP4::Items for this tag.
*/
const ItemMap &itemMap() const;
/*!
* \return The item, if any, corresponding to \a key.
*/
Item item(const String &key) const;
/*!
* Sets the value of \a key to \a value, overwriting any previous value.
*/
void setItem(const String &key, const Item &value);
/*!
* Removes the entry with \a key from the tag, or does nothing if it does
* not exist.
*/
void removeItem(const String &key);
/*!
* \return True if the tag contains an entry for \a key.
*/
bool contains(const String &key) const;
PropertyMap properties() const;
void removeUnsupportedProperties(const StringList& properties);
PropertyMap setProperties(const PropertyMap &properties);
private:
AtomDataList parseData2(Atom *atom, TagLib::File *file, int expectedFlags = -1, bool freeForm = false);
TagLib::ByteVectorList parseData(Atom *atom, TagLib::File *file, int expectedFlags = -1, bool freeForm = false);
void parseText(Atom *atom, TagLib::File *file, int expectedFlags = 1);
void parseFreeForm(Atom *atom, TagLib::File *file);
void parseInt(Atom *atom, TagLib::File *file);
void parseByte(Atom *atom, TagLib::File *file);
void parseUInt(Atom *atom, TagLib::File *file);
void parseLongLong(Atom *atom, TagLib::File *file);
void parseGnre(Atom *atom, TagLib::File *file);
void parseIntPair(Atom *atom, TagLib::File *file);
void parseBool(Atom *atom, TagLib::File *file);
void parseCovr(Atom *atom, TagLib::File *file);
AtomDataList parseData2(const Atom *atom, int expectedFlags = -1,
bool freeForm = false);
ByteVectorList parseData(const Atom *atom, int expectedFlags = -1,
bool freeForm = false);
void parseText(const Atom *atom, int expectedFlags = 1);
void parseFreeForm(const Atom *atom);
void parseInt(const Atom *atom);
void parseByte(const Atom *atom);
void parseUInt(const Atom *atom);
void parseLongLong(const Atom *atom);
void parseGnre(const Atom *atom);
void parseIntPair(const Atom *atom);
void parseBool(const Atom *atom);
void parseCovr(const Atom *atom);
TagLib::ByteVector padIlst(const ByteVector &data, int length = -1);
TagLib::ByteVector renderAtom(const ByteVector &name, const TagLib::ByteVector &data);
TagLib::ByteVector renderData(const ByteVector &name, int flags, const TagLib::ByteVectorList &data);
TagLib::ByteVector renderText(const ByteVector &name, Item &item, int flags = TypeUTF8);
TagLib::ByteVector renderFreeForm(const String &name, Item &item);
TagLib::ByteVector renderBool(const ByteVector &name, Item &item);
TagLib::ByteVector renderInt(const ByteVector &name, Item &item);
TagLib::ByteVector renderByte(const ByteVector &name, Item &item);
TagLib::ByteVector renderUInt(const ByteVector &name, Item &item);
TagLib::ByteVector renderLongLong(const ByteVector &name, Item &item);
TagLib::ByteVector renderIntPair(const ByteVector &name, Item &item);
TagLib::ByteVector renderIntPairNoTrailing(const ByteVector &name, Item &item);
TagLib::ByteVector renderCovr(const ByteVector &name, Item &item);
ByteVector padIlst(const ByteVector &data, int length = -1) const;
ByteVector renderAtom(const ByteVector &name, const ByteVector &data) const;
ByteVector renderData(const ByteVector &name, int flags,
const ByteVectorList &data) const;
ByteVector renderText(const ByteVector &name, const Item &item,
int flags = TypeUTF8) const;
ByteVector renderFreeForm(const String &name, const Item &item) const;
ByteVector renderBool(const ByteVector &name, const Item &item) const;
ByteVector renderInt(const ByteVector &name, const Item &item) const;
ByteVector renderByte(const ByteVector &name, const Item &item) const;
ByteVector renderUInt(const ByteVector &name, const Item &item) const;
ByteVector renderLongLong(const ByteVector &name, const Item &item) const;
ByteVector renderIntPair(const ByteVector &name, const Item &item) const;
ByteVector renderIntPairNoTrailing(const ByteVector &name, const Item &item) const;
ByteVector renderCovr(const ByteVector &name, const Item &item) const;
void updateParents(AtomList &path, long delta, int ignore = 0);
void updateParents(const AtomList &path, long delta, int ignore = 0);
void updateOffsets(long delta, long offset);
void saveNew(TagLib::ByteVector &data);
void saveExisting(TagLib::ByteVector &data, AtomList &path);
void saveNew(ByteVector data);
void saveExisting(ByteVector data, const AtomList &path);
void addItem(const String &name, const Item &value);

View File

@@ -28,6 +28,7 @@
#include <tagunion.h>
#include <tdebug.h>
#include <tpropertymap.h>
#include <tagutils.h>
#include "mpcfile.h"
#include "id3v1tag.h"
@@ -52,11 +53,7 @@ public:
ID3v2Header(0),
ID3v2Location(-1),
ID3v2Size(0),
properties(0),
scanned(false),
hasAPE(false),
hasID3v1(false),
hasID3v2(false) {}
properties(0) {}
~FilePrivate()
{
@@ -65,45 +62,37 @@ public:
}
long APELocation;
uint APESize;
long APESize;
long ID3v1Location;
ID3v2::Header *ID3v2Header;
long ID3v2Location;
uint ID3v2Size;
long ID3v2Size;
TagUnion tag;
Properties *properties;
bool scanned;
// These indicate whether the file *on disk* has these tags, not if
// this data structure does. This is used in computing offsets.
bool hasAPE;
bool hasID3v1;
bool hasID3v2;
};
////////////////////////////////////////////////////////////////////////////////
// public members
////////////////////////////////////////////////////////////////////////////////
MPC::File::File(FileName file, bool readProperties,
Properties::ReadStyle propertiesStyle) : TagLib::File(file)
MPC::File::File(FileName file, bool readProperties, Properties::ReadStyle) :
TagLib::File(file),
d(new FilePrivate())
{
d = new FilePrivate;
if(isOpen())
read(readProperties, propertiesStyle);
read(readProperties);
}
MPC::File::File(IOStream *stream, bool readProperties,
Properties::ReadStyle propertiesStyle) : TagLib::File(stream)
MPC::File::File(IOStream *stream, bool readProperties, Properties::ReadStyle) :
TagLib::File(stream),
d(new FilePrivate())
{
d = new FilePrivate;
if(isOpen())
read(readProperties, propertiesStyle);
read(readProperties);
}
MPC::File::~File()
@@ -118,26 +107,20 @@ TagLib::Tag *MPC::File::tag() const
PropertyMap MPC::File::properties() const
{
if(d->hasAPE)
return d->tag.access<APE::Tag>(MPCAPEIndex, false)->properties();
if(d->hasID3v1)
return d->tag.access<ID3v1::Tag>(MPCID3v1Index, false)->properties();
return PropertyMap();
return d->tag.properties();
}
void MPC::File::removeUnsupportedProperties(const StringList &properties)
{
if(d->hasAPE)
d->tag.access<APE::Tag>(MPCAPEIndex, false)->removeUnsupportedProperties(properties);
if(d->hasID3v1)
d->tag.access<ID3v1::Tag>(MPCID3v1Index, false)->removeUnsupportedProperties(properties);
d->tag.removeUnsupportedProperties(properties);
}
PropertyMap MPC::File::setProperties(const PropertyMap &properties)
{
if(d->hasID3v1)
d->tag.access<APE::Tag>(MPCID3v1Index, false)->setProperties(properties);
return d->tag.access<APE::Tag>(MPCAPEIndex, true)->setProperties(properties);
if(ID3v1Tag())
ID3v1Tag()->setProperties(properties);
return APETag(true)->setProperties(properties);
}
MPC::Properties *MPC::File::audioProperties() const
@@ -154,69 +137,80 @@ bool MPC::File::save()
// Possibly strip ID3v2 tag
if(d->hasID3v2 && !d->ID3v2Header) {
if(!d->ID3v2Header && d->ID3v2Location >= 0) {
removeBlock(d->ID3v2Location, d->ID3v2Size);
d->hasID3v2 = false;
if(d->hasID3v1)
d->ID3v1Location -= d->ID3v2Size;
if(d->hasAPE)
if(d->APELocation >= 0)
d->APELocation -= d->ID3v2Size;
if(d->ID3v1Location >= 0)
d->ID3v1Location -= d->ID3v2Size;
d->ID3v2Location = -1;
d->ID3v2Size = 0;
}
// Update ID3v1 tag
if(ID3v1Tag()) {
if(d->hasID3v1) {
if(ID3v1Tag() && !ID3v1Tag()->isEmpty()) {
// ID3v1 tag is not empty. Update the old one or create a new one.
if(d->ID3v1Location >= 0) {
seek(d->ID3v1Location);
writeBlock(ID3v1Tag()->render());
}
else {
seek(0, End);
d->ID3v1Location = tell();
writeBlock(ID3v1Tag()->render());
d->hasID3v1 = true;
}
} else
if(d->hasID3v1) {
removeBlock(d->ID3v1Location, 128);
d->hasID3v1 = false;
if(d->hasAPE) {
if(d->APELocation > d->ID3v1Location)
d->APELocation -= 128;
}
writeBlock(ID3v1Tag()->render());
}
else {
// ID3v1 tag is empty. Remove the old one.
if(d->ID3v1Location >= 0) {
truncate(d->ID3v1Location);
d->ID3v1Location = -1;
}
}
// Update APE tag
if(APETag()) {
if(d->hasAPE)
insert(APETag()->render(), d->APELocation, d->APESize);
else {
if(d->hasID3v1) {
insert(APETag()->render(), d->ID3v1Location, 0);
d->APESize = APETag()->footer()->completeTagSize();
d->hasAPE = true;
if(APETag() && !APETag()->isEmpty()) {
// APE tag is not empty. Update the old one or create a new one.
if(d->APELocation < 0) {
if(d->ID3v1Location >= 0)
d->APELocation = d->ID3v1Location;
d->ID3v1Location += d->APESize;
}
else {
seek(0, End);
d->APELocation = tell();
writeBlock(APETag()->render());
d->APESize = APETag()->footer()->completeTagSize();
d->hasAPE = true;
}
else
d->APELocation = length();
}
const ByteVector data = APETag()->render();
insert(data, d->APELocation, d->APESize);
if(d->ID3v1Location >= 0)
d->ID3v1Location += (static_cast<long>(data.size()) - d->APESize);
d->APESize = data.size();
}
else {
// APE tag is empty. Remove the old one.
if(d->APELocation >= 0) {
removeBlock(d->APELocation, d->APESize);
if(d->ID3v1Location >= 0)
d->ID3v1Location -= d->APESize;
d->APELocation = -1;
d->APESize = 0;
}
}
else
if(d->hasAPE) {
removeBlock(d->APELocation, d->APESize);
d->hasAPE = false;
if(d->hasID3v1) {
if(d->ID3v1Location > d->APELocation)
d->ID3v1Location -= d->APESize;
}
}
return true;
}
@@ -233,22 +227,19 @@ APE::Tag *MPC::File::APETag(bool create)
void MPC::File::strip(int tags)
{
if(tags & ID3v1) {
if(tags & ID3v1)
d->tag.set(MPCID3v1Index, 0);
if(tags & APE)
d->tag.set(MPCAPEIndex, 0);
if(!ID3v1Tag())
APETag(true);
}
if(tags & ID3v2) {
delete d->ID3v2Header;
d->ID3v2Header = 0;
}
if(tags & APE) {
d->tag.set(MPCAPEIndex, 0);
if(!ID3v1Tag())
APETag(true);
}
}
void MPC::File::remove(int tags)
@@ -258,110 +249,71 @@ void MPC::File::remove(int tags)
bool MPC::File::hasID3v1Tag() const
{
return d->hasID3v1;
return (d->ID3v1Location >= 0);
}
bool MPC::File::hasAPETag() const
{
return d->hasAPE;
return (d->APELocation >= 0);
}
////////////////////////////////////////////////////////////////////////////////
// private members
////////////////////////////////////////////////////////////////////////////////
void MPC::File::read(bool readProperties, Properties::ReadStyle /* propertiesStyle */)
void MPC::File::read(bool readProperties)
{
// Look for an ID3v1 tag
// Look for an ID3v2 tag
d->ID3v1Location = findID3v1();
if(d->ID3v1Location >= 0) {
d->tag.set(MPCID3v1Index, new ID3v1::Tag(this, d->ID3v1Location));
d->hasID3v1 = true;
}
// Look for an APE tag
findAPE();
d->APELocation = findAPE();
if(d->APELocation >= 0) {
d->tag.set(MPCAPEIndex, new APE::Tag(this, d->APELocation));
d->APESize = APETag()->footer()->completeTagSize();
d->APELocation = d->APELocation + APETag()->footer()->size() - d->APESize;
d->hasAPE = true;
}
if(!d->hasID3v1)
APETag(true);
// Look for and skip an ID3v2 tag
d->ID3v2Location = findID3v2();
d->ID3v2Location = Utils::findID3v2(this);
if(d->ID3v2Location >= 0) {
seek(d->ID3v2Location);
d->ID3v2Header = new ID3v2::Header(readBlock(ID3v2::Header::size()));
d->ID3v2Size = d->ID3v2Header->completeTagSize();
d->hasID3v2 = true;
}
if(d->hasID3v2)
seek(d->ID3v2Location + d->ID3v2Size);
else
seek(0);
// Look for an ID3v1 tag
d->ID3v1Location = Utils::findID3v1(this);
if(d->ID3v1Location >= 0)
d->tag.set(MPCID3v1Index, new ID3v1::Tag(this, d->ID3v1Location));
// Look for an APE tag
d->APELocation = Utils::findAPE(this, d->ID3v1Location);
if(d->APELocation >= 0) {
d->tag.set(MPCAPEIndex, new APE::Tag(this, d->APELocation));
d->APESize = APETag()->footer()->completeTagSize();
d->APELocation = d->APELocation + APE::Footer::size() - d->APESize;
}
if(d->ID3v1Location < 0)
APETag(true);
// Look for MPC metadata
if(readProperties) {
d->properties = new Properties(this, length() - d->ID3v2Size - d->APESize);
long streamLength;
if(d->APELocation >= 0)
streamLength = d->APELocation;
else if(d->ID3v1Location >= 0)
streamLength = d->ID3v1Location;
else
streamLength = length();
if(d->ID3v2Location >= 0) {
seek(d->ID3v2Location + d->ID3v2Size);
streamLength -= (d->ID3v2Location + d->ID3v2Size);
}
else {
seek(0);
}
d->properties = new Properties(this, streamLength);
}
}
long MPC::File::findAPE()
{
if(!isValid())
return -1;
if(d->hasID3v1)
seek(-160, End);
else
seek(-32, End);
long p = tell();
if(readBlock(8) == APE::Tag::fileIdentifier())
return p;
return -1;
}
long MPC::File::findID3v1()
{
if(!isValid())
return -1;
seek(-128, End);
long p = tell();
if(readBlock(3) == ID3v1::Tag::fileIdentifier())
return p;
return -1;
}
long MPC::File::findID3v2()
{
if(!isValid())
return -1;
seek(0);
if(readBlock(3) == ID3v2::Header::fileIdentifier())
return 0;
return -1;
}

View File

@@ -139,6 +139,8 @@ namespace TagLib {
/*!
* Saves the file.
*
* This returns true if the save was successful.
*/
virtual bool save();
@@ -149,8 +151,8 @@ namespace TagLib {
* if there is no valid APE tag. If \a create is true it will create
* an APE tag if one does not exist and returns a valid pointer.
*
* \note This may return a valid pointer regardless of whether or not the
* file on disk has an ID3v1 tag. Use hasID3v1Tag() to check if the file
* \note This may return a valid pointer regardless of whether or not the
* file on disk has an ID3v1 tag. Use hasID3v1Tag() to check if the file
* on disk actually has an ID3v1 tag.
*
* \note The Tag <b>is still</b> owned by the MPEG::File and should not be
@@ -166,11 +168,11 @@ namespace TagLib {
*
* If \a create is false (the default) this may return a null pointer
* if there is no valid APE tag. If \a create is true it will create
* an APE tag if one does not exist and returns a valid pointer. If
* an APE tag if one does not exist and returns a valid pointer. If
* there already be an ID3v1 tag, the new APE tag will be placed before it.
*
* \note This may return a valid pointer regardless of whether or not the
* file on disk has an APE tag. Use hasAPETag() to check if the file
* \note This may return a valid pointer regardless of whether or not the
* file on disk has an APE tag. Use hasAPETag() to check if the file
* on disk actually has an APE tag.
*
* \note The Tag <b>is still</b> owned by the MPEG::File and should not be
@@ -216,11 +218,7 @@ namespace TagLib {
File(const File &);
File &operator=(const File &);
void read(bool readProperties, Properties::ReadStyle propertiesStyle);
void scan();
long findAPE();
long findID3v1();
long findID3v2();
void read(bool readProperties);
class FilePrivate;
FilePrivate *d;

View File

@@ -36,9 +36,7 @@ using namespace TagLib;
class MPC::Properties::PropertiesPrivate
{
public:
PropertiesPrivate(long length, ReadStyle s) :
streamLength(length),
style(s),
PropertiesPrivate() :
version(0),
length(0),
bitrate(0),
@@ -51,43 +49,43 @@ public:
albumGain(0),
albumPeak(0) {}
long streamLength;
ReadStyle style;
int version;
int length;
int bitrate;
int sampleRate;
int channels;
uint totalFrames;
uint sampleFrames;
uint trackGain;
uint trackPeak;
uint albumGain;
uint albumPeak;
String flags;
int version;
int length;
int bitrate;
int sampleRate;
int channels;
unsigned int totalFrames;
unsigned int sampleFrames;
unsigned int trackGain;
unsigned int trackPeak;
unsigned int albumGain;
unsigned int albumPeak;
String flags;
};
////////////////////////////////////////////////////////////////////////////////
// public members
////////////////////////////////////////////////////////////////////////////////
MPC::Properties::Properties(const ByteVector &data, long streamLength, ReadStyle style) : AudioProperties(style)
MPC::Properties::Properties(const ByteVector &data, long streamLength, ReadStyle style) :
AudioProperties(style),
d(new PropertiesPrivate())
{
d = new PropertiesPrivate(streamLength, style);
readSV7(data);
readSV7(data, streamLength);
}
MPC::Properties::Properties(File *file, long streamLength, ReadStyle style) : AudioProperties(style)
MPC::Properties::Properties(File *file, long streamLength, ReadStyle style) :
AudioProperties(style),
d(new PropertiesPrivate())
{
d = new PropertiesPrivate(streamLength, style);
ByteVector magic = file->readBlock(4);
if(magic == "MPCK") {
// Musepack version 8
readSV8(file);
readSV8(file, streamLength);
}
else {
// Musepack version 7 or older, fixed size header
readSV7(magic + file->readBlock(MPC::HeaderSize - 4));
readSV7(magic + file->readBlock(MPC::HeaderSize - 4), streamLength);
}
}
@@ -97,6 +95,16 @@ MPC::Properties::~Properties()
}
int MPC::Properties::length() const
{
return lengthInSeconds();
}
int MPC::Properties::lengthInSeconds() const
{
return d->length / 1000;
}
int MPC::Properties::lengthInMilliseconds() const
{
return d->length;
}
@@ -121,12 +129,12 @@ int MPC::Properties::mpcVersion() const
return d->version;
}
TagLib::uint MPC::Properties::totalFrames() const
unsigned int MPC::Properties::totalFrames() const
{
return d->totalFrames;
}
TagLib::uint MPC::Properties::sampleFrames() const
unsigned int MPC::Properties::sampleFrames() const
{
return d->sampleFrames;
}
@@ -155,78 +163,121 @@ int MPC::Properties::albumPeak() const
// private members
////////////////////////////////////////////////////////////////////////////////
unsigned long readSize(File *file, TagLib::uint &sizelength)
namespace
{
unsigned char tmp;
unsigned long size = 0;
unsigned long readSize(File *file, unsigned int &sizeLength, bool &eof)
{
sizeLength = 0;
eof = false;
do {
ByteVector b = file->readBlock(1);
tmp = b[0];
size = (size << 7) | (tmp & 0x7F);
sizelength++;
} while((tmp & 0x80));
return size;
unsigned char tmp;
unsigned long size = 0;
do {
const ByteVector b = file->readBlock(1);
if(b.isEmpty()) {
eof = true;
break;
}
tmp = b[0];
size = (size << 7) | (tmp & 0x7F);
sizeLength++;
} while((tmp & 0x80));
return size;
}
unsigned long readSize(const ByteVector &data, unsigned int &pos)
{
unsigned char tmp;
unsigned long size = 0;
do {
tmp = data[pos++];
size = (size << 7) | (tmp & 0x7F);
} while((tmp & 0x80) && (pos < data.size()));
return size;
}
// This array looks weird, but the same as original MusePack code found at:
// https://www.musepack.net/index.php?pg=src
const unsigned short sftable [8] = { 44100, 48000, 37800, 32000, 0, 0, 0, 0 };
}
unsigned long readSize(const ByteVector &data, TagLib::uint &sizelength)
{
unsigned char tmp;
unsigned long size = 0;
unsigned long pos = 0;
do {
tmp = data[pos++];
size = (size << 7) | (tmp & 0x7F);
sizelength++;
} while((tmp & 0x80) && (pos < data.size()));
return size;
}
static const unsigned short sftable [4] = { 44100, 48000, 37800, 32000 };
void MPC::Properties::readSV8(File *file)
void MPC::Properties::readSV8(File *file, long streamLength)
{
bool readSH = false, readRG = false;
while(!readSH && !readRG) {
ByteVector packetType = file->readBlock(2);
uint packetSizeLength = 0;
unsigned long packetSize = readSize(file, packetSizeLength);
unsigned long dataSize = packetSize - 2 - packetSizeLength;
const ByteVector packetType = file->readBlock(2);
unsigned int packetSizeLength;
bool eof;
const unsigned long packetSize = readSize(file, packetSizeLength, eof);
if(eof) {
debug("MPC::Properties::readSV8() - Reached to EOF.");
break;
}
const unsigned long dataSize = packetSize - 2 - packetSizeLength;
const ByteVector data = file->readBlock(dataSize);
if(data.size() != dataSize) {
debug("MPC::Properties::readSV8() - dataSize doesn't match the actual data size.");
break;
}
if(packetType == "SH") {
// Stream Header
// http://trac.musepack.net/wiki/SV8Specification#StreamHeaderPacket
ByteVector data = file->readBlock(dataSize);
if(dataSize <= 5) {
debug("MPC::Properties::readSV8() - \"SH\" packet is too short to parse.");
break;
}
readSH = true;
TagLib::uint pos = 4;
unsigned int pos = 4;
d->version = data[pos];
pos += 1;
d->sampleFrames = readSize(data.mid(pos), pos);
ulong begSilence = readSize(data.mid(pos), pos);
d->sampleFrames = readSize(data, pos);
if(pos > dataSize - 3) {
debug("MPC::Properties::readSV8() - \"SH\" packet is corrupt.");
break;
}
std::bitset<16> flags(TAGLIB_CONSTRUCT_BITSET(data.toUShort(pos, true)));
const unsigned long begSilence = readSize(data, pos);
if(pos > dataSize - 2) {
debug("MPC::Properties::readSV8() - \"SH\" packet is corrupt.");
break;
}
const unsigned short flags = data.toUShort(pos, true);
pos += 2;
d->sampleRate = sftable[flags[15] * 4 + flags[14] * 2 + flags[13]];
d->channels = flags[7] * 8 + flags[6] * 4 + flags[5] * 2 + flags[4] + 1;
d->sampleRate = sftable[(flags >> 13) & 0x07];
d->channels = ((flags >> 4) & 0x0F) + 1;
if((d->sampleFrames - begSilence) != 0)
d->bitrate = (int)(d->streamLength * 8.0 * d->sampleRate / (d->sampleFrames - begSilence));
d->bitrate = d->bitrate / 1000;
d->length = (d->sampleFrames - begSilence) / d->sampleRate;
const unsigned int frameCount = d->sampleFrames - begSilence;
if(frameCount > 0 && d->sampleRate > 0) {
const double length = frameCount * 1000.0 / d->sampleRate;
d->length = static_cast<int>(length + 0.5);
d->bitrate = static_cast<int>(streamLength * 8.0 / length + 0.5);
}
}
else if (packetType == "RG") {
// Replay Gain
// http://trac.musepack.net/wiki/SV8Specification#ReplaygainPacket
ByteVector data = file->readBlock(dataSize);
if(dataSize <= 9) {
debug("MPC::Properties::readSV8() - \"RG\" packet is too short to parse.");
break;
}
readRG = true;
int replayGainVersion = data[0];
const int replayGainVersion = data[0];
if(replayGainVersion == 1) {
d->trackGain = data.toShort(1, true);
d->trackPeak = data.toShort(3, true);
@@ -245,7 +296,7 @@ void MPC::Properties::readSV8(File *file)
}
}
void MPC::Properties::readSV7(const ByteVector &data)
void MPC::Properties::readSV7(const ByteVector &data, long streamLength)
{
if(data.startsWith("MP+")) {
d->version = data[3] & 15;
@@ -254,11 +305,11 @@ void MPC::Properties::readSV7(const ByteVector &data)
d->totalFrames = data.toUInt(4, false);
std::bitset<32> flags(TAGLIB_CONSTRUCT_BITSET(data.toUInt(8, false)));
d->sampleRate = sftable[flags[17] * 2 + flags[16]];
d->channels = 2;
const unsigned int flags = data.toUInt(8, false);
d->sampleRate = sftable[(flags >> 16) & 0x03];
d->channels = 2;
uint gapless = data.toUInt(5, false);
const unsigned int gapless = data.toUInt(5, false);
d->trackGain = data.toShort(14, false);
d->trackPeak = data.toShort(12, false);
@@ -286,19 +337,19 @@ void MPC::Properties::readSV7(const ByteVector &data)
bool trueGapless = (gapless >> 31) & 0x0001;
if(trueGapless) {
uint lastFrameSamples = (gapless >> 20) & 0x07FF;
unsigned int lastFrameSamples = (gapless >> 20) & 0x07FF;
d->sampleFrames = d->totalFrames * 1152 - lastFrameSamples;
}
else
d->sampleFrames = d->totalFrames * 1152 - 576;
}
else {
uint headerData = data.toUInt(0, false);
const unsigned int headerData = data.toUInt(0, false);
d->bitrate = (headerData >> 23) & 0x01ff;
d->version = (headerData >> 11) & 0x03ff;
d->bitrate = (headerData >> 23) & 0x01ff;
d->version = (headerData >> 11) & 0x03ff;
d->sampleRate = 44100;
d->channels = 2;
d->channels = 2;
if(d->version >= 5)
d->totalFrames = data.toUInt(4, false);
@@ -308,9 +359,11 @@ void MPC::Properties::readSV7(const ByteVector &data)
d->sampleFrames = d->totalFrames * 1152 - 576;
}
d->length = d->sampleRate > 0 ? (d->sampleFrames + (d->sampleRate / 2)) / d->sampleRate : 0;
if(d->sampleFrames > 0 && d->sampleRate > 0) {
const double length = d->sampleFrames * 1000.0 / d->sampleRate;
d->length = static_cast<int>(length + 0.5);
if(!d->bitrate)
d->bitrate = d->length > 0 ? ((d->streamLength * 8L) / d->length) / 1000 : 0;
if(d->bitrate == 0)
d->bitrate = static_cast<int>(streamLength * 8.0 / length + 0.5);
}
}

View File

@@ -35,7 +35,7 @@ namespace TagLib {
class File;
static const uint HeaderSize = 8*7;
static const unsigned int HeaderSize = 8 * 7;
//! An implementation of audio property reading for MPC
@@ -66,19 +66,55 @@ namespace TagLib {
*/
virtual ~Properties();
// Reimplementations.
/*!
* Returns the length of the file in seconds. The length is rounded down to
* the nearest whole second.
*
* \note This method is just an alias of lengthInSeconds().
*
* \deprecated
*/
virtual int length() const;
/*!
* Returns the length of the file in seconds. The length is rounded down to
* the nearest whole second.
*
* \see lengthInMilliseconds()
*/
// BIC: make virtual
int lengthInSeconds() const;
/*!
* Returns the length of the file in milliseconds.
*
* \see lengthInSeconds()
*/
// BIC: make virtual
int lengthInMilliseconds() const;
/*!
* Returns the average bit rate of the file in kb/s.
*/
virtual int bitrate() const;
/*!
* Returns the sample rate in Hz.
*/
virtual int sampleRate() const;
/*!
* Returns the number of audio channels.
*/
virtual int channels() const;
/*!
* Returns the version of the bitstream (SV4-SV8)
*/
int mpcVersion() const;
uint totalFrames() const;
uint sampleFrames() const;
unsigned int totalFrames() const;
unsigned int sampleFrames() const;
/*!
* Returns the track gain as an integer value,
@@ -110,8 +146,8 @@ namespace TagLib {
Properties(const Properties &);
Properties &operator=(const Properties &);
void readSV7(const ByteVector &data);
void readSV8(File *file);
void readSV7(const ByteVector &data, long streamLength);
void readSV8(File *file, long streamLength);
class PropertiesPrivate;
PropertiesPrivate *d;

View File

@@ -27,193 +27,239 @@
using namespace TagLib;
namespace TagLib {
namespace ID3v1 {
static const int genresSize = 148;
static const String genres[] = {
"Blues",
"Classic Rock",
"Country",
"Dance",
"Disco",
"Funk",
"Grunge",
"Hip-Hop",
"Jazz",
"Metal",
"New Age",
"Oldies",
"Other",
"Pop",
"R&B",
"Rap",
"Reggae",
"Rock",
"Techno",
"Industrial",
"Alternative",
"Ska",
"Death Metal",
"Pranks",
"Soundtrack",
"Euro-Techno",
"Ambient",
"Trip-Hop",
"Vocal",
"Jazz+Funk",
"Fusion",
"Trance",
"Classical",
"Instrumental",
"Acid",
"House",
"Game",
"Sound Clip",
"Gospel",
"Noise",
"Alternative Rock",
"Bass",
"Soul",
"Punk",
"Space",
"Meditative",
"Instrumental Pop",
"Instrumental Rock",
"Ethnic",
"Gothic",
"Darkwave",
"Techno-Industrial",
"Electronic",
"Pop-Folk",
"Eurodance",
"Dream",
"Southern Rock",
"Comedy",
"Cult",
"Gangsta",
"Top 40",
"Christian Rap",
"Pop/Funk",
"Jungle",
"Native American",
"Cabaret",
"New Wave",
"Psychedelic",
"Rave",
"Showtunes",
"Trailer",
"Lo-Fi",
"Tribal",
"Acid Punk",
"Acid Jazz",
"Polka",
"Retro",
"Musical",
"Rock & Roll",
"Hard Rock",
"Folk",
"Folk/Rock",
"National Folk",
"Swing",
"Fusion",
"Bebob",
"Latin",
"Revival",
"Celtic",
"Bluegrass",
"Avantgarde",
"Gothic Rock",
"Progressive Rock",
"Psychedelic Rock",
"Symphonic Rock",
"Slow Rock",
"Big Band",
"Chorus",
"Easy Listening",
"Acoustic",
"Humour",
"Speech",
"Chanson",
"Opera",
"Chamber Music",
"Sonata",
"Symphony",
"Booty Bass",
"Primus",
"Porn Groove",
"Satire",
"Slow Jam",
"Club",
"Tango",
"Samba",
"Folklore",
"Ballad",
"Power Ballad",
"Rhythmic Soul",
"Freestyle",
"Duet",
"Punk Rock",
"Drum Solo",
"A Cappella",
"Euro-House",
"Dance Hall",
"Goa",
"Drum & Bass",
"Club-House",
"Hardcore",
"Terror",
"Indie",
"BritPop",
"Negerpunk",
"Polsk Punk",
"Beat",
"Christian Gangsta Rap",
"Heavy Metal",
"Black Metal",
"Crossover",
"Contemporary Christian",
"Christian Rock",
"Merengue",
"Salsa",
"Thrash Metal",
"Anime",
"Jpop",
"Synthpop"
};
}
namespace
{
const wchar_t *genres[] = {
L"Blues",
L"Classic Rock",
L"Country",
L"Dance",
L"Disco",
L"Funk",
L"Grunge",
L"Hip-Hop",
L"Jazz",
L"Metal",
L"New Age",
L"Oldies",
L"Other",
L"Pop",
L"R&B",
L"Rap",
L"Reggae",
L"Rock",
L"Techno",
L"Industrial",
L"Alternative",
L"Ska",
L"Death Metal",
L"Pranks",
L"Soundtrack",
L"Euro-Techno",
L"Ambient",
L"Trip-Hop",
L"Vocal",
L"Jazz+Funk",
L"Fusion",
L"Trance",
L"Classical",
L"Instrumental",
L"Acid",
L"House",
L"Game",
L"Sound Clip",
L"Gospel",
L"Noise",
L"Alternative Rock",
L"Bass",
L"Soul",
L"Punk",
L"Space",
L"Meditative",
L"Instrumental Pop",
L"Instrumental Rock",
L"Ethnic",
L"Gothic",
L"Darkwave",
L"Techno-Industrial",
L"Electronic",
L"Pop-Folk",
L"Eurodance",
L"Dream",
L"Southern Rock",
L"Comedy",
L"Cult",
L"Gangsta",
L"Top 40",
L"Christian Rap",
L"Pop/Funk",
L"Jungle",
L"Native American",
L"Cabaret",
L"New Wave",
L"Psychedelic",
L"Rave",
L"Showtunes",
L"Trailer",
L"Lo-Fi",
L"Tribal",
L"Acid Punk",
L"Acid Jazz",
L"Polka",
L"Retro",
L"Musical",
L"Rock & Roll",
L"Hard Rock",
L"Folk",
L"Folk/Rock",
L"National Folk",
L"Swing",
L"Fusion",
L"Bebob",
L"Latin",
L"Revival",
L"Celtic",
L"Bluegrass",
L"Avantgarde",
L"Gothic Rock",
L"Progressive Rock",
L"Psychedelic Rock",
L"Symphonic Rock",
L"Slow Rock",
L"Big Band",
L"Chorus",
L"Easy Listening",
L"Acoustic",
L"Humour",
L"Speech",
L"Chanson",
L"Opera",
L"Chamber Music",
L"Sonata",
L"Symphony",
L"Booty Bass",
L"Primus",
L"Porn Groove",
L"Satire",
L"Slow Jam",
L"Club",
L"Tango",
L"Samba",
L"Folklore",
L"Ballad",
L"Power Ballad",
L"Rhythmic Soul",
L"Freestyle",
L"Duet",
L"Punk Rock",
L"Drum Solo",
L"A Cappella",
L"Euro-House",
L"Dance Hall",
L"Goa",
L"Drum & Bass",
L"Club-House",
L"Hardcore",
L"Terror",
L"Indie",
L"BritPop",
L"Negerpunk",
L"Polsk Punk",
L"Beat",
L"Christian Gangsta Rap",
L"Heavy Metal",
L"Black Metal",
L"Crossover",
L"Contemporary Christian",
L"Christian Rock",
L"Merengue",
L"Salsa",
L"Thrash Metal",
L"Anime",
L"Jpop",
L"Synthpop",
L"Abstract",
L"Art Rock",
L"Baroque",
L"Bhangra",
L"Big Beat",
L"Breakbeat",
L"Chillout",
L"Downtempo",
L"Dub",
L"EBM",
L"Eclectic",
L"Electro",
L"Electroclash",
L"Emo",
L"Experimental",
L"Garage",
L"Global",
L"IDM",
L"Illbient",
L"Industro-Goth",
L"Jam Band",
L"Krautrock",
L"Leftfield",
L"Lounge",
L"Math Rock",
L"New Romantic",
L"Nu-Breakz",
L"Post-Punk",
L"Post-Rock",
L"Psytrance",
L"Shoegaze",
L"Space Rock",
L"Trop Rock",
L"World Music",
L"Neoclassical",
L"Audiobook",
L"Audio Theatre",
L"Neue Deutsche Welle",
L"Podcast",
L"Indie Rock",
L"G-Funk",
L"Dubstep",
L"Garage Rock",
L"Psybient"
};
const int genresSize = sizeof(genres) / sizeof(genres[0]);
}
StringList ID3v1::genreList()
{
static StringList l;
if(l.isEmpty()) {
for(int i = 0; i < genresSize; i++)
l.append(genres[i]);
StringList l;
for(int i = 0; i < genresSize; i++) {
l.append(genres[i]);
}
return l;
}
ID3v1::GenreMap ID3v1::genreMap()
{
static GenreMap m;
if(m.isEmpty()) {
for(int i = 0; i < genresSize; i++)
m.insert(genres[i], i);
GenreMap m;
for(int i = 0; i < genresSize; i++) {
m.insert(genres[i], i);
}
return m;
}
String ID3v1::genre(int i)
{
if(i >= 0 && i < genresSize)
return genres[i] + String::null; // always make a copy
return String::null;
return String(genres[i]); // always make a copy
else
return String();
}
int ID3v1::genreIndex(const String &name)
{
if(genreMap().contains(name))
return genreMap()[name];
for(int i = 0; i < genresSize; ++i) {
if(name == genres[i])
return i;
}
return 255;
}

View File

@@ -49,7 +49,7 @@ namespace TagLib {
/*!
* Returns the name of the genre at \a index in the ID3v1 genre list. If
* \a index is out of range -- less than zero or greater than 146 -- a null
* \a index is out of range -- less than zero or greater than 191 -- a null
* string will be returned.
*/
String TAGLIB_EXPORT genre(int index);

View File

@@ -32,10 +32,20 @@
using namespace TagLib;
using namespace ID3v1;
namespace
{
const ID3v1::StringHandler defaultStringHandler;
const ID3v1::StringHandler *stringHandler = &defaultStringHandler;
}
class ID3v1::Tag::TagPrivate
{
public:
TagPrivate() : file(0), tagOffset(-1), track(0), genre(255) {}
TagPrivate() :
file(0),
tagOffset(0),
track(0),
genre(255) {}
File *file;
long tagOffset;
@@ -45,15 +55,10 @@ public:
String album;
String year;
String comment;
uchar track;
uchar genre;
static const StringHandler *stringHandler;
unsigned char track;
unsigned char genre;
};
static const StringHandler defaultStringHandler;
const ID3v1::StringHandler *ID3v1::Tag::TagPrivate::stringHandler = &defaultStringHandler;
////////////////////////////////////////////////////////////////////////////////
// StringHandler implementation
////////////////////////////////////////////////////////////////////////////////
@@ -69,26 +74,26 @@ String ID3v1::StringHandler::parse(const ByteVector &data) const
ByteVector ID3v1::StringHandler::render(const String &s) const
{
if(!s.isLatin1())
{
if(s.isLatin1())
return s.data(String::Latin1);
else
return ByteVector();
}
return s.data(String::Latin1);
}
////////////////////////////////////////////////////////////////////////////////
// public methods
////////////////////////////////////////////////////////////////////////////////
ID3v1::Tag::Tag() : TagLib::Tag()
ID3v1::Tag::Tag() :
TagLib::Tag(),
d(new TagPrivate())
{
d = new TagPrivate;
}
ID3v1::Tag::Tag(File *file, long tagOffset) : TagLib::Tag()
ID3v1::Tag::Tag(File *file, long tagOffset) :
TagLib::Tag(),
d(new TagPrivate())
{
d = new TagPrivate;
d->file = file;
d->tagOffset = tagOffset;
@@ -105,11 +110,11 @@ ByteVector ID3v1::Tag::render() const
ByteVector data;
data.append(fileIdentifier());
data.append(TagPrivate::stringHandler->render(d->title).resize(30));
data.append(TagPrivate::stringHandler->render(d->artist).resize(30));
data.append(TagPrivate::stringHandler->render(d->album).resize(30));
data.append(TagPrivate::stringHandler->render(d->year).resize(4));
data.append(TagPrivate::stringHandler->render(d->comment).resize(28));
data.append(stringHandler->render(d->title).resize(30));
data.append(stringHandler->render(d->artist).resize(30));
data.append(stringHandler->render(d->album).resize(30));
data.append(stringHandler->render(d->year).resize(4));
data.append(stringHandler->render(d->comment).resize(28));
data.append(char(0));
data.append(char(d->track));
data.append(char(d->genre));
@@ -147,12 +152,12 @@ String ID3v1::Tag::genre() const
return ID3v1::genre(d->genre);
}
TagLib::uint ID3v1::Tag::year() const
unsigned int ID3v1::Tag::year() const
{
return d->year.toInt();
}
TagLib::uint ID3v1::Tag::track() const
unsigned int ID3v1::Tag::track() const
{
return d->track;
}
@@ -182,32 +187,32 @@ void ID3v1::Tag::setGenre(const String &s)
d->genre = ID3v1::genreIndex(s);
}
void ID3v1::Tag::setYear(TagLib::uint i)
void ID3v1::Tag::setYear(unsigned int i)
{
d->year = i > 0 ? String::number(i) : String::null;
d->year = i > 0 ? String::number(i) : String();
}
void ID3v1::Tag::setTrack(TagLib::uint i)
void ID3v1::Tag::setTrack(unsigned int i)
{
d->track = i < 256 ? i : 0;
}
TagLib::uint ID3v1::Tag::genreNumber() const
unsigned int ID3v1::Tag::genreNumber() const
{
return d->genre;
}
void ID3v1::Tag::setGenreNumber(TagLib::uint i)
void ID3v1::Tag::setGenreNumber(unsigned int i)
{
d->genre = i < 256 ? i : 255;
}
void ID3v1::Tag::setStringHandler(const StringHandler *handler)
{
if (handler)
TagPrivate::stringHandler = handler;
if(handler)
stringHandler = handler;
else
TagPrivate::stringHandler = &defaultStringHandler;
stringHandler = &defaultStringHandler;
}
////////////////////////////////////////////////////////////////////////////////
@@ -219,7 +224,7 @@ void ID3v1::Tag::read()
if(d->file && d->file->isValid()) {
d->file->seek(d->tagOffset);
// read the tag -- always 128 bytes
ByteVector data = d->file->readBlock(128);
const ByteVector data = d->file->readBlock(128);
// some initial sanity checking
if(data.size() == 128 && data.startsWith("TAG"))
@@ -233,16 +238,16 @@ void ID3v1::Tag::parse(const ByteVector &data)
{
int offset = 3;
d->title = TagPrivate::stringHandler->parse(data.mid(offset, 30));
d->title = stringHandler->parse(data.mid(offset, 30));
offset += 30;
d->artist = TagPrivate::stringHandler->parse(data.mid(offset, 30));
d->artist = stringHandler->parse(data.mid(offset, 30));
offset += 30;
d->album = TagPrivate::stringHandler->parse(data.mid(offset, 30));
d->album = stringHandler->parse(data.mid(offset, 30));
offset += 30;
d->year = TagPrivate::stringHandler->parse(data.mid(offset, 4));
d->year = stringHandler->parse(data.mid(offset, 4));
offset += 4;
// Check for ID3v1.1 -- Note that ID3v1 *does not* support "track zero" -- this
@@ -253,13 +258,13 @@ void ID3v1::Tag::parse(const ByteVector &data)
if(data[offset + 28] == 0 && data[offset + 29] != 0) {
// ID3v1.1 detected
d->comment = TagPrivate::stringHandler->parse(data.mid(offset, 28));
d->track = uchar(data[offset + 29]);
d->comment = stringHandler->parse(data.mid(offset, 28));
d->track = static_cast<unsigned char>(data[offset + 29]);
}
else
d->comment = data.mid(offset, 30);
offset += 30;
d->genre = uchar(data[offset]);
d->genre = static_cast<unsigned char>(data[offset]);
}

View File

@@ -85,7 +85,7 @@ namespace TagLib {
//! The main class in the ID3v1 implementation
/*!
* This is an implementation of the ID3v1 format. ID3v1 is both the simplist
* This is an implementation of the ID3v1 format. ID3v1 is both the simplest
* and most common of tag formats but is rather limited. Because of its
* pervasiveness and the way that applications have been written around the
* fields that it provides, the generic TagLib::Tag API is a mirror of what is
@@ -140,31 +140,31 @@ namespace TagLib {
virtual String album() const;
virtual String comment() const;
virtual String genre() const;
virtual TagLib::uint year() const;
virtual TagLib::uint track() const;
virtual unsigned int year() const;
virtual unsigned int track() const;
virtual void setTitle(const String &s);
virtual void setArtist(const String &s);
virtual void setAlbum(const String &s);
virtual void setComment(const String &s);
virtual void setGenre(const String &s);
virtual void setYear(TagLib::uint i);
virtual void setTrack(TagLib::uint i);
virtual void setYear(unsigned int i);
virtual void setTrack(unsigned int i);
/*!
* Returns the genre in number.
*
* /note Normally 255 indicates that this tag contains no genre.
* \note Normally 255 indicates that this tag contains no genre.
*/
TagLib::uint genreNumber() const;
unsigned int genreNumber() const;
/*!
* Sets the genre in number to \a i.
*
* /note Valid value is from 0 up to 255. Normally 255 indicates that
* \note Valid value is from 0 up to 255. Normally 255 indicates that
* this tag contains no genre.
*/
void setGenreNumber(TagLib::uint i);
void setGenreNumber(unsigned int i);
/*!
* Sets the string handler that decides how the ID3v1 data will be

View File

@@ -137,7 +137,7 @@ void AttachedPictureFrame::parseFields(const ByteVector &data)
d->mimeType = readStringField(data, String::Latin1, &pos);
/* Now we need at least two more bytes available */
if (uint(pos) + 1 >= data.size()) {
if(static_cast<unsigned int>(pos) + 1 >= data.size()) {
debug("Truncated picture frame.");
return;
}

View File

@@ -0,0 +1,306 @@
/***************************************************************************
copyright : (C) 2013 by Lukas Krejci
email : krejclu6@fel.cvut.cz
***************************************************************************/
/***************************************************************************
* 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 <tbytevectorlist.h>
#include <tpropertymap.h>
#include <tdebug.h>
#include <stdio.h>
#include "chapterframe.h"
using namespace TagLib;
using namespace ID3v2;
class ChapterFrame::ChapterFramePrivate
{
public:
ChapterFramePrivate() :
tagHeader(0)
{
embeddedFrameList.setAutoDelete(true);
}
const ID3v2::Header *tagHeader;
ByteVector elementID;
unsigned int startTime;
unsigned int endTime;
unsigned int startOffset;
unsigned int endOffset;
FrameListMap embeddedFrameListMap;
FrameList embeddedFrameList;
};
////////////////////////////////////////////////////////////////////////////////
// public methods
////////////////////////////////////////////////////////////////////////////////
ChapterFrame::ChapterFrame(const ID3v2::Header *tagHeader, const ByteVector &data) :
ID3v2::Frame(data)
{
d = new ChapterFramePrivate;
d->tagHeader = tagHeader;
setData(data);
}
ChapterFrame::ChapterFrame(const ByteVector &elementID,
unsigned int startTime, unsigned int endTime,
unsigned int startOffset, unsigned int endOffset,
const FrameList &embeddedFrames) :
ID3v2::Frame("CHAP")
{
d = new ChapterFramePrivate;
// setElementID has a workaround for a previously silly API where you had to
// specifically include the null byte.
setElementID(elementID);
d->startTime = startTime;
d->endTime = endTime;
d->startOffset = startOffset;
d->endOffset = endOffset;
for(FrameList::ConstIterator it = embeddedFrames.begin();
it != embeddedFrames.end(); ++it)
addEmbeddedFrame(*it);
}
ChapterFrame::~ChapterFrame()
{
delete d;
}
ByteVector ChapterFrame::elementID() const
{
return d->elementID;
}
unsigned int ChapterFrame::startTime() const
{
return d->startTime;
}
unsigned int ChapterFrame::endTime() const
{
return d->endTime;
}
unsigned int ChapterFrame::startOffset() const
{
return d->startOffset;
}
unsigned int ChapterFrame::endOffset() const
{
return d->endOffset;
}
void ChapterFrame::setElementID(const ByteVector &eID)
{
d->elementID = eID;
if(d->elementID.endsWith(char(0)))
d->elementID = d->elementID.mid(0, d->elementID.size() - 1);
}
void ChapterFrame::setStartTime(const unsigned int &sT)
{
d->startTime = sT;
}
void ChapterFrame::setEndTime(const unsigned int &eT)
{
d->endTime = eT;
}
void ChapterFrame::setStartOffset(const unsigned int &sO)
{
d->startOffset = sO;
}
void ChapterFrame::setEndOffset(const unsigned int &eO)
{
d->endOffset = eO;
}
const FrameListMap &ChapterFrame::embeddedFrameListMap() const
{
return d->embeddedFrameListMap;
}
const FrameList &ChapterFrame::embeddedFrameList() const
{
return d->embeddedFrameList;
}
const FrameList &ChapterFrame::embeddedFrameList(const ByteVector &frameID) const
{
return d->embeddedFrameListMap[frameID];
}
void ChapterFrame::addEmbeddedFrame(Frame *frame)
{
d->embeddedFrameList.append(frame);
d->embeddedFrameListMap[frame->frameID()].append(frame);
}
void ChapterFrame::removeEmbeddedFrame(Frame *frame, bool del)
{
// remove the frame from the frame list
FrameList::Iterator it = d->embeddedFrameList.find(frame);
d->embeddedFrameList.erase(it);
// ...and from the frame list map
it = d->embeddedFrameListMap[frame->frameID()].find(frame);
d->embeddedFrameListMap[frame->frameID()].erase(it);
// ...and delete as desired
if(del)
delete frame;
}
void ChapterFrame::removeEmbeddedFrames(const ByteVector &id)
{
FrameList l = d->embeddedFrameListMap[id];
for(FrameList::ConstIterator it = l.begin(); it != l.end(); ++it)
removeEmbeddedFrame(*it, true);
}
String ChapterFrame::toString() const
{
String s = String(d->elementID) +
": start time: " + String::number(d->startTime) +
", end time: " + String::number(d->endTime);
if(d->startOffset != 0xFFFFFFFF)
s += ", start offset: " + String::number(d->startOffset);
if(d->endOffset != 0xFFFFFFFF)
s += ", end offset: " + String::number(d->endOffset);
if(!d->embeddedFrameList.isEmpty()) {
StringList frameIDs;
for(FrameList::ConstIterator it = d->embeddedFrameList.begin();
it != d->embeddedFrameList.end(); ++it)
frameIDs.append((*it)->frameID());
s += ", sub-frames: [ " + frameIDs.toString(", ") + " ]";
}
return s;
}
PropertyMap ChapterFrame::asProperties() const
{
PropertyMap map;
map.unsupportedData().append(frameID() + String("/") + d->elementID);
return map;
}
ChapterFrame *ChapterFrame::findByElementID(const ID3v2::Tag *tag, const ByteVector &eID) // static
{
ID3v2::FrameList comments = tag->frameList("CHAP");
for(ID3v2::FrameList::ConstIterator it = comments.begin();
it != comments.end();
++it)
{
ChapterFrame *frame = dynamic_cast<ChapterFrame *>(*it);
if(frame && frame->elementID() == eID)
return frame;
}
return 0;
}
void ChapterFrame::parseFields(const ByteVector &data)
{
unsigned int size = data.size();
if(size < 18) {
debug("A CHAP frame must contain at least 18 bytes (1 byte element ID "
"terminated by null and 4x4 bytes for start and end time and offset).");
return;
}
int pos = 0;
unsigned int embPos = 0;
d->elementID = readStringField(data, String::Latin1, &pos).data(String::Latin1);
d->startTime = data.toUInt(pos, true);
pos += 4;
d->endTime = data.toUInt(pos, true);
pos += 4;
d->startOffset = data.toUInt(pos, true);
pos += 4;
d->endOffset = data.toUInt(pos, true);
pos += 4;
size -= pos;
// Embedded frames are optional
if(size < header()->size())
return;
while(embPos < size - header()->size()) {
Frame *frame = FrameFactory::instance()->createFrame(data.mid(pos + embPos), (d->tagHeader != 0));
if(!frame)
return;
// Checks to make sure that frame parsed correctly.
if(frame->size() <= 0) {
delete frame;
return;
}
embPos += frame->size() + header()->size();
addEmbeddedFrame(frame);
}
}
ByteVector ChapterFrame::renderFields() const
{
ByteVector data;
data.append(d->elementID);
data.append('\0');
data.append(ByteVector::fromUInt(d->startTime, true));
data.append(ByteVector::fromUInt(d->endTime, true));
data.append(ByteVector::fromUInt(d->startOffset, true));
data.append(ByteVector::fromUInt(d->endOffset, true));
FrameList l = d->embeddedFrameList;
for(FrameList::ConstIterator it = l.begin(); it != l.end(); ++it)
data.append((*it)->render());
return data;
}
ChapterFrame::ChapterFrame(const ID3v2::Header *tagHeader, const ByteVector &data, Header *h) :
Frame(h)
{
d = new ChapterFramePrivate;
d->tagHeader = tagHeader;
parseFields(fieldData(data));
}

View File

@@ -0,0 +1,249 @@
/***************************************************************************
copyright : (C) 2013 by Lukas Krejci
email : krejclu6@fel.cvut.cz
***************************************************************************/
/***************************************************************************
* 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_CHAPTERFRAME
#define TAGLIB_CHAPTERFRAME
#include "id3v2tag.h"
#include "id3v2frame.h"
#include "taglib_export.h"
namespace TagLib {
namespace ID3v2 {
/*!
* This is an implementation of ID3v2 chapter frames. The purpose of this
* frame is to describe a single chapter within an audio file.
*/
//! An implementation of ID3v2 chapter frames
class TAGLIB_EXPORT ChapterFrame : public ID3v2::Frame
{
friend class FrameFactory;
public:
/*!
* Creates a chapter frame based on \a data. \a tagHeader is required as
* the internal frames are parsed based on the tag version.
*/
ChapterFrame(const ID3v2::Header *tagHeader, const ByteVector &data);
/*!
* Creates a chapter frame with the element ID \a elementID, start time
* \a startTime, end time \a endTime, start offset \a startOffset,
* end offset \a endOffset and optionally a list of embedded frames,
* whose ownership will then be taken over by this Frame, in
* \a embeededFrames;
*
* All times are in milliseconds.
*/
ChapterFrame(const ByteVector &elementID,
unsigned int startTime, unsigned int endTime,
unsigned int startOffset, unsigned int endOffset,
const FrameList &embeddedFrames = FrameList());
/*!
* Destroys the frame.
*/
virtual ~ChapterFrame();
/*!
* Returns the element ID of the frame. Element ID
* is a null terminated string, however it's not human-readable.
*
* \see setElementID()
*/
ByteVector elementID() const;
/*!
* Returns time of chapter's start (in milliseconds).
*
* \see setStartTime()
*/
unsigned int startTime() const;
/*!
* Returns time of chapter's end (in milliseconds).
*
* \see setEndTime()
*/
unsigned int endTime() const;
/*!
* Returns zero based byte offset (count of bytes from the beginning
* of the audio file) of chapter's start.
*
* \note If returned value is 0xFFFFFFFF, start time should be used instead.
* \see setStartOffset()
*/
unsigned int startOffset() const;
/*!
* Returns zero based byte offset (count of bytes from the beginning
* of the audio file) of chapter's end.
*
* \note If returned value is 0xFFFFFFFF, end time should be used instead.
* \see setEndOffset()
*/
unsigned int endOffset() const;
/*!
* Sets the element ID of the frame to \a eID. If \a eID isn't
* null terminated, a null char is appended automatically.
*
* \see elementID()
*/
void setElementID(const ByteVector &eID);
/*!
* Sets time of chapter's start (in milliseconds) to \a sT.
*
* \see startTime()
*/
void setStartTime(const unsigned int &sT);
/*!
* Sets time of chapter's end (in milliseconds) to \a eT.
*
* \see endTime()
*/
void setEndTime(const unsigned int &eT);
/*!
* Sets zero based byte offset (count of bytes from the beginning
* of the audio file) of chapter's start to \a sO.
*
* \see startOffset()
*/
void setStartOffset(const unsigned int &sO);
/*!
* Sets zero based byte offset (count of bytes from the beginning
* of the audio file) of chapter's end to \a eO.
*
* \see endOffset()
*/
void setEndOffset(const unsigned int &eO);
/*!
* Returns a reference to the frame list map. This is an FrameListMap of
* all of the frames embedded in the CHAP frame.
*
* This is the most convenient structure for accessing the CHAP frame's
* embedded frames. Many frame types allow multiple instances of the same
* frame type so this is a map of lists. In most cases however there will
* only be a single frame of a certain type.
*
* \warning You should not modify this data structure directly, instead
* use addEmbeddedFrame() and removeEmbeddedFrame().
*
* \see embeddedFrameList()
*/
const FrameListMap &embeddedFrameListMap() const;
/*!
* Returns a reference to the embedded frame list. This is an FrameList
* of all of the frames embedded in the CHAP frame in the order that they
* were parsed.
*
* This can be useful if for example you want iterate over the CHAP frame's
* embedded frames in the order that they occur in the CHAP frame.
*
* \warning You should not modify this data structure directly, instead
* use addEmbeddedFrame() and removeEmbeddedFrame().
*/
const FrameList &embeddedFrameList() const;
/*!
* Returns the embedded frame list for frames with the id \a frameID
* or an empty list if there are no embedded frames of that type. This
* is just a convenience and is equivalent to:
*
* \code
* embeddedFrameListMap()[frameID];
* \endcode
*
* \see embeddedFrameListMap()
*/
const FrameList &embeddedFrameList(const ByteVector &frameID) const;
/*!
* Add an embedded frame to the CHAP frame. At this point the CHAP frame
* takes ownership of the embedded frame and will handle freeing its memory.
*
* \note Using this method will invalidate any pointers on the list
* returned by embeddedFrameList()
*/
void addEmbeddedFrame(Frame *frame);
/*!
* Remove an embedded frame from the CHAP frame. If \a del is true the frame's
* memory will be freed; if it is false, it must be deleted by the user.
*
* \note Using this method will invalidate any pointers on the list
* returned by embeddedFrameList()
*/
void removeEmbeddedFrame(Frame *frame, bool del = true);
/*!
* Remove all embedded frames of type \a id from the CHAP frame and free their
* memory.
*
* \note Using this method will invalidate any pointers on the list
* returned by embeddedFrameList()
*/
void removeEmbeddedFrames(const ByteVector &id);
virtual String toString() const;
PropertyMap asProperties() const;
/*!
* CHAP frames each have a unique element ID. This searches for a CHAP
* frame with the element ID \a eID and returns a pointer to it. This
* can be used to link CTOC and CHAP frames together.
*
* \see elementID()
*/
static ChapterFrame *findByElementID(const Tag *tag, const ByteVector &eID);
protected:
virtual void parseFields(const ByteVector &data);
virtual ByteVector renderFields() const;
private:
ChapterFrame(const ID3v2::Header *tagHeader, const ByteVector &data, Header *h);
ChapterFrame(const ChapterFrame &);
ChapterFrame &operator=(const ChapterFrame &);
class ChapterFramePrivate;
ChapterFramePrivate *d;
};
}
}
#endif

View File

@@ -116,8 +116,6 @@ PropertyMap CommentsFrame::asProperties() const
PropertyMap map;
if(key.isEmpty() || key == "COMMENT")
map.insert("COMMENT", text());
else if(key.isNull())
map.unsupportedData().append(L"COMM/" + description());
else
map.insert("COMMENT:" + key, text());
return map;
@@ -164,7 +162,7 @@ void CommentsFrame::parseFields(const ByteVector &data)
} else {
d->description = String(l.front(), d->textEncoding);
d->text = String(l.back(), d->textEncoding);
}
}
}
}

View File

@@ -36,7 +36,7 @@ namespace TagLib {
//! An implementation of ID3v2 comments
/*!
* This implements the ID3v2 comment format. An ID3v2 comment concists of
* This implements the ID3v2 comment format. An ID3v2 comment consists of
* a language encoding, a description and a single text field.
*/
@@ -106,7 +106,7 @@ namespace TagLib {
/*!
* Sets the description of the comment to \a s.
*
* \see decription()
* \see description()
*/
void setDescription(const String &s);
@@ -149,7 +149,7 @@ namespace TagLib {
/*!
* Comments each have a unique description. This searches for a comment
* frame with the decription \a d and returns a pointer to it. If no
* frame with the description \a d and returns a pointer to it. If no
* frame is found that matches the given description null is returned.
*
* \see description()

View File

@@ -0,0 +1,144 @@
/***************************************************************************
copyright : (C) 2014 by Urs Fleisch
email : ufleisch@users.sourceforge.net
***************************************************************************/
/***************************************************************************
* This library is free software; you can redistribute it and/or modify *
* it under the terms of the GNU Lesser General Public License version *
* 2.1 as published by the Free Software Foundation. *
* *
* This library is distributed in the hope that it will be useful, but *
* WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
* Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public *
* License along with this library; if not, write to the Free Software *
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
* 02110-1301 USA *
* *
* Alternatively, this file is available under the Mozilla Public *
* License Version 1.1. You may obtain a copy of the License at *
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#include "eventtimingcodesframe.h"
#include <tbytevectorlist.h>
#include <id3v2tag.h>
#include <tdebug.h>
#include <tpropertymap.h>
using namespace TagLib;
using namespace ID3v2;
class EventTimingCodesFrame::EventTimingCodesFramePrivate
{
public:
EventTimingCodesFramePrivate() :
timestampFormat(EventTimingCodesFrame::AbsoluteMilliseconds) {}
EventTimingCodesFrame::TimestampFormat timestampFormat;
EventTimingCodesFrame::SynchedEventList synchedEvents;
};
////////////////////////////////////////////////////////////////////////////////
// public members
////////////////////////////////////////////////////////////////////////////////
EventTimingCodesFrame::EventTimingCodesFrame() :
Frame("ETCO")
{
d = new EventTimingCodesFramePrivate;
}
EventTimingCodesFrame::EventTimingCodesFrame(const ByteVector &data) :
Frame(data)
{
d = new EventTimingCodesFramePrivate;
setData(data);
}
EventTimingCodesFrame::~EventTimingCodesFrame()
{
delete d;
}
String EventTimingCodesFrame::toString() const
{
return String();
}
EventTimingCodesFrame::TimestampFormat
EventTimingCodesFrame::timestampFormat() const
{
return d->timestampFormat;
}
EventTimingCodesFrame::SynchedEventList
EventTimingCodesFrame::synchedEvents() const
{
return d->synchedEvents;
}
void EventTimingCodesFrame::setTimestampFormat(
EventTimingCodesFrame::TimestampFormat f)
{
d->timestampFormat = f;
}
void EventTimingCodesFrame::setSynchedEvents(
const EventTimingCodesFrame::SynchedEventList &e)
{
d->synchedEvents = e;
}
////////////////////////////////////////////////////////////////////////////////
// protected members
////////////////////////////////////////////////////////////////////////////////
void EventTimingCodesFrame::parseFields(const ByteVector &data)
{
const int end = data.size();
if(end < 1) {
debug("An event timing codes frame must contain at least 1 byte.");
return;
}
d->timestampFormat = TimestampFormat(data[0]);
int pos = 1;
d->synchedEvents.clear();
while(pos + 4 < end) {
EventType type = static_cast<EventType>(static_cast<unsigned char>(data[pos++]));
unsigned int time = data.toUInt(pos, true);
pos += 4;
d->synchedEvents.append(SynchedEvent(time, type));
}
}
ByteVector EventTimingCodesFrame::renderFields() const
{
ByteVector v;
v.append(char(d->timestampFormat));
for(SynchedEventList::ConstIterator it = d->synchedEvents.begin();
it != d->synchedEvents.end();
++it) {
const SynchedEvent &entry = *it;
v.append(char(entry.type));
v.append(ByteVector::fromUInt(entry.time));
}
return v;
}
////////////////////////////////////////////////////////////////////////////////
// private members
////////////////////////////////////////////////////////////////////////////////
EventTimingCodesFrame::EventTimingCodesFrame(const ByteVector &data, Header *h)
: Frame(h)
{
d = new EventTimingCodesFramePrivate();
parseFields(fieldData(data));
}

View File

@@ -0,0 +1,185 @@
/***************************************************************************
copyright : (C) 2014 by Urs Fleisch
email : ufleisch@users.sourceforge.net
***************************************************************************/
/***************************************************************************
* This library is free software; you can redistribute it and/or modify *
* it under the terms of the GNU Lesser General Public License version *
* 2.1 as published by the Free Software Foundation. *
* *
* This library is distributed in the hope that it will be useful, but *
* WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
* Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public *
* License along with this library; if not, write to the Free Software *
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
* 02110-1301 USA *
* *
* Alternatively, this file is available under the Mozilla Public *
* License Version 1.1. You may obtain a copy of the License at *
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#ifndef TAGLIB_EVENTTIMINGCODESFRAME_H
#define TAGLIB_EVENTTIMINGCODESFRAME_H
#include "id3v2frame.h"
#include "tlist.h"
namespace TagLib {
namespace ID3v2 {
//! ID3v2 event timing codes frame
/*!
* An implementation of ID3v2 event timing codes.
*/
class TAGLIB_EXPORT EventTimingCodesFrame : public Frame
{
friend class FrameFactory;
public:
/*!
* Specifies the timestamp format used.
*/
enum TimestampFormat {
//! The timestamp is of unknown format.
Unknown = 0x00,
//! The timestamp represents the number of MPEG frames since
//! the beginning of the audio stream.
AbsoluteMpegFrames = 0x01,
//! The timestamp represents the number of milliseconds since
//! the beginning of the audio stream.
AbsoluteMilliseconds = 0x02
};
/*!
* Event types defined in id3v2.4.0-frames.txt 4.5. Event timing codes.
*/
enum EventType {
Padding = 0x00,
EndOfInitialSilence = 0x01,
IntroStart = 0x02,
MainPartStart = 0x03,
OutroStart = 0x04,
OutroEnd = 0x05,
VerseStart = 0x06,
RefrainStart = 0x07,
InterludeStart = 0x08,
ThemeStart = 0x09,
VariationStart = 0x0a,
KeyChange = 0x0b,
TimeChange = 0x0c,
MomentaryUnwantedNoise = 0x0d,
SustainedNoise = 0x0e,
SustainedNoiseEnd = 0x0f,
IntroEnd = 0x10,
MainPartEnd = 0x11,
VerseEnd = 0x12,
RefrainEnd = 0x13,
ThemeEnd = 0x14,
Profanity = 0x15,
ProfanityEnd = 0x16,
NotPredefinedSynch0 = 0xe0,
NotPredefinedSynch1 = 0xe1,
NotPredefinedSynch2 = 0xe2,
NotPredefinedSynch3 = 0xe3,
NotPredefinedSynch4 = 0xe4,
NotPredefinedSynch5 = 0xe5,
NotPredefinedSynch6 = 0xe6,
NotPredefinedSynch7 = 0xe7,
NotPredefinedSynch8 = 0xe8,
NotPredefinedSynch9 = 0xe9,
NotPredefinedSynchA = 0xea,
NotPredefinedSynchB = 0xeb,
NotPredefinedSynchC = 0xec,
NotPredefinedSynchD = 0xed,
NotPredefinedSynchE = 0xee,
NotPredefinedSynchF = 0xef,
AudioEnd = 0xfd,
AudioFileEnds = 0xfe
};
/*!
* Single entry of time stamp and event.
*/
struct SynchedEvent {
SynchedEvent(unsigned int ms, EventType t) : time(ms), type(t) {}
unsigned int time;
EventType type;
};
/*!
* List of synchronized events.
*/
typedef TagLib::List<SynchedEvent> SynchedEventList;
/*!
* Construct an empty event timing codes frame.
*/
explicit EventTimingCodesFrame();
/*!
* Construct a event timing codes frame based on the data in \a data.
*/
explicit EventTimingCodesFrame(const ByteVector &data);
/*!
* Destroys this EventTimingCodesFrame instance.
*/
virtual ~EventTimingCodesFrame();
/*!
* Returns a null string.
*/
virtual String toString() const;
/*!
* Returns the timestamp format.
*/
TimestampFormat timestampFormat() const;
/*!
* Returns the events with the time stamps.
*/
SynchedEventList synchedEvents() const;
/*!
* Set the timestamp format.
*
* \see timestampFormat()
*/
void setTimestampFormat(TimestampFormat f);
/*!
* Sets the text with the time stamps.
*
* \see text()
*/
void setSynchedEvents(const SynchedEventList &e);
protected:
// Reimplementations.
virtual void parseFields(const ByteVector &data);
virtual ByteVector renderFields() const;
private:
/*!
* The constructor used by the FrameFactory.
*/
EventTimingCodesFrame(const ByteVector &data, Header *h);
EventTimingCodesFrame(const EventTimingCodesFrame &);
EventTimingCodesFrame &operator=(const EventTimingCodesFrame &);
class EventTimingCodesFramePrivate;
EventTimingCodesFramePrivate *d;
};
}
}
#endif

View File

@@ -1,6 +1,7 @@
/***************************************************************************
copyright : (C) 2002 - 2008 by Scott Wheeler
email : wheeler@kde.org
copyright : (C) 2006 by Aaron VonderHaar
email : avh4@users.sourceforge.net
***************************************************************************/
@@ -26,6 +27,7 @@
***************************************************************************/
#include <tdebug.h>
#include <tstringlist.h>
#include "generalencapsulatedobjectframe.h"
@@ -151,15 +153,21 @@ void GeneralEncapsulatedObjectFrame::parseFields(const ByteVector &data)
ByteVector GeneralEncapsulatedObjectFrame::renderFields() const
{
StringList sl;
sl.append(d->fileName);
sl.append(d->description);
const String::Type encoding = checkTextEncoding(sl, d->textEncoding);
ByteVector data;
data.append(char(d->textEncoding));
data.append(char(encoding));
data.append(d->mimeType.data(String::Latin1));
data.append(textDelimiter(String::Latin1));
data.append(d->fileName.data(d->textEncoding));
data.append(textDelimiter(d->textEncoding));
data.append(d->description.data(d->textEncoding));
data.append(textDelimiter(d->textEncoding));
data.append(d->fileName.data(encoding));
data.append(textDelimiter(encoding));
data.append(d->description.data(encoding));
data.append(textDelimiter(encoding));
data.append(d->data);
return data;

View File

@@ -1,6 +1,7 @@
/***************************************************************************
copyright : (C) 2002 - 2008 by Scott Wheeler
email : wheeler@kde.org
copyright : (C) 2006 by Aaron VonderHaar
email : avh4@users.sourceforge.net
***************************************************************************/

View File

@@ -24,9 +24,10 @@
***************************************************************************/
#include <tdebug.h>
#include <tstringlist.h>
#include <id3v2tag.h>
#include "ownershipframe.h"
#include <id3v2tag.h>
using namespace TagLib;
using namespace ID3v2;
@@ -113,24 +114,24 @@ void OwnershipFrame::setTextEncoding(String::Type encoding)
void OwnershipFrame::parseFields(const ByteVector &data)
{
int pos = 0;
// Get the text encoding
d->textEncoding = String::Type(data[0]);
pos += 1;
// Read the price paid this is a null terminate string
d->pricePaid = readStringField(data, String::Latin1, &pos);
// If we don't have at least 8 bytes left then don't parse the rest of the
// data
if(data.size() - pos < 8) {
return;
}
// Read the date purchased YYYYMMDD
d->datePurchased = String(data.mid(pos, 8));
pos += 8;
// Read the seller
if(d->textEncoding == String::Latin1)
d->seller = Tag::latin1StringHandler()->parse(data.mid(pos));
@@ -140,14 +141,19 @@ void OwnershipFrame::parseFields(const ByteVector &data)
ByteVector OwnershipFrame::renderFields() const
{
StringList sl;
sl.append(d->seller);
const String::Type encoding = checkTextEncoding(sl, d->textEncoding);
ByteVector v;
v.append(char(d->textEncoding));
v.append(char(encoding));
v.append(d->pricePaid.data(String::Latin1));
v.append(textDelimiter(String::Latin1));
v.append(d->datePurchased.data(String::Latin1));
v.append(d->seller.data(d->textEncoding));
v.append(d->seller.data(encoding));
return v;
}

View File

@@ -94,21 +94,21 @@ namespace TagLib {
* \see pricePaid()
*/
void setPricePaid(const String &pricePaid);
/*!
* Returns the seller.
*
* \see setSeller()
*/
String seller() const;
/*!
* Set the seller.
*
* \see seller()
*/
void setSeller(const String &seller);
/*!
* Returns the text encoding that will be used in rendering this frame.
* This defaults to the type that was either specified in the constructor
@@ -118,7 +118,7 @@ namespace TagLib {
* \see render()
*/
String::Type textEncoding() const;
/*!
* Sets the text encoding to be used when rendering this frame to
* \a encoding.

View File

@@ -0,0 +1,79 @@
/***************************************************************************
copyright : (C) 2015 by Urs Fleisch
email : ufleisch@users.sourceforge.net
***************************************************************************/
/***************************************************************************
* This library is free software; you can redistribute it and/or modify *
* it under the terms of the GNU Lesser General Public License version *
* 2.1 as published by the Free Software Foundation. *
* *
* This library is distributed in the hope that it will be useful, but *
* WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
* Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public *
* License along with this library; if not, write to the Free Software *
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
* 02110-1301 USA *
* *
* Alternatively, this file is available under the Mozilla Public *
* License Version 1.1. You may obtain a copy of the License at *
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#include "podcastframe.h"
using namespace TagLib;
using namespace ID3v2;
class PodcastFrame::PodcastFramePrivate
{
public:
ByteVector fieldData;
};
////////////////////////////////////////////////////////////////////////////////
// public members
////////////////////////////////////////////////////////////////////////////////
PodcastFrame::PodcastFrame() : Frame("PCST")
{
d = new PodcastFramePrivate;
d->fieldData = ByteVector(4, '\0');
}
PodcastFrame::~PodcastFrame()
{
delete d;
}
String PodcastFrame::toString() const
{
return String();
}
////////////////////////////////////////////////////////////////////////////////
// protected members
////////////////////////////////////////////////////////////////////////////////
void PodcastFrame::parseFields(const ByteVector &data)
{
d->fieldData = data;
}
ByteVector PodcastFrame::renderFields() const
{
return d->fieldData;
}
////////////////////////////////////////////////////////////////////////////////
// private members
////////////////////////////////////////////////////////////////////////////////
PodcastFrame::PodcastFrame(const ByteVector &data, Header *h) : Frame(h)
{
d = new PodcastFramePrivate;
parseFields(fieldData(data));
}

View File

@@ -0,0 +1,80 @@
/***************************************************************************
copyright : (C) 2015 by Urs Fleisch
email : ufleisch@users.sourceforge.net
***************************************************************************/
/***************************************************************************
* This library is free software; you can redistribute it and/or modify *
* it under the terms of the GNU Lesser General Public License version *
* 2.1 as published by the Free Software Foundation. *
* *
* This library is distributed in the hope that it will be useful, but *
* WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
* Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public *
* License along with this library; if not, write to the Free Software *
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
* 02110-1301 USA *
* *
* Alternatively, this file is available under the Mozilla Public *
* License Version 1.1. You may obtain a copy of the License at *
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#ifndef TAGLIB_PODCASTFRAME_H
#define TAGLIB_PODCASTFRAME_H
#include "id3v2frame.h"
#include "taglib_export.h"
namespace TagLib {
namespace ID3v2 {
//! ID3v2 podcast frame
/*!
* An implementation of ID3v2 podcast flag, a frame with four zero bytes.
*/
class TAGLIB_EXPORT PodcastFrame : public Frame
{
friend class FrameFactory;
public:
/*!
* Construct a podcast frame.
*/
PodcastFrame();
/*!
* Destroys this PodcastFrame instance.
*/
virtual ~PodcastFrame();
/*!
* Returns a null string.
*/
virtual String toString() const;
protected:
// Reimplementations.
virtual void parseFields(const ByteVector &data);
virtual ByteVector renderFields() const;
private:
/*!
* The constructor used by the FrameFactory.
*/
PodcastFrame(const ByteVector &data, Header *h);
PodcastFrame(const PodcastFrame &);
PodcastFrame &operator=(const PodcastFrame &);
class PodcastFramePrivate;
PodcastFramePrivate *d;
};
}
}
#endif

View File

@@ -36,7 +36,7 @@ public:
PopularimeterFramePrivate() : rating(0), counter(0) {}
String email;
int rating;
TagLib::uint counter;
unsigned int counter;
};
////////////////////////////////////////////////////////////////////////////////
@@ -84,12 +84,12 @@ void PopularimeterFrame::setRating(int s)
d->rating = s;
}
TagLib::uint PopularimeterFrame::counter() const
unsigned int PopularimeterFrame::counter() const
{
return d->counter;
}
void PopularimeterFrame::setCounter(TagLib::uint s)
void PopularimeterFrame::setCounter(unsigned int s)
{
d->counter = s;
}
@@ -109,7 +109,7 @@ void PopularimeterFrame::parseFields(const ByteVector &data)
if(pos < size) {
d->rating = (unsigned char)(data[pos++]);
if(pos < size) {
d->counter = data.toUInt(static_cast<uint>(pos));
d->counter = data.toUInt(static_cast<unsigned int>(pos));
}
}
}

View File

@@ -36,7 +36,7 @@ namespace TagLib {
//! An implementation of ID3v2 "popularimeter"
/*!
* This implements the ID3v2 popularimeter (POPM frame). It concists of
* This implements the ID3v2 popularimeter (POPM frame). It consists of
* an email, a rating and an optional counter.
*/
@@ -100,14 +100,14 @@ namespace TagLib {
*
* \see setCounter()
*/
uint counter() const;
unsigned int counter() const;
/*!
* Set the counter.
*
* \see counter()
*/
void setCounter(uint counter);
void setCounter(unsigned int counter);
protected:
// Reimplementations.

View File

@@ -31,11 +31,6 @@
using namespace TagLib;
using namespace ID3v2;
static inline int bitsToBytes(int i)
{
return i % 8 == 0 ? i / 8 : (i - i % 8) / 8 + 1;
}
struct ChannelData
{
ChannelData() : channelType(RelativeVolumeFrame::Other), volumeAdjustment(0) {}
@@ -185,19 +180,18 @@ void RelativeVolumeFrame::parseFields(const ByteVector &data)
while(pos <= (int)data.size() - 4) {
ChannelType type = ChannelType(data[pos]);
pos += 1;
ChannelData &channel = d->channels[type];
channel.volumeAdjustment = data.toShort(static_cast<uint>(pos));
channel.volumeAdjustment = data.toShort(static_cast<unsigned int>(pos));
pos += 2;
channel.peakVolume.bitsRepresentingPeak = data[pos];
pos += 1;
int bytes = bitsToBytes(channel.peakVolume.bitsRepresentingPeak);
const int bytes = (channel.peakVolume.bitsRepresentingPeak + 7) / 8;
channel.peakVolume.peakVolume = data.mid(pos, bytes);
pos += bytes;
}

View File

@@ -140,7 +140,7 @@ namespace TagLib {
/*
* There was a terrible API goof here, and while this can't be changed to
* the way it appears below for binary compaibility reasons, let's at
* the way it appears below for binary compatibility reasons, let's at
* least pretend that it looks clean.
*/
@@ -149,7 +149,7 @@ namespace TagLib {
/*!
* Returns the relative volume adjustment "index". As indicated by the
* ID3v2 standard this is a 16-bit signed integer that reflects the
* decibils of adjustment when divided by 512.
* decibels of adjustment when divided by 512.
*
* This defaults to returning the value for the master volume channel if
* available and returns 0 if the specified channel does not exist.
@@ -161,7 +161,7 @@ namespace TagLib {
/*!
* Set the volume adjustment to \a index. As indicated by the ID3v2
* standard this is a 16-bit signed integer that reflects the decibils of
* standard this is a 16-bit signed integer that reflects the decibels of
* adjustment when divided by 512.
*
* By default this sets the value for the master volume.

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