189 Commits

Author SHA1 Message Date
Lukáš Lalinský
aa34afda79 Merge remote-tracking branch 'TsudaKageyu/substr-bug' 2012-09-06 20:03:15 +02:00
Lukáš Lalinský
942ec58de5 Add tests for String::substr 2012-09-06 20:03:08 +02:00
Lukáš Lalinský
082a36147b Add change log, update SOVERSION 2012-09-06 19:57:10 +02:00
Lukáš Lalinský
f11b206fe8 Do not delete the IOStream object in TagLib::File 2012-09-06 19:43:52 +02:00
Lukáš Lalinský
e37f6ed752 Update CMakeLists.txt 2012-09-06 19:30:45 +02:00
Lukáš Lalinský
d2f20e8d2a Merge branch 'master' of https://github.com/moeeka/taglib 2012-09-06 19:24:22 +02:00
Rupert Daniel
f194a55c0f Updated OWNE implementaion with minor changes after pull review 2012-09-06 12:11:20 +01:00
Rupert Daniel
719187794e Implementation of the ID3v2.4 OWNE frame. 2012-09-05 16:37:46 +01:00
Tsuda Kageyu
74b94613a0 Bug fix in String::substr() 2012-09-05 19:46:52 +09:00
Lukáš Lalinský
33d0be110b Fix ambiguous reference to uint in tests 2012-09-03 19:55:55 +02:00
Tsuda Kageyu
df12b4ffc6 Refectored the FileStream constructor. 2012-09-03 01:45:28 +09:00
Tsuda Kageyu
d16c24ae21 Merge branch 'master' of https://github.com/taglib/taglib into security-warnings 2012-09-02 23:40:11 +09:00
Lukáš Lalinský
1c35918834 Merge remote-tracking branch 'TsudaKageyu/warnings' 2012-09-02 15:25:41 +02:00
Tsuda Kageyu
d163f36d35 Fix Visual C++ specific warnings about security 2012-08-23 21:57:12 +09:00
Tsuda Kageyu
590cd4c9f6 Fix warnings with VS2010 2012-08-23 20:54:18 +09:00
Tsuda Kageyu
6c0227ee13 Fix compilation errors with Visual Studio 2010 2012-08-23 19:58:21 +09:00
Lukáš Lalinský
9bb57fe0a7 Merge remote-tracking branch 'TsudaKageyu/id3v2-comment-update' 2012-08-23 11:00:32 +02:00
tsuda.kageyu@gmail.com
3fecdbf428 Comment update for ID3v2::Tag::setLatin1StringHandler() 2012-08-23 17:57:00 +09:00
Lukáš Lalinský
356c7a5d6e Merge remote-tracking branch 'TsudaKageyu/cross-border-delete' 2012-08-23 10:51:47 +02:00
tsuda.kageyu@gmail.com
4b4f70253b Comment update for ID3v1::Tag::setStringHandler() 2012-08-23 17:45:25 +09:00
Lukáš Lalinský
8b61a06fda Merge remote-tracking branch 'TsudaKageyu/id3v2-brokenlatin1-patch' 2012-08-23 10:22:43 +02:00
Lukáš Lalinský
6801ac2515 Merge remote-tracking branch 'poiru/master' 2012-08-23 10:19:08 +02:00
Lukáš Lalinský
29d17bb8e9 Merge remote-tracking branch 'supermihi/master' 2012-08-23 10:17:05 +02:00
tsuda.kageyu@gmail.com
fe8053c7d5 Support broken Latin-1 encodings in ID3V2 2012-08-23 12:19:51 +09:00
tsuda.kageyu@gmail.com
eb63ee8ec6 Remove possible cross binary boundary delete 2012-08-23 11:09:22 +09:00
Lukáš Lalinský
e86e5f906b ID3 frame IDs with 0 should be recognized as valid
http://article.gmane.org/gmane.comp.kde.devel.taglib/2275
2012-08-17 07:50:48 +02:00
Lukáš Lalinský
60e82e6694 Ignore additional files 2012-08-17 07:34:44 +02:00
Birunthan Mohanathas
fc6e02da35 Update header comments to reflect c4163a2 2012-08-02 17:25:20 +03:00
Michael Helmling
4140c5f2eb Check PropertyMap keys format-specifically instead of globally.
Instead of statically forbidding certain keys in PropertyMap, now the
setProperties() implementations of the different formats check if the
keys are valid for that particular specification and include them in
the returned PropertyMap otherwise.
This should remove an unneccessary complification for programmers since
now there's only one step, namely calling setProperties(), where
problems might occur.
Also the previous implementation leads to problems with invalid keys:
because taglib doesn't use exceptions, something like

  map.insert("FORBIDDEN KEY", "some value");

would lead to the value being inserted under String::null, which
smells like the source of strange bugs.
2012-07-30 20:52:30 +02:00
Michael Helmling
fc3fc10f60 add id3v2 frame delete test 2012-07-23 20:53:25 +02:00
BSDKaffee
3bc123aed6 - Disambiguate uint and ushort references 2012-07-15 01:57:53 -04:00
Lukáš Lalinský
3b392f2402 Changelog for 1.8 beta 2012-07-14 21:53:09 +02:00
Lukáš Lalinský
8ff0feb28e Cast to long 2012-07-14 21:21:03 +02:00
Lukáš Lalinský
61ed295af8 Merge remote-tracking branch 'poiru/master' 2012-07-14 21:09:54 +02:00
Lukáš Lalinský
09af2a7b57 Merge remote-tracking branch 'robinst/mpeg-file-id3vxtag-docs-inexistent-tags' 2012-07-14 21:08:59 +02:00
Lukáš Lalinský
050ff3835d Revert "Install examples if built."
This reverts commit 52e96e48c5.
2012-07-14 21:06:53 +02:00
Lukáš Lalinský
674ae0fa6d Merge remote-tracking branch 'kensington/master' 2012-07-14 21:06:08 +02:00
Lukáš Lalinský
411d318f34 Merge remote-tracking branch 'robinst/dont-copy-from-id3v1-when-only-saving-id3v2' 2012-07-14 21:03:16 +02:00
Lukáš Lalinský
fbb1c7e554 Merge branch 'mpc_sv8' 2012-07-11 14:15:37 +02:00
Lukáš Lalinský
fd818857e0 More Musepack tests 2012-07-11 14:15:12 +02:00
Lukáš Lalinský
930168f990 Refactoring of the Musepack SV8 properties code 2012-07-11 14:13:41 +02:00
Alex Novichkov
291d925fc1 MPC v8 audio properties 2012-07-11 13:08:10 +02:00
Scott Wheeler
7c4c455a40 Use '0' here instead of 'NULL'
0 is used throughout the rest of the TagLib source, and generally
preferred in C++:

http://www2.research.att.com/~bs/bs_faq2.html#null
2012-07-03 12:26:20 +02:00
Stephen F. Booth
f429d6f406 Merge pull request #41 from EliaCereda/master
Properly initialize frame
2012-06-30 07:33:25 -07:00
Elia Cereda
c68fe9ad7b Fixed error "Branch condition evaluates to a garbage value" (from Xcode 4.3.3 Static Analyzer, mpeg/id3v2/id3v2tag.cpp:377)
This error occurs when the frame variable is left uninitialized because its id doesn't match any of the ids in the if and else-id cases. Initializing it on declaration fixes this issue.
2012-06-30 16:25:33 +02:00
Stephen F. Booth
81aac3a590 Merge pull request #39 from robinst/install-build-tests-option
Mention -DBUILD_TESTS=on in "Unit Tests" of INSTALL
2012-06-11 04:57:04 -07:00
Robin Stocker
7279b4fb7b Don't duplicate from ID3v1 to ID3v2 when saving only ID3v2
When saving only v2 with stripOthers (which means stripping v1), the
data from v1 would still be duplicated to v2. Likewise for the other way
around.

This is not the expected outcome when e.g. a frame was removed in v2,
because it would be added again on save from the v1 data. The test shows
that.

This changes save to only duplicate the data when the other tag type
will not be stripped.
2012-06-10 18:53:25 +02:00
Robin Stocker
b0ac79c60e Mention -DBUILD_TESTS=on in "Unit Tests" of INSTALL 2012-06-10 18:26:06 +02:00
Robin Stocker
382aec46f7 Fix docs of MPEG::File::ID3v(1|2)Tag for inexistent tag
Since 37e2d629, the ID3v1 and ID3v2 tags are always created at the end
of MPEG::File::read. So contrary to what the documentation said, a null
pointer is never returned.

To check if a tag contains data, refer to isEmpty() in the
documentation.
2012-06-10 15:27:54 +02:00
Michael Palimaka
b5ad68d64b Add custom 'check' target that depends on the test_runner target for backwards compatibility. 2012-05-17 03:42:36 +10:00
Michael Palimaka
52e96e48c5 Install examples if built. 2012-05-16 04:37:25 +10:00
Michael Palimaka
5bcfecb6cc Use ctest instead of custom target. 2012-05-16 04:00:22 +10:00
Lukáš Lalinský
2c2a486313 Merge branch 'stable'
Conflicts:
	CMakeLists.txt
	NEWS
2012-04-20 18:30:07 +02:00
Lukáš Lalinský
059f2243b3 Prepare 1.7.2 release 2012-04-20 17:57:13 +02:00
Lukáš Lalinský
cce6ad46c9 Reverse the version check, similarly to what mp4v2 does 2012-04-20 17:52:12 +02:00
Birunthan Mohanathas
06597123b8 Remove trailing whitespace 2012-04-19 13:09:45 +03:00
Birunthan Mohanathas
e5ede410bc Tabs to spaces 2012-04-19 13:04:42 +03:00
Vinnie Falco
dafb3af742 Remove 'using namespace std' in tmap.h 2012-04-17 22:05:09 -07:00
Lukáš Lalinský
089643f115 Merge remote-tracking branch 'vinniefalco/rename_enums' 2012-04-17 08:49:02 +02:00
Vinnie Falco
26f458b87f Resolve scope resolution for APE::Footer definitions in apefooter.cpp 2012-04-15 07:58:50 -07:00
Vinnie Falco
c22791318c Resolve ambiguous File symbol in apetag.cpp 2012-04-15 07:57:02 -07:00
Vinnie Falco
5081e3cf4f Rename anonymous enumeration symbols to be unique trueaudiofile.cpp 2012-04-15 02:41:27 -07:00
Vinnie Falco
1bde4cea09 Rename anonymous enumeration symbols to be unique in wavpackfile.cpp 2012-04-15 02:40:46 -07:00
Vinnie Falco
0907e86a94 Rename anonymous enumeration symbols to be unique in apefile.cpp 2012-04-15 02:40:23 -07:00
Vinnie Falco
742a3a1dbb Rename anonymous enumeration symbols to be unique in mpcfile.cpp 2012-04-15 02:39:19 -07:00
Vinnie Falco
04a4a6b8d4 Rename anonymous enumeration symbols to be unique in flacfile.cpp 2012-04-15 02:38:15 -07:00
Jeff Mitchell
b216b448c5 Fix compilation 2012-04-12 18:41:40 -04:00
Stephen F. Booth
4f8a6fdfaf Verify that an APE text item isn't empty before use 2012-04-12 10:53:32 -04:00
Stephen F. Booth
32a4ac6599 Preserve source code backward compatibility 2012-04-10 18:06:58 -04:00
Stephen F. Booth
ca26a9ad3e Added support for APE tag binary items 2012-04-10 07:24:12 -04:00
Stephen F. Booth
bd03e352cc Allow tag items to be replaced 2012-04-07 08:54:22 -04:00
Stephen F. Booth
69ac59f5f0 Added sampleFrames() to audio properties 2012-04-06 18:30:13 -04:00
Stephen F. Booth
2297a6d531 Added missing tmap.h 2012-04-06 18:29:56 -04:00
Lukáš Lalinský
2a4850f211 Merge branch 'stable'
Conflicts:
	CMakeLists.txt
2012-03-18 09:22:53 +01:00
scottmc
288c6e4a3f Include <iostream> instead of <ostream> to fix compilation on Haiku 2012-03-18 09:20:26 +01:00
Lukáš Lalinský
606edf8171 Increment the version number 2012-03-17 11:02:24 +01:00
Lukáš Lalinský
3c7b05a900 Merge branch 'stable'
Conflicts:
	NEWS
2012-03-17 10:59:34 +01:00
Lukáš Lalinský
009c43952f Changelog 2012-03-17 10:58:22 +01:00
Birunthan Mohnathas
9c1668f28b Fixed (huge) memory leak with ASF. 2012-03-17 10:48:57 +01:00
Lukáš Lalinský
8e67b40bdc Fix compilation errors 2012-03-17 10:45:52 +01:00
Lukáš Lalinský
76222cb1eb Merge branch 'master' of https://github.com/supermihi/taglib 2012-03-17 10:41:02 +01:00
Birunthan Mohnathas
138dfca682 Additional change to previous fix. 2012-03-12 22:13:58 +02:00
Birunthan Mohnathas
c4163a26e8 Fixed memory leak of FrameFactory singleton. 2012-03-12 18:56:08 +02:00
Birunthan Mohnathas
4496efe33b Fixed (huge) memory leak with ASF. 2012-03-12 18:44:08 +02:00
Lukáš Lalinský
3a760b060c Merge branch 'stable'
Conflicts:
	taglib/riff/aiff/aiffproperties.cpp
	taglib/trueaudio/trueaudioproperties.cpp
	tests/test_wav.cpp
2012-03-10 09:16:37 +01:00
Lukáš Lalinský
110cac8429 Avoid uint overflow in case the length + index is over UINT_MAX 2012-03-10 09:13:04 +01:00
Lukáš Lalinský
258ae751b5 Don't store the output of ByteVector::toUInt() in int, use uint instead 2012-03-10 09:12:32 +01:00
Lukáš Lalinský
df1d3e028e Make sure to not try dividing by zero 2012-03-10 09:12:19 +01:00
Stephen F. Booth
23c86cf27d Check if the header is TTA1 before parsing 2012-03-10 09:11:51 +01:00
Lukáš Lalinský
f59c3b67aa Detect RIFF files with invalid chunk sizes
The bug report has a WAVE file with zero-sized 'data' chunk, which causes
TagLib to iterate over the file, 8 bytes in each iteration. The new code
adds a check for the chunk name, which forces it to mark the file as
invalid if the chunk name doesn't contain ASCII characters.

https://bugs.kde.org/show_bug.cgi?id=283412
2012-03-10 09:06:55 +01:00
Stephen F. Booth
294cb22241 Don't crash when wav files have a 0 for bit per channel (sampleWidth)
I've seen this in a wav that has an audio format of MP3 (0x55)
2012-03-10 08:58:45 +01:00
Frank Lai
b7ec0d26ab Be more careful when parsing Vorbis Comments 2012-03-10 08:52:59 +01:00
Scott Wheeler
934ce51790 Don't lead the scanned blocks on save 2012-03-10 08:52:17 +01:00
Lukáš Lalinský
dcdf4fd954 Avoid uint overflow in case the length + index is over UINT_MAX 2012-03-10 08:46:20 +01:00
Lukáš Lalinský
ab8a0ee893 Don't store the output of ByteVector::toUInt() in int, use uint instead 2012-03-04 12:01:21 +01:00
Lukáš Lalinský
77d61c6eca Make sure to not try dividing by zero 2012-03-04 11:51:05 +01:00
Michael Helmling
f5a2518273 Fixed handling of UnknownFrames in ID3v2.
- If an unknown frame with id "XXXX" occurs, an entry
"UNKNOWN/XXXX" is added to unsupportedData().
The removeUnsupportedProperties() method in turn
removes all unknown frames with id "XXXX" if it
encounters a string "UNKNOWN/XXXX" in the given list.

- Implemented findByDescription() to UnsynchronizedLyricsFrame
in order to support removal of lyrics frames with unsupported
keys.

- Adapted id3v2 test case to new QuodLibet policy.
2012-02-26 19:21:57 +01:00
Michael Helmling
6e6d823992 Removed quodlibet special case handling 2012-02-26 18:38:03 +01:00
Michael Helmling
f859fcf82a Add support for Unknown TXXX frames. 2012-02-26 18:07:02 +01:00
Michael Helmling
37c87e0317 Fixed identation 2012-02-26 10:56:18 +01:00
Michael Helmling
0a3b998ca5 Fix USLT frame creation in Frame::createTextualFrame() 2012-02-26 10:43:08 +01:00
Michael Helmling
fa0656e3c6 remove Tests/Examples build from CMakeLists 2012-02-26 10:37:59 +01:00
Michael Helmling
b05c3161c7 Added ID3v2 PropertyMap interface documentation. 2012-02-25 19:11:31 +01:00
Michael Helmling
79670beca1 some cosmetic changes 2012-02-25 18:59:53 +01:00
Michael Helmling
9fd22023cd Merge remote-tracking branch 'official/master' 2012-02-25 18:51:30 +01:00
Michael Helmling
b8d5246f88 Moved APE test to correct place; added MOD tag test. 2012-02-25 18:46:19 +01:00
Michael Helmling
05b5e06928 added APE tag PropertyMap test 2012-02-25 18:32:00 +01:00
Michael Helmling
d28cc83fb4 Added another test for ID3v2 PropertyMap interface; fixed various bugs 2012-02-25 18:22:17 +01:00
Michael Helmling
495a028da3 removed debug messages 2012-02-19 15:15:25 +01:00
Michael Helmling
23d303a896 fixed bugs preventing tests from running 2012-02-19 15:13:31 +01:00
Michael Helmling
6c054af3ed Added some functions, started to fix bugs. 2012-02-19 12:15:28 +01:00
Michael Helmling
70c3264279 fixed tests 2012-02-15 22:09:28 +01:00
Michael Helmling
cfa5ac6569 Fixed id3v2 test 2012-02-15 21:56:02 +01:00
Michael Helmling
de51307de7 Added lots of missing includes 2012-02-15 21:54:19 +01:00
Michael Helmling
140f4a57e2 fixed lots of bugs found by 'make' 2012-02-14 22:11:30 +01:00
Michael Helmling
8a8e9b702c Ported xm. 2012-02-14 21:35:50 +01:00
Michael Helmling
d6215365a1 Ported wavpack. 2012-02-14 21:34:43 +01:00
Michael Helmling
2185d52f56 Ported trueaudio. 2012-02-14 21:32:36 +01:00
Michael Helmling
48aaaf8dc4 Ported s3m; removed old id3v2dicttools. 2012-02-14 21:29:30 +01:00
Michael Helmling
d2c43d7174 ID3 interface complete; vorbis done; wav done 2012-02-14 21:27:14 +01:00
Lukáš Lalinský
cdfb447042 Add explicitly declared default constructor to StringHandler 2012-02-04 21:22:52 +01:00
Lukáš Lalinský
2d00b690de Merge branch 'master' of https://github.com/poiru/taglib 2012-02-04 20:38:14 +01:00
Stephen F. Booth
51675f3399 Added sampleFrames to FLACProperties 2012-02-04 11:34:40 -05:00
Stephen F. Booth
fa662a23db Check if the header is TTA1 before parsing 2012-02-04 08:39:45 -05:00
Stephen F. Booth
dc628204c0 Added sampleFrames() for TTA files 2012-02-04 08:30:34 -05:00
Birunthan Mohanathas
9564956a7f Removed space. 2012-02-02 18:12:37 +02:00
Birunthan Mohnathas
1f2248d24b Additional change to previous fix. 2012-02-02 17:50:58 +02:00
Birunthan Mohnathas
06424598bb Fixed memory leak. 2012-02-02 15:03:41 +02:00
Stephen F. Booth
7b3f279294 Correctly handle non-integral bit depths 2012-01-30 22:31:15 -05:00
Michael Helmling
a8632f710f More progress in ID3 ... setProperties() will get messy :( 2012-01-22 22:06:24 +01:00
Michael Helmling
0c8e5bbec8 Implemented asProperties() in all relevant textual frames. 2012-01-22 17:08:02 +01:00
Michael Helmling
a5e45f196b Started to work on ID3v2. 2012-01-21 23:05:59 +01:00
Michael Helmling
e4d955d6ef Migration to new PropertyMap ... done ape to mod. 2012-01-21 14:52:24 +01:00
Michael Helmling
18ae797df4 Add unsupportedData() to PropertyMap, simplified [] behavior. 2012-01-17 18:09:30 +01:00
Michael Helmling
d11189b975 Basic implementation of a PropertyMap.
Implemented key/valuelist property map with
case-insensitive ASCII keys and StringList values.

Todo:
- subclass StringList to add flags indicating whether a value could
be written to the specific file format
- add member attribute indicating list of frames that could not be
parsed into the PropertyMap representation.
2012-01-16 22:37:30 +01:00
Michael Helmling
67d896e6a7 Implemented the most easy comments on the pull request. 2012-01-14 22:02:17 +01:00
Michael Helmling
ea41cd8903 Merge remote-tracking branch 'official/master' 2012-01-14 20:57:15 +01:00
Lukáš Lalinský
d904281c6b Make it possible to generate an XML report 2012-01-14 10:16:54 +01:00
Lukáš Lalinský
40bf52e70f Reverse the version check, similarly to what mp4v2 does 2012-01-10 17:18:20 +01:00
Lukáš Lalinský
b981b6dde7 Check also for the deprecated GIF type 2012-01-08 15:54:15 +01:00
Lukáš Lalinský
c237998983 Support non-UTF8 free-form atoms 2012-01-08 13:17:42 +01:00
Urs Fleisch
5ff810e98d Support for ID3v2.2 frames used by iTunes (TCP, TS2, TSA, TSC, TSP, TST)
https://bugs.kde.org/show_bug.cgi?id=290330
2012-01-05 16:33:29 +01:00
Michael Helmling
7875d02a8f Merge remote-tracking branch 'official/master' 2012-01-04 19:49:50 +01:00
Scott Wheeler
baafb3e290 Funny that there managed to be no operator!= for String for so long. 2012-01-01 16:01:18 +01:00
Michael Helmling
c4cef55158 Added tests and information about ignored id3 frames.
The ID3v2::toDict() function now has an optional
StringList* argument which will contain information
about frames that could not be converted to the dict
interface.
There are some dict tests for APE and FLAC now, and the
ID3v2 test was enlarged.
2012-01-01 14:42:48 +01:00
Michael Helmling
0eaf3a3fbd Implemented dict interface for more formats.
Now supported: MOD files (IT, MOD, S3M, XM), RIFF files
(AIFF, WAV), TrueAudio, WavPack.
2011-11-02 21:02:35 +01:00
Michael Helmling
292a377d1e Merge remote-tracking branch 'official/master' 2011-10-30 18:28:52 +01:00
Lukáš Lalinský
6ea8599313 Detect RIFF files with invalid chunk sizes
The bug report has a WAVE file with zero-sized 'data' chunk, which causes
TagLib to iterate over the file, 8 bytes in each iteration. The new code
adds a check for the chunk name, which forces it to mark the file as
invalid if the chunk name doesn't contain ASCII characters.

https://bugs.kde.org/show_bug.cgi?id=283412
2011-10-08 18:41:15 +02:00
Michael Helmling
772bc9f7c4 Further cleanup and simplification in id3v2dicttools 2011-09-12 21:52:11 +02:00
Michael Helmling
0c2ca20ec2 Restructured and simplified ID3v2Tag::fromDict(). 2011-09-11 22:07:49 +02:00
Michael Helmling
2d31075047 Splitted ID3v2Tag::toDict() into several functions.
This should simplify future transition to virtual functions.
2011-09-11 18:22:15 +02:00
Graham Perks
837c9ef288 Add cmake option for visibility=hidden 2011-09-09 10:17:54 -05:00
Michael Helmling
0356249368 Merge remote-tracking branch 'official/master' 2011-09-01 16:33:39 +02:00
Michael Helmling
5647b2e293 Made im/export functions nonvirtual. Added similar functions to File and
its subclasses. TagLib::File contains a bunch of dynamic_casts to call
the correct specializations.
2011-08-28 22:58:40 +02:00
Lukáš Lalinský
686bcf55a9 Add support for iPhone ringtones 2011-08-28 11:02:34 +02:00
Michael Helmling
fa8159a9d0 Added toDict and fromDict methods for APE tags. 2011-08-27 22:30:20 +02:00
Michael Helmling
58db919e43 More support for the unified dictionary interface.
Addded fromDict() function to ID3v2Tag. Added fromDict() and
toDict() functions to the TagUnion class (uses the first non-empty tag).
Added fromDict() and toDict() functions for the generic Tag class, only
handling common tags without duplicates. Addded preliminary mp3 test
case. Python3 bindings now available on my github site.
2011-08-27 01:18:21 +02:00
Michael Helmling
b262180857 Some preliminary work for unified dictionary tag interface support.
- toDict() and fromDict() for XiphComments
- toDict() for ID3v2 Tags
2011-08-26 21:48:40 +02:00
Tim De Baets
bec3875b94 Added removePicture() to FLAC::File 2011-08-16 01:57:01 +02:00
Urs Fleisch
ce53d13af1 Add suport for more MP4 metadata atoms
https://bugs.kde.org/show_bug.cgi?id=275784
2011-08-13 17:07:41 +02:00
Mathias Panzenböck
4868bb5690 ByteVector::replace: test shrinking 2011-08-06 19:43:17 +02:00
Jonathan Liu
7cc36db760 Use the default frame factory when it's necessary to parse ID3v2 tags in APE files
https://bugs.kde.org/show_bug.cgi?id=278773
2011-08-06 11:05:20 +02:00
Lukáš Lalinský
028f831417 Basic tests for ByteVector::replace() 2011-08-05 18:47:53 +02:00
Lukáš Lalinský
303af305db Merge remote branch 'panzi/master' 2011-08-05 18:35:24 +02:00
Johannes Pfau
22b57f4463 Add taglib_free function to C binding 2011-08-05 13:17:42 +02:00
Mathias Panzenböck
11c993e9f0 use DATA() macro 2011-08-01 15:33:27 +02:00
Mathias Panzenböck
3b14dc3e94 ByteVector::replace: forgot detach() and opt. when pattern not found 2011-08-01 15:14:58 +02:00
Mathias Panzenböck
ad7645f8e9 ByteVector::replace performance improvements 2011-08-01 04:13:55 +02:00
Lukáš Lalinský
fb2decb7de Reformatting 2011-07-28 19:16:32 +02:00
Lukáš Lalinský
3a837e7fc7 Reformatting 2011-07-28 19:06:35 +02:00
Lukáš Lalinský
0730076a0f Merge remote-tracking branch 'gperks/master'
Conflicts:
	taglib/asf/asfpicture.cpp
	taglib/it/itproperties.h
2011-07-28 19:03:46 +02:00
Lukáš Lalinský
364a840d83 Merge remote-tracking branch 'setosha/ASFPicture_warning_fix' 2011-07-28 18:53:57 +02:00
Scott Wheeler
e9cd383139 Merge pull request #14 from sbooth/master
Some non-PCM wave files can cause SIG_ARITHMETIC
2011-07-28 05:45:11 -07:00
Stephen F. Booth
a41b32bbb2 Don't crash when wav files have a 0 for bit per channel (sampleWidth)
I've seen this in a wav that has an audio format of MP3 (0x55)
2011-07-28 08:36:14 -04:00
Scott Wheeler
98d6b97798 Fix warning 2011-07-27 23:22:23 +02:00
Scott Wheeler
019fe4843f ByteVector works on chars, not unsigned chars, so there needs to be a cast before the comparison 2011-07-27 23:14:54 +02:00
Scott Wheeler
bb25953767 These methods can't be protected since they're called from IT::File
In general this code could use a fair bit of tidying up both stylistically and semantically.
2011-07-27 23:07:38 +02:00
Scott Wheeler
4795831b4a Fix spelling / formatting in comments. 2011-07-27 23:06:06 +02:00
Graham Perks
19484c059d Document ENABLE_STATIC_RUNTIME 2011-07-27 14:35:24 -05:00
Graham Perks
10ea76ff11 Add build option for Visual Studio to link with the static runtime (/MT vs /MD), pass -DENABLE_STATIC_RUNTIME=ON to cmake 2011-07-27 14:25:26 -05:00
Graham Perks
5f84bbf61a Correction to OS X build; include basic Windows build instructions 2011-07-27 10:54:33 -05:00
Graham Perks
8b647e5fa7 Fix for VS2010 which had been throwing out "cannot access protected member declared in class 'TagLib::Mod::Properties'" errors. 2011-07-26 21:45:32 -05:00
Graham Perks
0341079b92 Misc typo corrections 2011-07-26 21:45:28 -05:00
Graham Perks
1a53bfd71a Example cmake for OS X to build a static library 2011-07-26 11:12:27 -05:00
Graham Perks
6aa41d8180 Updated OS X build instructions 2011-07-25 17:56:42 -05:00
Scott Wheeler
101a624c46 Merge branch 'master' of github.com:taglib/taglib 2011-07-21 00:06:50 +02:00
Lukáš Lalinský
3b4e4357e6 Only include config.h if HAVE_CONFIG_H is defined 2011-07-20 22:06:28 +02:00
Scott Wheeler
3baf0a413d Missing const 2011-07-18 21:27:59 +02:00
Anton Sergunov
68c6a7da7a ASFPicture fix warning
class/struct thing

Signed-off-by: Anton Sergunov <setosha@gmail.com>
2011-07-01 16:12:04 +07:00
158 changed files with 4477 additions and 491 deletions

3
.gitignore vendored
View File

@@ -1,5 +1,7 @@
cmake_install.cmake
cmake_uninstall.cmake
Makefile
CTestTestfile.cmake
CMakeFiles/
*.so
*.so.*
@@ -16,6 +18,7 @@ CMakeFiles/
/config.h
/taglib.pc
/tests/test_runner
/tests/Testing
/taglib_config.h
/taglib-config
/bindings/c/taglib_c.pc

View File

@@ -9,6 +9,12 @@ if(ENABLE_STATIC)
else()
set(BUILD_SHARED_LIBS ON)
endif()
OPTION(ENABLE_STATIC_RUNTIME "Visual Studio, link with runtime statically" OFF)
option(VISIBILITY_HIDDEN "Build with -fvisibility=hidden" OFF)
if(VISIBILITY_HIDDEN)
add_definitions (-fvisibility=hidden)
endif()
option(BUILD_TESTS "Build the test suite" OFF)
option(BUILD_EXAMPLES "Build the examples" OFF)
@@ -34,9 +40,14 @@ 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)
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 "7")
set(TAGLIB_LIB_MINOR_VERSION "8")
set(TAGLIB_LIB_PATCH_VERSION "0")
set(TAGLIB_LIB_VERSION_STRING "${TAGLIB_LIB_MAJOR_VERSION}.${TAGLIB_LIB_MINOR_VERSION}.${TAGLIB_LIB_PATCH_VERSION}")
@@ -45,9 +56,9 @@ set(TAGLIB_LIB_VERSION_STRING "${TAGLIB_LIB_MAJOR_VERSION}.${TAGLIB_LIB_MINOR_VE
# 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 11)
set(TAGLIB_SOVERSION_CURRENT 13)
set(TAGLIB_SOVERSION_REVISION 0)
set(TAGLIB_SOVERSION_AGE 10)
set(TAGLIB_SOVERSION_AGE 12)
math(EXPR TAGLIB_SOVERSION_MAJOR "${TAGLIB_SOVERSION_CURRENT} - ${TAGLIB_SOVERSION_AGE}")
math(EXPR TAGLIB_SOVERSION_MINOR "${TAGLIB_SOVERSION_AGE}")
@@ -77,7 +88,10 @@ configure_file(taglib/taglib_config.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/taglib_c
add_subdirectory(taglib)
add_subdirectory(bindings)
add_subdirectory(tests)
if(BUILD_TESTS)
enable_testing()
add_subdirectory(tests)
endif(BUILD_TESTS)
add_subdirectory(examples)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/Doxyfile.cmake ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile)

37
INSTALL
View File

@@ -16,8 +16,8 @@ In order to build the included examples, use the BUILD_EXAMPLES option:
See http://www.cmake.org/cmake/help/runningcmake.html for generic help on
running CMake.
Mac OS X Framework
------------------
Mac OS X
--------
On Mac OS X, you might want to build a framework that can be easily integrated
into your application. If you set the BUILD_FRAMEWORK option on, it will compile
@@ -30,14 +30,41 @@ an Universal Binary framework with Mac OS X 10.4 as the deployment target:
-DCMAKE_CXX_COMPILER=/usr/bin/c++-4.0 \
-DCMAKE_OSX_SYSROOT=/Developer/SDKs/MacOSX10.4u.sdk/ \
-DCMAKE_OSX_DEPLOYMENT_TARGET=10.4 \
-DCMAKE_OSX_ARCHITECTURES="ppc;i368;x86_64"
-DCMAKE_OSX_ARCHITECTURES="ppc;i386;x86_64"
For a 10.6 Snow Leopard static library with both 32-bit and 64-bit code, use:
cmake -DCMAKE_BUILD_TYPE=Release \
-DCMAKE_OSX_DEPLOYMENT_TARGET=10.6 \
-DCMAKE_OSX_ARCHITECTURES="i386;x86_64" \
-DENABLE_STATIC=ON \
-DCMAKE_INSTALL_PREFIX="<folder you want to build to>"
After 'make', and 'make install', add libtag.a to your XCode project, and add
the include folder to the project's User Header Search Paths.
Windows
-------
For building a static library on Windows with Visual Studio 2010, cd to
the TagLib folder then:
cmake -DENABLE_STATIC=ON -DENABLE_STATIC_RUNTIME=ON -G "Visual Studio 10" ...
Including ENABLE_STATIC_RUNTIME=ON indicates you want TagLib built using the
static runtime library, rather than the DLL form of the runtime.
CMake will create a Visual Studio solution, taglib.sln that you can open and
build as normal.
Unit Tests
----------
If you want to run the test suite to make sure TagLib works properly on your
system, you need to have cppunit installed. The test suite has a custom target
in the build system, so you can run the tests using make:
system, you need to have cppunit installed. To build the tests, include
the option -DBUILD_TESTS=on when running cmake.
The test suite has a custom target in the build system, so you can run
the tests using make:
make check

50
NEWS
View File

@@ -1,9 +1,51 @@
TagLib 1.8 (In Development)
TagLib 1.8 (Sep 6, 2012)
==============================
1.8:
* Added support for OWNE ID3 frames.
* Changed key validation in the new PropertyMap API.
* ID3v1::Tag::setStringHandler will no londer delete the previous handler,
the caller is responsible for this.
* File objects will also no longer delete the passed IOStream objects. It
should be done in the caller code after the File object is no longer
used.
* Added ID3v2::Tag::setLatin1StringHandler for custom handling of
latin1-encoded text in ID3v2 frames.
* Fixed validation of ID3v2 frame IDs (IDs with '0' were ignored).
1.8 BETA:
* New API for accessing tags by name.
* New abstract I/O stream layer to allow custom I/O handlers.
* Support for writing ID3v2.3 tags.
* Support for various module file formats (MOD, S3M, IT, XM).
* Support for MP4 and ASF is now enabled by default.
* Started using atomic int operations for reference counting.
* Added methods for checking if WMA and MP4 files are DRM-protected.
* Added taglib_free to the C bindings.
* New method to allow removing pictures from FLAC files.
* Support for reading audio properties from ALAC and Musepack SV8 files.
* Added replay-gain information to Musepack audio properties.
* Support for APEv2 binary tags.
* Many AudioProperties subclasses now provide information about the total number of samples.
* Various small bug fixes.
TagLib 1.7.2 (Apr 20, 2012)
===========================
* Support for writing ID3v2.3 tags.
* Added methods for checking if WMA and MP4 files are DRM-protected.
* Started using atomic int operations for reference counting.
* Fixed division by zero while parsing corrupted MP4 files (CVE-2012-2396).
* Fixed compilation on Haiku.
TagLib 1.7.1 (Mar 17, 2012)
===========================
* Improved parsing of corrupted WMA, RIFF and OGG files.
* Fixed a memory leak in the WMA parser.
* Fixed a memory leak in the FLAC parser.
* Fixed a possible division by zero in the APE parser.
* Added detection of TTA2 files.
* Fixed saving of multiple identically named tags to Vorbis Comments.
TagLib 1.7 (Mar 11, 2011)
=========================

View File

@@ -58,6 +58,11 @@ void taglib_set_string_management_enabled(BOOL management)
stringManagementEnabled = bool(management);
}
void taglib_free(void* pointer)
{
free(pointer);
}
////////////////////////////////////////////////////////////////////////////////
// TagLib::File wrapper
////////////////////////////////////////////////////////////////////////////////
@@ -104,7 +109,7 @@ void taglib_file_free(TagLib_File *file)
BOOL taglib_file_is_valid(const TagLib_File *file)
{
return reinterpret_cast<const File *>(file)->isValid();
return reinterpret_cast<const File *>(file)->isValid();
}
TagLib_Tag *taglib_file_tag(const TagLib_File *file)

View File

@@ -79,6 +79,11 @@ TAGLIB_C_EXPORT void taglib_set_strings_unicode(BOOL unicode);
*/
TAGLIB_C_EXPORT void taglib_set_string_management_enabled(BOOL management);
/*!
* Explicitly free a string returned from TagLib
*/
TAGLIB_C_EXPORT void taglib_free(void* pointer);
/*******************************************************************************
* File API
******************************************************************************/
@@ -99,7 +104,7 @@ typedef enum {
/*!
* Creates a TagLib file based on \a filename. TagLib will try to guess the file
* type.
*
*
* \returns NULL if the file type cannot be determined or the file cannot
* be opened.
*/

View File

@@ -48,6 +48,7 @@ set(tag_HDRS
toolkit/tfilestream.h
toolkit/tmap.h
toolkit/tmap.tcc
toolkit/tpropertymap.h
mpeg/mpegfile.h
mpeg/mpegproperties.h
mpeg/mpegheader.h
@@ -64,6 +65,7 @@ set(tag_HDRS
mpeg/id3v2/frames/attachedpictureframe.h
mpeg/id3v2/frames/commentsframe.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
@@ -150,6 +152,7 @@ set(frames_SRCS
mpeg/id3v2/frames/attachedpictureframe.cpp
mpeg/id3v2/frames/commentsframe.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
@@ -275,6 +278,7 @@ set(toolkit_SRCS
toolkit/tfile.cpp
toolkit/tfilestream.cpp
toolkit/tdebug.cpp
toolkit/tpropertymap.cpp
toolkit/unicode.cpp
)

View File

@@ -36,6 +36,7 @@
#include <tdebug.h>
#include <tagunion.h>
#include <id3v1tag.h>
#include <tpropertymap.h>
#include "apefile.h"
@@ -46,7 +47,7 @@ using namespace TagLib;
namespace
{
enum { APEIndex, ID3v1Index };
enum { ApeAPEIndex, ApeID3v1Index };
}
class APE::File::FilePrivate
@@ -109,6 +110,33 @@ TagLib::Tag *APE::File::tag() const
return &d->tag;
}
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();
}
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);
}
PropertyMap APE::File::setProperties(const PropertyMap &properties)
{
if(d->hasAPE)
return d->tag.access<APE::Tag>(ApeAPEIndex, false)->setProperties(properties);
else if(d->hasID3v1)
return d->tag.access<ID3v1::Tag>(ApeID3v1Index, false)->setProperties(properties);
else
return d->tag.access<APE::Tag>(ApeAPEIndex, true)->setProperties(properties);
}
APE::Properties *APE::File::audioProperties() const
{
return d->properties;
@@ -185,23 +213,23 @@ bool APE::File::save()
ID3v1::Tag *APE::File::ID3v1Tag(bool create)
{
return d->tag.access<ID3v1::Tag>(ID3v1Index, create);
return d->tag.access<ID3v1::Tag>(ApeID3v1Index, create);
}
APE::Tag *APE::File::APETag(bool create)
{
return d->tag.access<APE::Tag>(APEIndex, create);
return d->tag.access<APE::Tag>(ApeAPEIndex, create);
}
void APE::File::strip(int tags)
{
if(tags & ID3v1) {
d->tag.set(ID3v1Index, 0);
d->tag.set(ApeID3v1Index, 0);
APETag(true);
}
if(tags & APE) {
d->tag.set(APEIndex, 0);
d->tag.set(ApeAPEIndex, 0);
if(!ID3v1Tag())
APETag(true);
@@ -219,7 +247,7 @@ void APE::File::read(bool readProperties, Properties::ReadStyle /* propertiesSty
d->ID3v1Location = findID3v1();
if(d->ID3v1Location >= 0) {
d->tag.set(ID3v1Index, new ID3v1::Tag(this, d->ID3v1Location));
d->tag.set(ApeID3v1Index, new ID3v1::Tag(this, d->ID3v1Location));
d->hasID3v1 = true;
}
@@ -228,7 +256,7 @@ void APE::File::read(bool readProperties, Properties::ReadStyle /* propertiesSty
d->APELocation = findAPE();
if(d->APELocation >= 0) {
d->tag.set(APEIndex, new APE::Tag(this, d->APELocation));
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;

View File

@@ -95,6 +95,9 @@ namespace TagLib {
* Contructs an WavPack file from \a file. If \a readProperties is true the
* file's audio properties will also be read using \a propertiesStyle. If
* false, \a propertiesStyle is ignored.
*
* \note 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,
Properties::ReadStyle propertiesStyle = Properties::Average);
@@ -110,6 +113,25 @@ namespace TagLib {
*/
virtual TagLib::Tag *tag() const;
/*!
* Implements the unified property interface -- export function.
* If the file contains both an APE and an ID3v1 tag, only APE
* will be converted to the PropertyMap.
*/
PropertyMap properties() const;
/*!
* Removes unsupported properties. Forwards to the actual Tag's
* removeUnsupportedProperties() function.
*/
void removeUnsupportedProperties(const StringList &properties);
/*!
* Implements the unified property interface -- import function.
* As for the export, only one tag is taken into account. If the file
* has no tag at all, APE will be created.
*/
PropertyMap setProperties(const PropertyMap &);
/*!
* Returns the APE::Properties for this file. If no audio properties
* were read then this will return a null pointer.

View File

@@ -24,7 +24,7 @@
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#include <ostream>
#include <iostream>
#include <bitset>
#include <tstring.h>
@@ -35,7 +35,7 @@
using namespace TagLib;
using namespace APE;
class Footer::FooterPrivate
class APE::Footer::FooterPrivate
{
public:
FooterPrivate() : version(0),
@@ -64,12 +64,12 @@ public:
// static members
////////////////////////////////////////////////////////////////////////////////
TagLib::uint Footer::size()
TagLib::uint APE::Footer::size()
{
return FooterPrivate::size;
}
ByteVector Footer::fileIdentifier()
ByteVector APE::Footer::fileIdentifier()
{
return ByteVector::fromCString("APETAGEX");
}
@@ -78,63 +78,63 @@ ByteVector Footer::fileIdentifier()
// public members
////////////////////////////////////////////////////////////////////////////////
Footer::Footer()
APE::Footer::Footer()
{
d = new FooterPrivate;
}
Footer::Footer(const ByteVector &data)
APE::Footer::Footer(const ByteVector &data)
{
d = new FooterPrivate;
parse(data);
}
Footer::~Footer()
APE::Footer::~Footer()
{
delete d;
}
TagLib::uint Footer::version() const
TagLib::uint APE::Footer::version() const
{
return d->version;
}
bool Footer::headerPresent() const
bool APE::Footer::headerPresent() const
{
return d->headerPresent;
}
bool Footer::footerPresent() const
bool APE::Footer::footerPresent() const
{
return d->footerPresent;
}
bool Footer::isHeader() const
bool APE::Footer::isHeader() const
{
return d->isHeader;
}
void Footer::setHeaderPresent(bool b) const
void APE::Footer::setHeaderPresent(bool b) const
{
d->headerPresent = b;
}
TagLib::uint Footer::itemCount() const
TagLib::uint APE::Footer::itemCount() const
{
return d->itemCount;
}
void Footer::setItemCount(uint s)
void APE::Footer::setItemCount(uint s)
{
d->itemCount = s;
}
TagLib::uint Footer::tagSize() const
TagLib::uint APE::Footer::tagSize() const
{
return d->tagSize;
}
TagLib::uint Footer::completeTagSize() const
TagLib::uint APE::Footer::completeTagSize() const
{
if(d->headerPresent)
return d->tagSize + d->size;
@@ -142,22 +142,22 @@ TagLib::uint Footer::completeTagSize() const
return d->tagSize;
}
void Footer::setTagSize(uint s)
void APE::Footer::setTagSize(uint s)
{
d->tagSize = s;
}
void Footer::setData(const ByteVector &data)
void APE::Footer::setData(const ByteVector &data)
{
parse(data);
}
ByteVector Footer::renderFooter() const
ByteVector APE::Footer::renderFooter() const
{
return render(false);
}
ByteVector Footer::renderHeader() const
ByteVector APE::Footer::renderHeader() const
{
if (!d->headerPresent) return ByteVector();
@@ -168,7 +168,7 @@ ByteVector Footer::renderHeader() const
// protected members
////////////////////////////////////////////////////////////////////////////////
void Footer::parse(const ByteVector &data)
void APE::Footer::parse(const ByteVector &data)
{
if(data.size() < size())
return;
@@ -197,7 +197,7 @@ void Footer::parse(const ByteVector &data)
}
ByteVector Footer::render(bool isHeader) const
ByteVector APE::Footer::render(bool isHeader) const
{
ByteVector v;

View File

@@ -62,6 +62,18 @@ APE::Item::Item(const String &key, const StringList &values)
d->text = values;
}
APE::Item::Item(const String &key, const ByteVector &value, bool binary)
{
d = new ItemPrivate;
d->key = key;
if(binary) {
d->type = Binary;
d->value = value;
}
else
d->text.append(value);
}
APE::Item::Item(const Item &item)
{
d = new ItemPrivate(*item.d);
@@ -104,6 +116,17 @@ String APE::Item::key() const
return d->key;
}
ByteVector APE::Item::binaryData() const
{
return d->value;
}
void APE::Item::setBinaryData(const ByteVector &value)
{
d->type = Binary;
d->value = value;
}
ByteVector APE::Item::value() const
{
// This seems incorrect as it won't be actually rendering the value to keep it
@@ -119,27 +142,50 @@ void APE::Item::setKey(const String &key)
void APE::Item::setValue(const String &value)
{
d->type = Text;
d->text = value;
}
void APE::Item::setValues(const StringList &value)
{
d->type = Text;
d->text = value;
}
void APE::Item::appendValue(const String &value)
{
d->type = Text;
d->text.append(value);
}
void APE::Item::appendValues(const StringList &values)
{
d->type = Text;
d->text.append(values);
}
int APE::Item::size() const
{
return 8 + d->key.size() + 1 + d->value.size();
// 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) {
case Text:
if(d->text.size()) {
StringList::ConstIterator it = d->text.begin();
result += it->data(String::UTF8).size();
it++;
for(; it != d->text.end(); ++it)
result += 1 + it->data(String::UTF8).size();
}
break;
case Binary:
case Locator:
result += d->value.size();
break;
}
return result;
}
StringList APE::Item::toStringList() const
@@ -161,12 +207,12 @@ bool APE::Item::isEmpty() const
{
switch(d->type) {
case Text:
case Binary:
if(d->text.isEmpty())
return true;
if(d->text.size() == 1 && d->text.front().isEmpty())
return true;
return false;
case Binary:
case Locator:
return d->value.isEmpty();
default:
@@ -193,7 +239,7 @@ void APE::Item::parse(const ByteVector &data)
setReadOnly(flags & 1);
setType(ItemTypes((flags >> 1) & 3));
if(int(d->type) < 2)
if(Text == d->type)
d->text = StringList(ByteVectorList::split(d->value, '\0'), String::UTF8);
}

View File

@@ -59,16 +59,22 @@ namespace TagLib {
Item();
/*!
* Constructs an item with \a key and \a value.
* Constructs a text item with \a key and \a value.
*/
// BIC: Remove this, StringList has a constructor from a single string
Item(const String &key, const String &value);
/*!
* Constructs an item with \a key and \a values.
* Constructs a text item with \a key and \a values.
*/
Item(const String &key, const StringList &values);
/*!
* Constructs an item with \a key and \a value.
* If \a binary is true a Binary item will be created, otherwise \a value will be interpreted as text
*/
Item(const String &key, const ByteVector &value, bool binary);
/*!
* Construct an item as a copy of \a item.
*/
@@ -91,12 +97,20 @@ namespace TagLib {
/*!
* Returns the binary value.
*
* \deprecated This will be removed in the next binary incompatible version
* as it is not kept in sync with the things that are set using setValue()
* and friends.
* If the item type is not \a Binary, the returned contents are undefined
*/
ByteVector binaryData() const;
/*!
* Set the binary value to \a value
* The item's type will also be set to \a Binary
*/
void setBinaryData(const ByteVector &value);
#ifndef DO_NOT_DOCUMENT
/* Remove in next binary incompatible release */
ByteVector value() const;
#endif
/*!
* Sets the key for the item to \a key.
@@ -104,14 +118,14 @@ namespace TagLib {
void setKey(const String &key);
/*!
* Sets the value of the item to \a value and clears any previous contents.
* Sets the text value of the item to \a value and clears any previous contents.
*
* \see toString()
*/
void setValue(const String &value);
/*!
* Sets the value of the item to the list of values in \a value and clears
* Sets the text value of the item to the list of values in \a value and clears
* any previous contents.
*
* \see toStringList()
@@ -119,14 +133,14 @@ namespace TagLib {
void setValues(const StringList &values);
/*!
* Appends \a value to create (or extend) the current list of values.
* Appends \a value to create (or extend) the current list of text values.
*
* \see toString()
*/
void appendValue(const String &value);
/*!
* Appends \a values to extend the current list of values.
* Appends \a values to extend the current list of text values.
*
* \see toStringList()
*/
@@ -143,14 +157,13 @@ namespace TagLib {
*/
String toString() const;
/*!
* \deprecated
* \see values
*/
#ifndef DO_NOT_DOCUMENT
/* Remove in next binary incompatible release */
StringList toStringList() const;
#endif
/*!
* Returns the list of values.
* Returns the list of text values.
*/
StringList values() const;

View File

@@ -46,6 +46,7 @@ public:
channels(0),
version(0),
bitsPerSample(0),
sampleFrames(0),
file(file),
streamLength(streamLength) {}
@@ -55,6 +56,7 @@ public:
int channels;
int version;
int bitsPerSample;
uint sampleFrames;
File *file;
long streamLength;
};
@@ -104,6 +106,11 @@ int APE::Properties::bitsPerSample() const
return d->bitsPerSample;
}
TagLib::uint APE::Properties::sampleFrames() const
{
return d->sampleFrames;
}
////////////////////////////////////////////////////////////////////////////////
// private members
////////////////////////////////////////////////////////////////////////////////
@@ -137,7 +144,7 @@ long APE::Properties::findDescriptor()
long ID3v2OriginalSize = 0;
bool hasID3v2 = false;
if(ID3v2Location >= 0) {
ID3v2::Tag tag(d->file, ID3v2Location, 0);
ID3v2::Tag tag(d->file, ID3v2Location);
ID3v2OriginalSize = tag.header()->completeTagSize();
if(tag.header()->tagSize() > 0)
hasID3v2 = true;
@@ -192,8 +199,8 @@ void APE::Properties::analyzeCurrent()
uint totalFrames = header.mid(12, 4).toUInt(false);
uint blocksPerFrame = header.mid(4, 4).toUInt(false);
uint finalFrameBlocks = header.mid(8, 4).toUInt(false);
uint totalBlocks = totalFrames > 0 ? (totalFrames - 1) * blocksPerFrame + finalFrameBlocks : 0;
d->length = totalBlocks / d->sampleRate;
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;
}

View File

@@ -71,6 +71,7 @@ namespace TagLib {
* Returns number of bits per sample.
*/
int bitsPerSample() const;
uint sampleFrames() const;
/*!
* Returns APE version.

View File

@@ -34,6 +34,7 @@
#include <tfile.h>
#include <tstring.h>
#include <tmap.h>
#include <tpropertymap.h>
#include "apetag.h"
#include "apefooter.h"
@@ -47,7 +48,7 @@ class APE::Tag::TagPrivate
public:
TagPrivate() : file(0), footerLocation(-1), tagLength(0) {}
File *file;
TagLib::File *file;
long footerLocation;
long tagLength;
@@ -65,7 +66,7 @@ APE::Tag::Tag() : TagLib::Tag()
d = new TagPrivate;
}
APE::Tag::Tag(File *file, long footerLocation) : TagLib::Tag()
APE::Tag::Tag(TagLib::File *file, long footerLocation) : TagLib::Tag()
{
d = new TagPrivate;
d->file = file;
@@ -174,6 +175,103 @@ 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" }};
PropertyMap APE::Tag::properties() const
{
PropertyMap properties;
ItemListMap::ConstIterator it = itemListMap().begin();
for(; it != itemListMap().end(); ++it) {
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())
properties.unsupportedData().append(it->first);
else {
// Some tags need to be handled specially
for(uint i = 0; i < keyConversionsSize; ++i)
if(tagName == keyConversions[i][1])
tagName = keyConversions[i][0];
properties[tagName].append(it->second.toStringList());
}
}
return properties;
}
void APE::Tag::removeUnsupportedProperties(const StringList &properties)
{
StringList::ConstIterator it = properties.begin();
for(; it != properties.end(); ++it)
removeItem(*it);
}
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)
if(properties.contains(keyConversions[i][0])) {
properties.insert(keyConversions[i][1], properties[keyConversions[i][0]]);
properties.erase(keyConversions[i][0]);
}
// first check if tags need to be removed completely
StringList toRemove;
ItemListMap::ConstIterator remIt = itemListMap().begin();
for(; remIt != itemListMap().end(); ++remIt) {
String key = remIt->first.upper();
// 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))
toRemove.append(remIt->first);
}
for (StringList::Iterator removeIt = toRemove.begin(); removeIt != toRemove.end(); removeIt++)
removeItem(*removeIt);
// now sync in the "forward direction"
PropertyMap::ConstIterator it = properties.begin();
PropertyMap invalid;
for(; it != properties.end(); ++it) {
const String &tagName = it->first;
if(!checkKey(tagName))
invalid.insert(it->first, it->second);
else if(!(itemListMap().contains(tagName)) || !(itemListMap()[tagName].values() == it->second)) {
if(it->second.size() == 0)
removeItem(tagName);
else {
StringList::ConstIterator valueIt = it->second.begin();
addValue(tagName, *valueIt, true);
++valueIt;
for(; valueIt != it->second.end(); ++valueIt)
addValue(tagName, *valueIt, false);
}
}
}
return invalid;
}
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;
}
APE::Footer *APE::Tag::footer() const
{
return &d->footer;
@@ -195,17 +293,31 @@ void APE::Tag::addValue(const String &key, const String &value, bool replace)
{
if(replace)
removeItem(key);
if(!value.isEmpty()) {
if(d->itemListMap.contains(key) || !replace)
d->itemListMap[key.upper()].appendValue(value);
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));
}
}
void APE::Tag::setData(const String &key, const ByteVector &value)
{
removeItem(key);
if(!key.isEmpty() && !value.isEmpty())
setItem(key, Item(key, value, true));
}
void APE::Tag::setItem(const String &key, const Item &item)
{
d->itemListMap.insert(key.upper(), item);
if(!key.isEmpty())
d->itemListMap.insert(key.upper(), item);
}
bool APE::Tag::isEmpty() const

View File

@@ -103,6 +103,37 @@ namespace TagLib {
virtual void setYear(uint i);
virtual void setTrack(uint i);
/*!
* Implements the unified tag dictionary interface -- export function.
* APE tags are perfectly compatible with the dictionary interface because they
* support both arbitrary tag names and multiple values. Currently only
* APE items of type *Text* are handled by the dictionary interface; all *Binary*
* and *Locator* items will be put into the unsupportedData list and can be
* deleted on request using removeUnsupportedProperties(). The same happens
* to Text items if their key is invalid for PropertyMap (which should actually
* never happen).
*
* The only conversion done by this export function is to rename the APE tags
* TRACK to TRACKNUMBER, YEAR to DATE, and ALBUM ARTIST to ALBUMARTIST, respectively,
* in order to be compliant with the names used in other formats.
*/
PropertyMap properties() const;
void removeUnsupportedProperties(const StringList &properties);
/*!
* Implements the unified tag dictionary interface -- import function. The same
* comments as for the export function apply; additionally note that the APE tag
* specification requires keys to have between 2 and 16 printable ASCII characters
* with the exception of the fixed strings "ID3", "TAG", "OGGS", and "MP+".
*/
PropertyMap setProperties(const PropertyMap &);
/*!
* Check if the given String is a valid APE tag key.
*/
static bool checkKey(const String&);
/*!
* Returns a pointer to the tag's footer.
*/
@@ -128,12 +159,19 @@ namespace TagLib {
void removeItem(const String &key);
/*!
* Adds to the item specified by \a key the data \a value. If \a replace
* Adds to the text item specified by \a key the data \a value. If \a replace
* is true, then all of the other values on the same key will be removed
* first.
* first. If a binary item exists for \a key it will be removed first.
*/
void addValue(const String &key, const String &value, bool replace = true);
/*!
* Set the binary data for the key specified by \a item to \a value
* This will convert the item to type \a Binary if it isn't already and
* all of the other values on the same key will be removed.
*/
void setData(const String &key, const ByteVector &value);
/*!
* Sets the \a key item to the value of \a item. If an item with the \a key is already
* present, it will be replaced.

View File

@@ -70,7 +70,7 @@ namespace TagLib
/*!
* Constructs an attribute with \a key and a BytesType \a value.
*/
Attribute(const ByteVector &value);
Attribute(const ByteVector &value);
/*!
* Constructs an attribute with \a key and a Picture \a value.

View File

@@ -142,11 +142,19 @@ class ASF::File::HeaderExtensionObject : public ASF::File::BaseObject
{
public:
List<ASF::File::BaseObject *> objects;
~HeaderExtensionObject();
ByteVector guid();
void parse(ASF::File *file, uint size);
ByteVector render(ASF::File *file);
};
ASF::File::HeaderExtensionObject::~HeaderExtensionObject()
{
for(unsigned int i = 0; i < objects.size(); i++) {
delete objects[i];
}
}
void ASF::File::BaseObject::parse(ASF::File *file, unsigned int size)
{
data.clear();
@@ -340,7 +348,7 @@ void ASF::File::HeaderExtensionObject::parse(ASF::File *file, uint /*size*/)
else {
obj = new UnknownObject(guid);
}
obj->parse(file, size);
obj->parse(file, (unsigned int)size);
objects.append(obj);
dataPos += size;
}
@@ -360,14 +368,14 @@ ByteVector ASF::File::HeaderExtensionObject::render(ASF::File *file)
// public members
////////////////////////////////////////////////////////////////////////////////
ASF::File::File(FileName file, bool readProperties, Properties::ReadStyle propertiesStyle)
ASF::File::File(FileName file, bool readProperties, Properties::ReadStyle propertiesStyle)
: TagLib::File(file)
{
d = new FilePrivate;
read(readProperties, propertiesStyle);
}
ASF::File::File(IOStream *stream, bool readProperties, Properties::ReadStyle propertiesStyle)
ASF::File::File(IOStream *stream, bool readProperties, Properties::ReadStyle propertiesStyle)
: TagLib::File(stream)
{
d = new FilePrivate;
@@ -527,7 +535,7 @@ bool ASF::File::save()
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, d->size);
insert(data, 0, (TagLib::ulong)d->size);
return true;
}

View File

@@ -54,6 +54,9 @@ namespace TagLib {
*
* \note In the current implementation, both \a readProperties and
* \a propertiesStyle are ignored.
*
* \note TagLib will *not* take ownership of the stream, the caller is
* responsible for deleting it after the File object.
*/
File(FileName file, bool readProperties = true, Properties::ReadStyle propertiesStyle = Properties::Average);
@@ -64,6 +67,9 @@ namespace TagLib {
*
* \note In the current implementation, both \a readProperties and
* \a propertiesStyle are ignored.
*
* \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, Properties::ReadStyle propertiesStyle = Properties::Average);

View File

@@ -35,7 +35,7 @@
using namespace TagLib;
class ASF::Picture::PicturePriavte : public RefCounter
class ASF::Picture::PicturePrivate : public RefCounter
{
public:
bool valid;
@@ -51,7 +51,7 @@ public:
ASF::Picture::Picture()
{
d = new PicturePriavte();
d = new PicturePrivate();
d->valid = true;
}

View File

@@ -208,8 +208,8 @@ namespace TagLib
friend class Attribute;
#endif
private:
struct PicturePriavte;
PicturePriavte *d;
class PicturePrivate;
PicturePrivate *d;
};
}
}

View File

@@ -56,7 +56,7 @@ ASF::Properties::Properties() : AudioProperties(AudioProperties::Average)
ASF::Properties::~Properties()
{
if(d)
delete d;
delete d;
}
int ASF::Properties::length() const
@@ -77,7 +77,7 @@ int ASF::Properties::sampleRate() const
int ASF::Properties::channels() const
{
return d->channels;
}
}
bool ASF::Properties::isEncrypted() const
{

View File

@@ -73,4 +73,4 @@ namespace TagLib {
}
#endif
#endif

View File

@@ -120,17 +120,17 @@ namespace TagLib {
virtual void setComment(const String &s);
/*!
* Sets the rating to \a s.
* Sets the rating to \a s.
*/
virtual void setRating(const String &s);
/*!
* Sets the copyright to \a s.
* Sets the copyright to \a s.
*/
virtual void setCopyright(const String &s);
/*!
* Sets the genre to \a s.
* Sets the genre to \a s.
*/
virtual void setGenre(const String &s);

View File

@@ -1,7 +1,7 @@
/***************************************************************************
copyright : (C) 2002 - 2008 by Scott Wheeler
email : wheeler@kde.org
copyright : (C) 2010 by Alex Novichkov
email : novichko@atnet.ru
(added APE file support)
@@ -152,6 +152,7 @@ StringList FileRef::defaultFileExtensions()
l.append("spx");
l.append("tta");
l.append("m4a");
l.append("m4r");
l.append("m4b");
l.append("m4p");
l.append("3g2");
@@ -253,7 +254,7 @@ File *FileRef::create(FileName fileName, bool readAudioProperties,
return new Ogg::Speex::File(fileName, readAudioProperties, audioPropertiesStyle);
if(ext == "TTA")
return new TrueAudio::File(fileName, readAudioProperties, audioPropertiesStyle);
if(ext == "M4A" || ext == "M4B" || ext == "M4P" || ext == "MP4" || ext == "3G2")
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);

View File

@@ -28,6 +28,7 @@
#include <tlist.h>
#include <tdebug.h>
#include <tagunion.h>
#include <tpropertymap.h>
#include <id3v2header.h>
#include <id3v2tag.h>
@@ -43,7 +44,7 @@ using namespace TagLib;
namespace
{
enum { XiphIndex = 0, ID3v2Index = 1, ID3v1Index = 2 };
enum { FlacXiphIndex = 0, FlacID3v2Index = 1, FlacID3v1Index = 2 };
enum { MinPaddingLength = 4096 };
enum { LastBlockFlag = 0x80 };
}
@@ -138,6 +139,41 @@ TagLib::Tag *FLAC::File::tag() const
return &d->tag;
}
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();
}
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);
}
PropertyMap FLAC::File::setProperties(const PropertyMap &properties)
{
if(d->hasXiphComment)
return d->tag.access<Ogg::XiphComment>(FlacXiphIndex, false)->setProperties(properties);
else if(d->hasID3v2)
return d->tag.access<ID3v2::Tag>(FlacID3v2Index, false)->setProperties(properties);
else if(d->hasID3v1)
return d->tag.access<ID3v1::Tag>(FlacID3v1Index, false)->setProperties(properties);
else
return d->tag.access<Ogg::XiphComment>(FlacXiphIndex, true)->setProperties(properties);
}
FLAC::Properties *FLAC::File::audioProperties() const
{
return d->properties;
@@ -207,7 +243,7 @@ bool FLAC::File::save()
}
ByteVector padding = ByteVector::fromUInt(paddingLength);
padding.resize(paddingLength + 4);
padding[0] = FLAC::MetadataBlock::Padding | LastBlockFlag;
padding[0] = (char)(FLAC::MetadataBlock::Padding | LastBlockFlag);
data.append(padding);
// Write the data to the file
@@ -239,21 +275,21 @@ bool FLAC::File::save()
ID3v2::Tag *FLAC::File::ID3v2Tag(bool create)
{
if(!create || d->tag[ID3v2Index])
return static_cast<ID3v2::Tag *>(d->tag[ID3v2Index]);
if(!create || d->tag[FlacID3v2Index])
return static_cast<ID3v2::Tag *>(d->tag[FlacID3v2Index]);
d->tag.set(ID3v2Index, new ID3v2::Tag);
return static_cast<ID3v2::Tag *>(d->tag[ID3v2Index]);
d->tag.set(FlacID3v2Index, new ID3v2::Tag);
return static_cast<ID3v2::Tag *>(d->tag[FlacID3v2Index]);
}
ID3v1::Tag *FLAC::File::ID3v1Tag(bool create)
{
return d->tag.access<ID3v1::Tag>(ID3v1Index, create);
return d->tag.access<ID3v1::Tag>(FlacID3v1Index, create);
}
Ogg::XiphComment *FLAC::File::xiphComment(bool create)
{
return d->tag.access<Ogg::XiphComment>(XiphIndex, create);
return d->tag.access<Ogg::XiphComment>(FlacXiphIndex, create);
}
void FLAC::File::setID3v2FrameFactory(const ID3v2::FrameFactory *factory)
@@ -274,12 +310,12 @@ void FLAC::File::read(bool readProperties, Properties::ReadStyle propertiesStyle
if(d->ID3v2Location >= 0) {
d->tag.set(ID3v2Index, new ID3v2::Tag(this, d->ID3v2Location, d->ID3v2FrameFactory));
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(ID3v2Index, 0);
d->tag.set(FlacID3v2Index, 0);
else
d->hasID3v2 = true;
}
@@ -289,7 +325,7 @@ void FLAC::File::read(bool readProperties, Properties::ReadStyle propertiesStyle
d->ID3v1Location = findID3v1();
if(d->ID3v1Location >= 0) {
d->tag.set(ID3v1Index, new ID3v1::Tag(this, d->ID3v1Location));
d->tag.set(FlacID3v1Index, new ID3v1::Tag(this, d->ID3v1Location));
d->hasID3v1 = true;
}
@@ -301,9 +337,9 @@ void FLAC::File::read(bool readProperties, Properties::ReadStyle propertiesStyle
return;
if(d->hasXiphComment)
d->tag.set(XiphIndex, new Ogg::XiphComment(xiphCommentData()));
d->tag.set(FlacXiphIndex, new Ogg::XiphComment(xiphCommentData()));
else
d->tag.set(XiphIndex, new Ogg::XiphComment);
d->tag.set(FlacXiphIndex, new Ogg::XiphComment);
if(readProperties)
d->properties = new Properties(streamInfoData(), streamLength(), propertiesStyle);
@@ -493,6 +529,17 @@ void FLAC::File::addPicture(Picture *picture)
d->blocks.append(picture);
}
void FLAC::File::removePicture(Picture *picture, bool del)
{
MetadataBlock *block = picture;
List<MetadataBlock *>::Iterator it = d->blocks.find(block);
if(it != d->blocks.end())
d->blocks.erase(it);
if(del)
delete picture;
}
void FLAC::File::removePictures()
{
List<MetadataBlock *> newBlocks;

View File

@@ -29,6 +29,7 @@
#include "taglib_export.h"
#include "tfile.h"
#include "tlist.h"
#include "tag.h"
#include "flacpicture.h"
#include "flacproperties.h"
@@ -36,7 +37,6 @@
namespace TagLib {
class Tag;
namespace ID3v2 { class FrameFactory; class Tag; }
namespace ID3v1 { class Tag; }
namespace Ogg { class XiphComment; }
@@ -97,6 +97,9 @@ namespace TagLib {
*
* If this file contains and ID3v2 tag the frames will be created using
* \a frameFactory.
*
* \note TagLib will *not* take ownership of the stream, the caller is
* responsible for deleting it after the File object.
*/
// BIC: merge with the above constructor
File(IOStream *stream, ID3v2::FrameFactory *frameFactory,
@@ -118,6 +121,23 @@ namespace TagLib {
*/
virtual TagLib::Tag *tag() const;
/*!
* Implements the unified property interface -- export function.
* If the file contains more than one tag (e.g. XiphComment and ID3v1),
* only the first one (in the order XiphComment, ID3v2, ID3v1) will be
* converted to the PropertyMap.
*/
PropertyMap properties() const;
void removeUnsupportedProperties(const StringList &);
/*!
* Implements the unified property interface -- import function.
* As with the export, only one tag is taken into account. If the file
* has no tag at all, a XiphComment will be created.
*/
PropertyMap setProperties(const PropertyMap &);
/*!
* Returns the FLAC::Properties for this file. If no audio properties
* were read then this will return a null pointer.
@@ -202,6 +222,12 @@ namespace TagLib {
*/
List<Picture *> pictureList();
/*!
* Removes an attached picture. If \a del is true the picture's memory
* will be freed; if it is false, it must be deleted by the user.
*/
void removePicture(Picture *picture, bool del = true);
/*!
* Remove all attached images.
*/

View File

@@ -33,7 +33,7 @@
using namespace TagLib;
class FLAC::MetadataBlock::MetadataBlockPrivate
class FLAC::MetadataBlock::MetadataBlockPrivate
{
public:
MetadataBlockPrivate() {}

View File

@@ -33,7 +33,7 @@
using namespace TagLib;
class FLAC::Picture::PicturePrivate
class FLAC::Picture::PicturePrivate
{
public:
PicturePrivate() :
@@ -117,7 +117,7 @@ bool FLAC::Picture::parse(const ByteVector &data)
}
d->data = data.mid(pos, dataLength);
return true;
return true;
}
ByteVector FLAC::Picture::render() const

View File

@@ -42,7 +42,8 @@ public:
bitrate(0),
sampleRate(0),
sampleWidth(0),
channels(0) {}
channels(0),
sampleFrames(0) {}
ByteVector data;
long streamLength;
@@ -52,6 +53,7 @@ public:
int sampleRate;
int sampleWidth;
int channels;
unsigned long long sampleFrames;
ByteVector signature;
};
@@ -101,6 +103,11 @@ int FLAC::Properties::channels() const
return d->channels;
}
unsigned long long FLAC::Properties::sampleFrames() const
{
return d->sampleFrames;
}
ByteVector FLAC::Properties::signature() const
{
return d->signature;
@@ -132,6 +139,8 @@ void FLAC::Properties::read()
pos += 3;
uint flags = d->data.mid(pos, 4).toUInt(true);
pos += 4;
d->sampleRate = flags >> 12;
d->channels = ((flags >> 9) & 7) + 1;
d->sampleWidth = ((flags >> 4) & 31) + 1;
@@ -139,12 +148,14 @@ void FLAC::Properties::read()
// The last 4 bits are the most significant 4 bits for the 36 bit
// stream length in samples. (Audio files measured in days)
uint highLength =d->sampleRate > 0 ? (((flags & 0xf) << 28) / d->sampleRate) << 4 : 0;
unsigned long long hi = flags & 0xf;
unsigned long long lo = d->data.mid(pos, 4).toUInt(true);
pos += 4;
d->length = d->sampleRate > 0 ?
(d->data.mid(pos, 4).toUInt(true)) / d->sampleRate + highLength : 0;
pos += 4;
d->sampleFrames = (hi << 32) | lo;
if(d->sampleRate > 0)
d->length = int(d->sampleFrames / d->sampleRate);
// Uncompressed bitrate:

View File

@@ -77,6 +77,11 @@ namespace TagLib {
*/
int sampleWidth() const;
/*!
* 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.

View File

@@ -34,7 +34,7 @@
using namespace TagLib;
class FLAC::UnknownMetadataBlock::UnknownMetadataBlockPrivate
class FLAC::UnknownMetadataBlock::UnknownMetadataBlockPrivate
{
public:
UnknownMetadataBlockPrivate() : code(0) {}

View File

@@ -23,6 +23,7 @@
#include "itfile.h"
#include "tdebug.h"
#include "modfileprivate.h"
#include "tpropertymap.h"
using namespace TagLib;
using namespace IT;
@@ -65,6 +66,16 @@ Mod::Tag *IT::File::tag() const
return &d->tag;
}
PropertyMap IT::File::properties() const
{
return d->tag.properties();
}
PropertyMap IT::File::setProperties(const PropertyMap &properties)
{
return d->tag.setProperties(properties);
}
IT::Properties *IT::File::audioProperties() const
{
return &d->properties;
@@ -114,10 +125,10 @@ bool IT::File::save()
ulong sampleOffset = 0;
if(!readU32L(sampleOffset))
return false;
seek(sampleOffset + 20);
if((i + instrumentCount) < lines.size())
if((TagLib::uint)(i + instrumentCount) < lines.size())
writeString(lines[i + instrumentCount], 25);
else
writeString(String::null, 25);
@@ -144,7 +155,7 @@ bool IT::File::save()
if(!readU16L(special))
return false;
long fileSize = this->length();
ulong fileSize = File::length();
if(special & Properties::MessageAttached) {
seek(54);
if(!readU16L(messageLength) || !readU32L(messageOffset))
@@ -160,7 +171,7 @@ bool IT::File::save()
writeU16L(special | 0x1);
}
if((messageOffset + messageLength) >= fileSize) {
if(messageOffset + messageLength >= fileSize) {
// append new message
seek(54);
writeU16L(message.size());
@@ -195,7 +206,7 @@ void IT::File::read(bool)
READ_U16L_AS(length);
READ_U16L_AS(instrumentCount);
READ_U16L_AS(sampleCount);
d->properties.setInstrumentCount(instrumentCount);
d->properties.setSampleCount(sampleCount);
READ_U16L(d->properties.setPatternCount);
@@ -240,10 +251,11 @@ void IT::File::read(bool)
// But this always gives 64 channels for all my files anyway.
// Strangely VLC does report other values. I wonder how VLC
// gets it's values.
if(pannings[i] < 128 && volumes[i] > 0) ++ channels;
if((unsigned char) pannings[i] < 128 && volumes[i] > 0)
++channels;
}
d->properties.setChannels(channels);
// real length might be shorter because of skips and terminator
ushort realLength = 0;
for(ushort i = 0; i < length; ++ i) {
@@ -275,11 +287,11 @@ void IT::File::read(bool)
READ_STRING_AS(instrumentName, 26);
comment.append(instrumentName);
}
for(ushort i = 0; i < sampleCount; ++ i) {
seek(192L + length + ((long)instrumentCount << 2) + ((long)i << 2));
READ_U32L_AS(sampleOffset);
seek(sampleOffset);
ByteVector sampleMagic = readBlock(4);
@@ -305,7 +317,7 @@ void IT::File::read(bool)
READ_BYTE_AS(vibratoRate);
READ_BYTE_AS(vibratoType);
*/
comment.append(sampleName);
}

View File

@@ -48,6 +48,9 @@ namespace TagLib {
* Contructs a Impulse Tracker file from \a stream. If \a readProperties
* is true the file's audio properties will also be read using
* \a propertiesStyle. If false, \a propertiesStyle is ignored.
*
* \note TagLib will *not* take ownership of the stream, the caller is
* responsible for deleting it after the File object.
*/
File(IOStream *stram, bool readProperties = true,
AudioProperties::ReadStyle propertiesStyle =
@@ -60,6 +63,18 @@ namespace TagLib {
Mod::Tag *tag() const;
/*!
* Forwards to Mod::Tag::properties().
* BIC: will be removed once File::toDict() is made virtual
*/
PropertyMap properties() const;
/*!
* Forwards to Mod::Tag::setProperties().
* BIC: will be removed once File::setProperties() is made virtual
*/
PropertyMap setProperties(const PropertyMap &);
/*!
* Returns the IT::Properties for this file. If no audio properties
* were read then this will return a null pointer.
@@ -74,6 +89,7 @@ namespace TagLib {
*/
bool save();
private:
File(const File &);
File &operator=(const File &);

View File

@@ -94,7 +94,7 @@ int IT::Properties::channels() const
return d->channels;
}
ushort IT::Properties::lengthInPatterns() const
TagLib::ushort IT::Properties::lengthInPatterns() const
{
return d->lengthInPatterns;
}
@@ -104,37 +104,37 @@ bool IT::Properties::stereo() const
return d->flags & Stereo;
}
ushort IT::Properties::instrumentCount() const
TagLib::ushort IT::Properties::instrumentCount() const
{
return d->instrumentCount;
}
ushort IT::Properties::sampleCount() const
TagLib::ushort IT::Properties::sampleCount() const
{
return d->sampleCount;
}
ushort IT::Properties::patternCount() const
TagLib::ushort IT::Properties::patternCount() const
{
return d->patternCount;
}
ushort IT::Properties::version() const
TagLib::ushort IT::Properties::version() const
{
return d->version;
}
ushort IT::Properties::compatibleVersion() const
TagLib::ushort IT::Properties::compatibleVersion() const
{
return d->compatibleVersion;
}
ushort IT::Properties::flags() const
TagLib::ushort IT::Properties::flags() const
{
return d->flags;
}
ushort IT::Properties::special() const
TagLib::ushort IT::Properties::special() const
{
return d->special;
}

View File

@@ -50,7 +50,7 @@ namespace TagLib {
Properties(AudioProperties::ReadStyle propertiesStyle);
virtual ~Properties();
int length() const;
int bitrate() const;
int sampleRate() const;
@@ -72,9 +72,7 @@ namespace TagLib {
uchar panningSeparation() const;
uchar pitchWheelDepth() const;
protected:
void setChannels(int channels);
void setLengthInPatterns(ushort lengthInPatterns);
void setInstrumentCount(ushort instrumentCount);
void setSampleCount (ushort sampleCount);

View File

@@ -23,6 +23,7 @@
#include "tstringlist.h"
#include "tdebug.h"
#include "modfileprivate.h"
#include "tpropertymap.h"
using namespace TagLib;
using namespace Mod;
@@ -70,6 +71,16 @@ Mod::Properties *Mod::File::audioProperties() const
return &d->properties;
}
PropertyMap Mod::File::properties() const
{
return d->tag.properties();
}
PropertyMap Mod::File::setProperties(const PropertyMap &properties)
{
return d->tag.setProperties(properties);
}
bool Mod::File::save()
{
if(readOnly()) {

View File

@@ -33,57 +33,74 @@ namespace TagLib {
namespace Mod {
class TAGLIB_EXPORT File : public TagLib::Mod::FileBase {
public:
/*!
* Contructs a Protracker file from \a file. If \a readProperties
* is true the file's audio properties will also be read using
* \a propertiesStyle. If false, \a propertiesStyle is ignored.
*/
File(FileName file, bool readProperties = true,
AudioProperties::ReadStyle propertiesStyle =
AudioProperties::Average);
class TAGLIB_EXPORT File : public TagLib::Mod::FileBase
{
public:
/*!
* Contructs a Protracker file from \a file. If \a readProperties
* is true the file's audio properties will also be read using
* \a propertiesStyle. If false, \a propertiesStyle is ignored.
*/
File(FileName file, bool readProperties = true,
AudioProperties::ReadStyle propertiesStyle =
AudioProperties::Average);
/*!
* Contructs a Protracker file from \a stream. If \a readProperties
* is true the file's audio properties will also be read using
* \a propertiesStyle. If false, \a propertiesStyle is ignored.
*/
File(IOStream *stream, bool readProperties = true,
AudioProperties::ReadStyle propertiesStyle =
AudioProperties::Average);
/*!
* Contructs a Protracker file from \a stream. If \a readProperties
* is true the file's audio properties will also be read using
* \a propertiesStyle. If false, \a propertiesStyle is ignored.
*
* \note TagLib will *not* take ownership of the stream, the caller is
* responsible for deleting it after the File object.
*/
File(IOStream *stream, bool readProperties = true,
AudioProperties::ReadStyle propertiesStyle =
AudioProperties::Average);
/*!
* Destroys this instance of the File.
*/
virtual ~File();
/*!
* Destroys this instance of the File.
*/
virtual ~File();
Mod::Tag *tag() const;
Mod::Tag *tag() const;
/*!
* Returns the Mod::Properties for this file. If no audio properties
* were read then this will return a null pointer.
*/
Mod::Properties *audioProperties() const;
/*!
* Implements the unified property interface -- export function.
* Forwards to Mod::Tag::properties().
*/
PropertyMap properties() const;
/*!
* Save the file.
* This is the same as calling save(AllTags);
*
* \note Saving Protracker tags is not supported.
*/
bool save();
/*!
* Implements the unified property interface -- import function.
* Forwards to Mod::Tag::setProperties().
*/
PropertyMap setProperties(const PropertyMap &);
/*!
* Returns the Mod::Properties for this file. If no audio properties
* were read then this will return a null pointer.
*/
Mod::Properties *audioProperties() const;
private:
File(const File &);
File &operator=(const File &);
/*!
* Save the file.
* This is the same as calling save(AllTags);
*
* \note Saving Protracker tags is not supported.
*/
bool save();
void read(bool readProperties);
private:
File(const File &);
File &operator=(const File &);
class FilePrivate;
FilePrivate *d;
void read(bool readProperties);
class FilePrivate;
FilePrivate *d;
};
}
}
#endif

View File

@@ -31,7 +31,9 @@
#include <algorithm>
namespace TagLib {
namespace Mod {
class TAGLIB_EXPORT FileBase : public TagLib::File
{
protected:
@@ -52,7 +54,9 @@ namespace TagLib {
bool readU16B(ushort &number);
bool readU32B(ulong &number);
};
}
}
#endif

View File

@@ -33,7 +33,7 @@ public:
lengthInPatterns(0)
{
}
int channels;
uint instrumentCount;
uchar lengthInPatterns;
@@ -70,7 +70,7 @@ int Mod::Properties::channels() const
return d->channels;
}
uint Mod::Properties::instrumentCount() const
TagLib::uint Mod::Properties::instrumentCount() const
{
return d->instrumentCount;
}

View File

@@ -26,35 +26,40 @@
#include "audioproperties.h"
namespace TagLib {
namespace Mod {
class TAGLIB_EXPORT Properties : public AudioProperties {
friend class File;
class TAGLIB_EXPORT Properties : public AudioProperties
{
public:
Properties(AudioProperties::ReadStyle propertiesStyle);
virtual ~Properties();
int length() const;
int bitrate() const;
int sampleRate() const;
int channels() const;
uint instrumentCount() const;
int length() const;
int bitrate() const;
int sampleRate() const;
int channels() const;
uint instrumentCount() const;
uchar lengthInPatterns() const;
protected:
void setChannels(int channels);
void setInstrumentCount(uint sampleCount);
void setLengthInPatterns(uchar lengthInPatterns);
private:
friend class File;
Properties(const Properties&);
Properties &operator=(const Properties&);
class PropertiesPrivate;
PropertiesPrivate *d;
};
}
}
#endif

View File

@@ -20,6 +20,8 @@
***************************************************************************/
#include "modtag.h"
#include "tstringlist.h"
#include "tpropertymap.h"
using namespace TagLib;
using namespace Mod;
@@ -71,12 +73,12 @@ String Mod::Tag::genre() const
return String::null;
}
uint Mod::Tag::year() const
TagLib::uint Mod::Tag::year() const
{
return 0;
}
uint Mod::Tag::track() const
TagLib::uint Mod::Tag::track() const
{
return 0;
}
@@ -120,3 +122,47 @@ void Mod::Tag::setTrackerName(const String &trackerName)
{
d->trackerName = trackerName;
}
PropertyMap Mod::Tag::properties() const
{
PropertyMap properties;
properties["TITLE"] = d->title;
properties["COMMENT"] = d->comment;
if(!(d->trackerName.isNull()))
properties["TRACKERNAME"] = d->trackerName;
return properties;
}
PropertyMap Mod::Tag::setProperties(const PropertyMap &origProps)
{
PropertyMap properties(origProps);
properties.removeEmpty();
StringList oneValueSet;
if(properties.contains("TITLE")) {
d->title = properties["TITLE"].front();
oneValueSet.append("TITLE");
} else
d->title = String::null;
if(properties.contains("COMMENT")) {
d->comment = properties["COMMENT"].front();
oneValueSet.append("COMMENT");
} else
d->comment = String::null;
if(properties.contains("TRACKERNAME")) {
d->trackerName = properties["TRACKERNAME"].front();
oneValueSet.append("TRACKERNAME");
} else
d->trackerName = String::null;
// for each tag that has been set above, remove the first entry in the corresponding
// value list. The others will be returned as unsupported by this format.
for(StringList::Iterator it = oneValueSet.begin(); it != oneValueSet.end(); ++it) {
if(properties[*it].size() == 1)
properties.erase(*it);
else
properties[*it].erase( properties[*it].begin() );
}
return properties;
}

View File

@@ -25,7 +25,9 @@
#include "tag.h"
namespace TagLib {
namespace Mod {
/*!
* Tags for module files (Mod, S3M, IT, XM).
*
@@ -148,15 +150,31 @@ namespace TagLib {
/*!
* Sets the tracker name to \a trackerName. If \a trackerName is
* String::null then this value will be cleared.
*
*
* Note that only XM files support this tag. Setting the
* tracker name for other module file formats will be ignored.
*
*
* The length of this tag is limited to 20 characters (1 character
* = 1 byte).
*/
void setTrackerName(const String &trackerName);
/*!
* Implements the unified property interface -- export function.
* Since the module tag is very limited, the exported map is as well.
*/
PropertyMap properties() const;
/*!
* Implements the unified property interface -- import function.
* Because of the limitations of the 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,
* all but the first will be contained in the returned map of unsupported
* properties.
*/
PropertyMap setProperties(const PropertyMap &);
private:
Tag(const Tag &);
Tag &operator=(const Tag &);
@@ -164,7 +182,9 @@ namespace TagLib {
class TagPrivate;
TagPrivate *d;
};
}
}
#endif

View File

@@ -1,5 +1,5 @@
/**************************************************************************
copyright : (C) 2007 by Lukáš Lalinský
copyright : (C) 2007,2011 by Lukáš Lalinský
email : lalinsky@gmail.com
**************************************************************************/
@@ -40,6 +40,40 @@ namespace TagLib {
class Atom;
typedef TagLib::List<Atom *> AtomList;
enum AtomDataType
{
TypeImplicit = 0, // for use with tags for which no type needs to be indicated because only one type is allowed
TypeUTF8 = 1, // without any count or null terminator
TypeUTF16 = 2, // also known as UTF-16BE
TypeSJIS = 3, // deprecated unless it is needed for special Japanese characters
TypeHTML = 6, // the HTML file header specifies which HTML version
TypeXML = 7, // the XML header must identify the DTD or schemas
TypeUUID = 8, // also known as GUID; stored as 16 bytes in binary (valid as an ID)
TypeISRC = 9, // stored as UTF-8 text (valid as an ID)
TypeMI3P = 10, // stored as UTF-8 text (valid as an ID)
TypeGIF = 12, // (deprecated) a GIF image
TypeJPEG = 13, // a JPEG image
TypePNG = 14, // a PNG image
TypeURL = 15, // absolute, in UTF-8 characters
TypeDuration = 16, // in milliseconds, 32-bit integer
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
TypeUPC = 25, // Universal Product Code, in text UTF-8 format (valid as an ID)
TypeBMP = 27, // Windows bitmap image
TypeUndefined = 255 // undefined
};
struct AtomData {
AtomData(AtomDataType type, ByteVector data) : type(type), locale(0), data(data) {}
AtomDataType type;
int locale;
ByteVector data;
};
typedef TagLib::List<AtomData> AtomDataList;
class Atom
{
public:

View File

@@ -29,6 +29,7 @@
#include "tlist.h"
#include "tbytevector.h"
#include "taglib_export.h"
#include "mp4atom.h"
namespace TagLib {
@@ -41,8 +42,10 @@ namespace TagLib {
* This describes the image type.
*/
enum Format {
JPEG = 0x0D,
PNG = 0x0E
JPEG = TypeJPEG,
PNG = TypePNG,
BMP = TypeBMP,
GIF = TypeGIF
};
CoverArt(Format format, const ByteVector &data);

View File

@@ -65,6 +65,9 @@ namespace TagLib {
*
* \note In the current implementation, both \a readProperties and
* \a propertiesStyle are ignored.
*
* \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, Properties::ReadStyle audioPropertiesStyle = Properties::Average);

View File

@@ -36,15 +36,20 @@ using namespace TagLib;
class MP4::Item::ItemPrivate : public RefCounter
{
public:
ItemPrivate() : RefCounter(), valid(true) {}
ItemPrivate() : RefCounter(), valid(true), atomDataType(TypeUndefined) {}
bool valid;
AtomDataType atomDataType;
union {
bool m_bool;
int m_int;
IntPair m_intPair;
uchar m_byte;
uint m_uint;
long long m_longlong;
};
StringList m_stringList;
ByteVectorList m_byteVectorList;
MP4::CoverArtList m_coverArtList;
};
@@ -89,6 +94,24 @@ MP4::Item::Item(int value)
d->m_int = value;
}
MP4::Item::Item(uchar value)
{
d = new ItemPrivate;
d->m_byte = value;
}
MP4::Item::Item(uint value)
{
d = new ItemPrivate;
d->m_uint = value;
}
MP4::Item::Item(long long value)
{
d = new ItemPrivate;
d->m_longlong = value;
}
MP4::Item::Item(int value1, int value2)
{
d = new ItemPrivate;
@@ -96,6 +119,12 @@ MP4::Item::Item(int value1, int value2)
d->m_intPair.second = value2;
}
MP4::Item::Item(const ByteVectorList &value)
{
d = new ItemPrivate;
d->m_byteVectorList = value;
}
MP4::Item::Item(const StringList &value)
{
d = new ItemPrivate;
@@ -108,6 +137,16 @@ MP4::Item::Item(const MP4::CoverArtList &value)
d->m_coverArtList = value;
}
void MP4::Item::setAtomDataType(MP4::AtomDataType type)
{
d->atomDataType = type;
}
MP4::AtomDataType MP4::Item::atomDataType() const
{
return d->atomDataType;
}
bool
MP4::Item::toBool() const
{
@@ -120,6 +159,24 @@ MP4::Item::toInt() const
return d->m_int;
}
uchar
MP4::Item::toByte() const
{
return d->m_byte;
}
TagLib::uint
MP4::Item::toUInt() const
{
return d->m_uint;
}
long long
MP4::Item::toLongLong() const
{
return d->m_longlong;
}
MP4::Item::IntPair
MP4::Item::toIntPair() const
{
@@ -132,6 +189,12 @@ MP4::Item::toStringList() const
return d->m_stringList;
}
ByteVectorList
MP4::Item::toByteVectorList() const
{
return d->m_byteVectorList;
}
MP4::CoverArtList
MP4::Item::toCoverArtList() const
{

View File

@@ -47,15 +47,26 @@ namespace TagLib {
~Item();
Item(int value);
Item(uchar value);
Item(uint value);
Item(long long value);
Item(bool value);
Item(int first, int second);
Item(const StringList &value);
Item(const ByteVectorList &value);
Item(const CoverArtList &value);
void setAtomDataType(AtomDataType type);
AtomDataType atomDataType() const;
int toInt() const;
uchar toByte() const;
uint toUInt() const;
long long toLongLong() const;
bool toBool() const;
IntPair toIntPair() const;
StringList toStringList() const;
ByteVectorList toByteVectorList() const;
CoverArtList toCoverArtList() const;
bool isValid() const;

View File

@@ -90,15 +90,24 @@ MP4::Properties::Properties(File *file, MP4::Atoms *atoms, ReadStyle style)
file->seek(mdhd->offset);
data = file->readBlock(mdhd->length);
if(data[8] == 0) {
unsigned int unit = data.mid(20, 4).toUInt();
unsigned int length = data.mid(24, 4).toUInt();
d->length = length / unit;
}
else {
uint version = data[8];
if(version == 1) {
if (data.size() < 36 + 8) {
debug("MP4: Atom 'trak.mdia.mdhd' is smaller than expected");
return;
}
long long unit = data.mid(28, 8).toLongLong();
long long length = data.mid(36, 8).toLongLong();
d->length = int(length / unit);
d->length = unit ? int(length / unit) : 0;
}
else {
if (data.size() < 24 + 4) {
debug("MP4: Atom 'trak.mdia.mdhd' is smaller than expected");
return;
}
unsigned int unit = data.mid(20, 4).toUInt();
unsigned int length = data.mid(24, 4).toUInt();
d->length = unit ? length / unit : 0;
}
MP4::Atom *atom = trak->find("mdia", "minf", "stbl", "stsd");

View File

@@ -1,5 +1,5 @@
/**************************************************************************
copyright : (C) 2007 by Lukáš Lalinský
copyright : (C) 2007,2011 by Lukáš Lalinský
email : lalinsky@gmail.com
**************************************************************************/
@@ -71,12 +71,23 @@ MP4::Tag::Tag(TagLib::File *file, MP4::Atoms *atoms)
else if(atom->name == "trkn" || atom->name == "disk") {
parseIntPair(atom, file);
}
else if(atom->name == "cpil" || atom->name == "pgap" || atom->name == "pcst") {
else if(atom->name == "cpil" || atom->name == "pgap" || atom->name == "pcst" ||
atom->name == "hdvd") {
parseBool(atom, file);
}
else if(atom->name == "tmpo") {
parseInt(atom, file);
}
else if(atom->name == "tvsn" || atom->name == "tves" || atom->name == "cnID" ||
atom->name == "sfID" || atom->name == "atID" || atom->name == "geID") {
parseUInt(atom, file);
}
else if(atom->name == "plID") {
parseLongLong(atom, file);
}
else if(atom->name == "stik" || atom->name == "rtng" || atom->name == "akID") {
parseByte(atom, file);
}
else if(atom->name == "gnre") {
parseGnre(atom, file);
}
@@ -94,10 +105,10 @@ MP4::Tag::~Tag()
delete d;
}
ByteVectorList
MP4::Tag::parseData(MP4::Atom *atom, TagLib::File *file, int expectedFlags, bool freeForm)
MP4::AtomDataList
MP4::Tag::parseData2(MP4::Atom *atom, TagLib::File *file, int expectedFlags, bool freeForm)
{
ByteVectorList result;
AtomDataList result;
ByteVector data = file->readBlock(atom->length - 8);
int i = 0;
unsigned int pos = 0;
@@ -114,7 +125,7 @@ MP4::Tag::parseData(MP4::Atom *atom, TagLib::File *file, int expectedFlags, bool
debug("MP4: Unexpected atom \"" + name + "\", expecting \"name\"");
return result;
}
result.append(data.mid(pos + 12, length - 12));
result.append(AtomData(AtomDataType(flags), data.mid(pos + 12, length - 12)));
}
else {
if(name != "data") {
@@ -122,7 +133,7 @@ MP4::Tag::parseData(MP4::Atom *atom, TagLib::File *file, int expectedFlags, bool
return result;
}
if(expectedFlags == -1 || flags == expectedFlags) {
result.append(data.mid(pos + 16, length - 16));
result.append(AtomData(AtomDataType(flags), data.mid(pos + 16, length - 16)));
}
}
pos += length;
@@ -131,6 +142,17 @@ MP4::Tag::parseData(MP4::Atom *atom, TagLib::File *file, int expectedFlags, bool
return result;
}
ByteVectorList
MP4::Tag::parseData(MP4::Atom *atom, TagLib::File *file, int expectedFlags, bool freeForm)
{
AtomDataList data = parseData2(atom, file, expectedFlags, freeForm);
ByteVectorList result;
for(uint i = 0; i < data.size(); i++) {
result.append(data[i].data);
}
return result;
}
void
MP4::Tag::parseInt(MP4::Atom *atom, TagLib::File *file)
{
@@ -140,6 +162,33 @@ MP4::Tag::parseInt(MP4::Atom *atom, TagLib::File *file)
}
}
void
MP4::Tag::parseUInt(MP4::Atom *atom, TagLib::File *file)
{
ByteVectorList data = parseData(atom, file);
if(data.size()) {
d->items.insert(atom->name, data[0].toUInt());
}
}
void
MP4::Tag::parseLongLong(MP4::Atom *atom, TagLib::File *file)
{
ByteVectorList data = parseData(atom, file);
if(data.size()) {
d->items.insert(atom->name, data[0].toLongLong());
}
}
void
MP4::Tag::parseByte(MP4::Atom *atom, TagLib::File *file)
{
ByteVectorList data = parseData(atom, file);
if(data.size()) {
d->items.insert(atom->name, (uchar)data[0].at(0));
}
}
void
MP4::Tag::parseGnre(MP4::Atom *atom, TagLib::File *file)
{
@@ -189,14 +238,34 @@ MP4::Tag::parseText(MP4::Atom *atom, TagLib::File *file, int expectedFlags)
void
MP4::Tag::parseFreeForm(MP4::Atom *atom, TagLib::File *file)
{
ByteVectorList data = parseData(atom, file, 1, true);
AtomDataList data = parseData2(atom, file, -1, true);
if(data.size() > 2) {
StringList value;
for(unsigned int i = 2; i < data.size(); i++) {
value.append(String(data[i], String::UTF8));
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) {
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));
}
Item item(value);
item.setAtomDataType(type);
d->items.insert(name, item);
}
else {
ByteVectorList value;
for(uint i = 2; i < data.size(); i++) {
value.append(data[i].data);
}
Item item(value);
item.setAtomDataType(type);
d->items.insert(name, item);
}
String name = "----:" + data[0] + ':' + data[1];
d->items.insert(name, value);
}
}
@@ -214,7 +283,7 @@ MP4::Tag::parseCovr(MP4::Atom *atom, TagLib::File *file)
debug("MP4: Unexpected atom \"" + name + "\", expecting \"data\"");
break;
}
if(flags == MP4::CoverArt::PNG || flags == MP4::CoverArt::JPEG) {
if(flags == TypeJPEG || flags == TypePNG || flags == TypeBMP || flags == TypeGIF) {
value.append(MP4::CoverArt(MP4::CoverArt::Format(flags),
data.mid(pos + 16, length - 16)));
}
@@ -254,7 +323,7 @@ MP4::Tag::renderBool(const ByteVector &name, MP4::Item &item)
{
ByteVectorList data;
data.append(ByteVector(1, item.toBool() ? '\1' : '\0'));
return renderData(name, 0x15, data);
return renderData(name, TypeInteger, data);
}
ByteVector
@@ -262,7 +331,31 @@ MP4::Tag::renderInt(const ByteVector &name, MP4::Item &item)
{
ByteVectorList data;
data.append(ByteVector::fromShort(item.toInt()));
return renderData(name, 0x15, data);
return renderData(name, TypeInteger, data);
}
ByteVector
MP4::Tag::renderUInt(const ByteVector &name, MP4::Item &item)
{
ByteVectorList data;
data.append(ByteVector::fromUInt(item.toUInt()));
return renderData(name, TypeInteger, data);
}
ByteVector
MP4::Tag::renderLongLong(const ByteVector &name, MP4::Item &item)
{
ByteVectorList data;
data.append(ByteVector::fromLongLong(item.toLongLong()));
return renderData(name, TypeInteger, data);
}
ByteVector
MP4::Tag::renderByte(const ByteVector &name, MP4::Item &item)
{
ByteVectorList data;
data.append(ByteVector(1, item.toByte()));
return renderData(name, TypeInteger, data);
}
ByteVector
@@ -273,7 +366,7 @@ MP4::Tag::renderIntPair(const ByteVector &name, MP4::Item &item)
ByteVector::fromShort(item.toIntPair().first) +
ByteVector::fromShort(item.toIntPair().second) +
ByteVector(2, '\0'));
return renderData(name, 0x00, data);
return renderData(name, TypeImplicit, data);
}
ByteVector
@@ -283,7 +376,7 @@ MP4::Tag::renderIntPairNoTrailing(const ByteVector &name, MP4::Item &item)
data.append(ByteVector(2, '\0') +
ByteVector::fromShort(item.toIntPair().first) +
ByteVector::fromShort(item.toIntPair().second));
return renderData(name, 0x00, data);
return renderData(name, TypeImplicit, data);
}
ByteVector
@@ -320,9 +413,26 @@ MP4::Tag::renderFreeForm(const String &name, MP4::Item &item)
ByteVector data;
data.append(renderAtom("mean", ByteVector::fromUInt(0) + header[1].data(String::UTF8)));
data.append(renderAtom("name", ByteVector::fromUInt(0) + header[2].data(String::UTF8)));
StringList value = item.toStringList();
for(unsigned int i = 0; i < value.size(); i++) {
data.append(renderAtom("data", ByteVector::fromUInt(1) + ByteVector(4, '\0') + value[i].data(String::UTF8)));
AtomDataType type = item.atomDataType();
if(type == TypeUndefined) {
if(!item.toStringList().isEmpty()) {
type = TypeUTF8;
}
else {
type = TypeImplicit;
}
}
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)));
}
}
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]));
}
}
return renderAtom("----", data);
}
@@ -342,12 +452,22 @@ MP4::Tag::save()
else if(name == "disk") {
data.append(renderIntPairNoTrailing(name.data(String::Latin1), i->second));
}
else if(name == "cpil" || name == "pgap" || name == "pcst") {
else if(name == "cpil" || name == "pgap" || name == "pcst" || name == "hdvd") {
data.append(renderBool(name.data(String::Latin1), i->second));
}
else if(name == "tmpo") {
data.append(renderInt(name.data(String::Latin1), i->second));
}
else if(name == "tvsn" || name == "tves" || name == "cnID" ||
name == "sfID" || name == "atID" || name == "geID") {
data.append(renderUInt(name.data(String::Latin1), i->second));
}
else if(name == "plID") {
data.append(renderLongLong(name.data(String::Latin1), i->second));
}
else if(name == "stik" || name == "rtng" || name == "akID") {
data.append(renderByte(name.data(String::Latin1), i->second));
}
else if(name == "covr") {
data.append(renderCovr(name.data(String::Latin1), i->second));
}

View File

@@ -1,5 +1,5 @@
/**************************************************************************
copyright : (C) 2007 by Lukáš Lalinský
copyright : (C) 2007,2011 by Lukáš Lalinský
email : lalinsky@gmail.com
**************************************************************************/
@@ -68,10 +68,14 @@ namespace TagLib {
ItemListMap &itemListMap();
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);
@@ -80,10 +84,13 @@ namespace TagLib {
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 = 1);
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);

View File

@@ -27,6 +27,7 @@
#include <tstring.h>
#include <tagunion.h>
#include <tdebug.h>
#include <tpropertymap.h>
#include "mpcfile.h"
#include "id3v1tag.h"
@@ -38,7 +39,7 @@ using namespace TagLib;
namespace
{
enum { APEIndex, ID3v1Index };
enum { MPCAPEIndex, MPCID3v1Index };
}
class MPC::File::FilePrivate
@@ -113,6 +114,34 @@ TagLib::Tag *MPC::File::tag() const
return &d->tag;
}
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();
}
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);
}
PropertyMap MPC::File::setProperties(const PropertyMap &properties)
{
if(d->hasAPE)
return d->tag.access<APE::Tag>(MPCAPEIndex, false)->setProperties(properties);
else if(d->hasID3v1)
return d->tag.access<ID3v1::Tag>(MPCID3v1Index, false)->setProperties(properties);
else
return d->tag.access<APE::Tag>(APE, true)->setProperties(properties);
}
MPC::Properties *MPC::File::audioProperties() const
{
return d->properties;
@@ -196,18 +225,18 @@ bool MPC::File::save()
ID3v1::Tag *MPC::File::ID3v1Tag(bool create)
{
return d->tag.access<ID3v1::Tag>(ID3v1Index, create);
return d->tag.access<ID3v1::Tag>(MPCID3v1Index, create);
}
APE::Tag *MPC::File::APETag(bool create)
{
return d->tag.access<APE::Tag>(APEIndex, create);
return d->tag.access<APE::Tag>(MPCAPEIndex, create);
}
void MPC::File::strip(int tags)
{
if(tags & ID3v1) {
d->tag.set(ID3v1Index, 0);
d->tag.set(MPCID3v1Index, 0);
APETag(true);
}
@@ -217,7 +246,7 @@ void MPC::File::strip(int tags)
}
if(tags & APE) {
d->tag.set(APEIndex, 0);
d->tag.set(MPCAPEIndex, 0);
if(!ID3v1Tag())
APETag(true);
@@ -241,7 +270,7 @@ void MPC::File::read(bool readProperties, Properties::ReadStyle /* propertiesSty
d->ID3v1Location = findID3v1();
if(d->ID3v1Location >= 0) {
d->tag.set(ID3v1Index, new ID3v1::Tag(this, d->ID3v1Location));
d->tag.set(MPCID3v1Index, new ID3v1::Tag(this, d->ID3v1Location));
d->hasID3v1 = true;
}
@@ -252,7 +281,7 @@ void MPC::File::read(bool readProperties, Properties::ReadStyle /* propertiesSty
d->APELocation = findAPE();
if(d->APELocation >= 0) {
d->tag.set(APEIndex, new APE::Tag(this, d->APELocation));
d->tag.set(MPCAPEIndex, new APE::Tag(this, d->APELocation));
d->APESize = APETag()->footer()->completeTagSize();
d->APELocation = d->APELocation + APETag()->footer()->size() - d->APESize;
@@ -281,8 +310,7 @@ void MPC::File::read(bool readProperties, Properties::ReadStyle /* propertiesSty
// Look for MPC metadata
if(readProperties) {
d->properties = new Properties(readBlock(MPC::HeaderSize),
length() - d->ID3v2Size - d->APESize);
d->properties = new Properties(this, length() - d->ID3v2Size - d->APESize);
}
}

View File

@@ -28,9 +28,12 @@
#include "taglib_export.h"
#include "tfile.h"
#include "tag.h"
#include "mpcproperties.h"
#include "tlist.h"
namespace TagLib {
class Tag;
@@ -92,6 +95,9 @@ namespace TagLib {
* Contructs an MPC file from \a file. If \a readProperties is true the
* file's audio properties will also be read using \a propertiesStyle. If
* false, \a propertiesStyle is ignored.
*
* \note 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,
Properties::ReadStyle propertiesStyle = Properties::Average);
@@ -107,6 +113,22 @@ namespace TagLib {
*/
virtual TagLib::Tag *tag() const;
/*!
* Implements the unified property interface -- export function.
* If the file contains both an APE and an ID3v1 tag, only the APE
* tag will be converted to the PropertyMap.
*/
PropertyMap properties() const;
void removeUnsupportedProperties(const StringList &properties);
/*!
* Implements the unified property interface -- import function.
* As with the export, only one tag is taken into account. If the file
* has no tag at all, an APE tag will be created.
*/
PropertyMap setProperties(const PropertyMap &);
/*!
* Returns the MPC::Properties for this file. If no audio properties
* were read then this will return a null pointer.
@@ -163,7 +185,6 @@ namespace TagLib {
*/
void remove(int tags = AllTags);
private:
File(const File &);
File &operator=(const File &);

View File

@@ -26,6 +26,7 @@
#include <tstring.h>
#include <tdebug.h>
#include <bitset>
#include <math.h>
#include "mpcproperties.h"
#include "mpcfile.h"
@@ -35,17 +36,21 @@ using namespace TagLib;
class MPC::Properties::PropertiesPrivate
{
public:
PropertiesPrivate(const ByteVector &d, long length, ReadStyle s) :
data(d),
PropertiesPrivate(long length, ReadStyle s) :
streamLength(length),
style(s),
version(0),
length(0),
bitrate(0),
sampleRate(0),
channels(0) {}
channels(0),
totalFrames(0),
sampleFrames(0),
trackGain(0),
trackPeak(0),
albumGain(0),
albumPeak(0) {}
ByteVector data;
long streamLength;
ReadStyle style;
int version;
@@ -53,6 +58,13 @@ public:
int bitrate;
int sampleRate;
int channels;
uint totalFrames;
uint sampleFrames;
uint trackGain;
uint trackPeak;
uint albumGain;
uint albumPeak;
String flags;
};
////////////////////////////////////////////////////////////////////////////////
@@ -61,8 +73,22 @@ public:
MPC::Properties::Properties(const ByteVector &data, long streamLength, ReadStyle style) : AudioProperties(style)
{
d = new PropertiesPrivate(data, streamLength, style);
read();
d = new PropertiesPrivate(streamLength, style);
readSV7(data);
}
MPC::Properties::Properties(File *file, long streamLength, ReadStyle style) : AudioProperties(style)
{
d = new PropertiesPrivate(streamLength, style);
ByteVector magic = file->readBlock(4);
if(magic == "MPCK") {
// Musepack version 8
readSV8(file);
}
else {
// Musepack version 7 or older, fixed size header
readSV7(magic + file->readBlock(MPC::HeaderSize - 4));
}
}
MPC::Properties::~Properties()
@@ -95,30 +121,179 @@ int MPC::Properties::mpcVersion() const
return d->version;
}
TagLib::uint MPC::Properties::totalFrames() const
{
return d->totalFrames;
}
TagLib::uint MPC::Properties::sampleFrames() const
{
return d->sampleFrames;
}
int MPC::Properties::trackGain() const
{
return d->trackGain;
}
int MPC::Properties::trackPeak() const
{
return d->trackPeak;
}
int MPC::Properties::albumGain() const
{
return d->albumGain;
}
int MPC::Properties::albumPeak() const
{
return d->albumPeak;
}
////////////////////////////////////////////////////////////////////////////////
// private members
////////////////////////////////////////////////////////////////////////////////
unsigned long readSize(File *file, TagLib::uint &sizelength)
{
unsigned char tmp;
unsigned long size = 0;
do {
ByteVector b = file->readBlock(1);
tmp = b[0];
size = (size << 7) | (tmp & 0x7F);
sizelength++;
} while((tmp & 0x80));
return size;
}
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::read()
void MPC::Properties::readSV8(File *file)
{
if(!d->data.startsWith("MP+"))
return;
bool readSH = false, readRG = false;
d->version = d->data[3] & 15;
while(!readSH && !readRG) {
ByteVector packetType = file->readBlock(2);
uint packetSizeLength = 0;
unsigned long packetSize = readSize(file, packetSizeLength);
unsigned long dataSize = packetSize - 2 - packetSizeLength;
unsigned int frames;
if(packetType == "SH") {
// Stream Header
// http://trac.musepack.net/wiki/SV8Specification#StreamHeaderPacket
ByteVector data = file->readBlock(dataSize);
readSH = true;
if(d->version >= 7) {
frames = d->data.mid(4, 4).toUInt(false);
TagLib::uint pos = 4;
d->version = data[pos];
pos += 1;
d->sampleFrames = readSize(data.mid(pos), pos);
ulong begSilence = readSize(data.mid(pos), pos);
std::bitset<32> flags(TAGLIB_CONSTRUCT_BITSET(d->data.mid(8, 4).toUInt(false)));
std::bitset<16> flags(TAGLIB_CONSTRUCT_BITSET(data.mid(pos, 2).toUShort(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;
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;
}
else if (packetType == "RG") {
// Replay Gain
// http://trac.musepack.net/wiki/SV8Specification#ReplaygainPacket
ByteVector data = file->readBlock(dataSize);
readRG = true;
int replayGainVersion = data[0];
if(replayGainVersion == 1) {
d->trackGain = data.mid(1, 2).toUInt(true);
d->trackPeak = data.mid(3, 2).toUInt(true);
d->albumGain = data.mid(5, 2).toUInt(true);
d->albumPeak = data.mid(7, 2).toUInt(true);
}
}
else if(packetType == "SE") {
break;
}
else {
file->seek(dataSize, File::Current);
}
}
}
void MPC::Properties::readSV7(const ByteVector &data)
{
if(data.startsWith("MP+")) {
d->version = data[3] & 15;
if(d->version < 7)
return;
d->totalFrames = data.mid(4, 4).toUInt(false);
std::bitset<32> flags(TAGLIB_CONSTRUCT_BITSET(data.mid(8, 4).toUInt(false)));
d->sampleRate = sftable[flags[17] * 2 + flags[16]];
d->channels = 2;
uint gapless = data.mid(5, 4).toUInt(false);
d->trackGain = data.mid(14,2).toShort(false);
d->trackPeak = data.mid(12,2).toUInt(false);
d->albumGain = data.mid(18,2).toShort(false);
d->albumPeak = data.mid(16,2).toUInt(false);
// convert gain info
if(d->trackGain != 0) {
int tmp = (int)((64.82 - (short)d->trackGain / 100.) * 256. + .5);
if(tmp >= (1 << 16) || tmp < 0) tmp = 0;
d->trackGain = tmp;
}
if(d->albumGain != 0) {
int tmp = (int)((64.82 - d->albumGain / 100.) * 256. + .5);
if(tmp >= (1 << 16) || tmp < 0) tmp = 0;
d->albumGain = tmp;
}
if (d->trackPeak != 0)
d->trackPeak = (int)(log10((double)d->trackPeak) * 20 * 256 + .5);
if (d->albumPeak != 0)
d->albumPeak = (int)(log10((double)d->albumPeak) * 20 * 256 + .5);
bool trueGapless = (gapless >> 31) & 0x0001;
if(trueGapless) {
uint lastFrameSamples = (gapless >> 20) & 0x07FF;
d->sampleFrames = d->totalFrames * 1152 - lastFrameSamples;
}
else
d->sampleFrames = d->totalFrames * 1152 - 576;
}
else {
uint headerData = d->data.mid(0, 4).toUInt(false);
uint headerData = data.mid(0, 4).toUInt(false);
d->bitrate = (headerData >> 23) & 0x01ff;
d->version = (headerData >> 11) & 0x03ff;
@@ -126,15 +301,16 @@ void MPC::Properties::read()
d->channels = 2;
if(d->version >= 5)
frames = d->data.mid(4, 4).toUInt(false);
d->totalFrames = data.mid(4, 4).toUInt(false);
else
frames = d->data.mid(6, 2).toUInt(false);
d->totalFrames = data.mid(6, 2).toUInt(false);
d->sampleFrames = d->totalFrames * 1152 - 576;
}
uint samples = frames * 1152 - 576;
d->length = d->sampleRate > 0 ? (samples + (d->sampleRate / 2)) / d->sampleRate : 0;
d->length = d->sampleRate > 0 ? (d->sampleFrames + (d->sampleRate / 2)) / d->sampleRate : 0;
if(!d->bitrate)
d->bitrate = d->length > 0 ? ((d->streamLength * 8L) / d->length) / 1000 : 0;
}

View File

@@ -50,9 +50,17 @@ namespace TagLib {
/*!
* Create an instance of MPC::Properties with the data read from the
* ByteVector \a data.
*
* This constructor is deprecated. It only works for MPC version up to 7.
*/
Properties(const ByteVector &data, long streamLength, ReadStyle style = Average);
/*!
* Create an instance of MPC::Properties with the data read directly
* from a MPC::File.
*/
Properties(File *file, long streamLength, ReadStyle style = Average);
/*!
* Destroys this MPC::Properties instance.
*/
@@ -66,15 +74,44 @@ namespace TagLib {
virtual int channels() const;
/*!
* Returns the version of the bitstream (SV4-SV7)
* Returns the version of the bitstream (SV4-SV8)
*/
int mpcVersion() const;
uint totalFrames() const;
uint sampleFrames() const;
/*!
* Returns the track gain as an integer value,
* to convert to dB: trackGain in dB = 64.82 - (trackGain / 256)
*/
int trackGain() const;
/*!
* Returns the track peak as an integer value,
* to convert to dB: trackPeak in dB = trackPeak / 256
* to convert to floating [-1..1]: trackPeak = 10^(trackPeak / 256 / 20)/32768
*/
int trackPeak() const;
/*!
* Returns the album gain as an integer value,
* to convert to dB: albumGain in dB = 64.82 - (albumGain / 256)
*/
int albumGain() const;
/*!
* Returns the album peak as an integer value,
* to convert to dB: albumPeak in dB = albumPeak / 256
* to convert to floating [-1..1]: albumPeak = 10^(albumPeak / 256 / 20)/32768
*/
int albumPeak() const;
private:
Properties(const Properties &);
Properties &operator=(const Properties &);
void read();
void readSV7(const ByteVector &data);
void readSV8(File *file);
class PropertiesPrivate;
PropertiesPrivate *d;

View File

@@ -51,12 +51,17 @@ public:
static const StringHandler *stringHandler;
};
const ID3v1::StringHandler *ID3v1::Tag::TagPrivate::stringHandler = new StringHandler;
static const StringHandler defaultStringHandler;
const ID3v1::StringHandler *ID3v1::Tag::TagPrivate::stringHandler = &defaultStringHandler;
////////////////////////////////////////////////////////////////////////////////
// StringHandler implementation
////////////////////////////////////////////////////////////////////////////////
StringHandler::StringHandler()
{
}
String ID3v1::StringHandler::parse(const ByteVector &data) const
{
return String(data, String::Latin1).stripWhiteSpace();
@@ -189,8 +194,10 @@ void ID3v1::Tag::setTrack(uint i)
void ID3v1::Tag::setStringHandler(const StringHandler *handler)
{
delete TagPrivate::stringHandler;
TagPrivate::stringHandler = handler;
if (handler)
TagPrivate::stringHandler = handler;
else
TagPrivate::stringHandler = &defaultStringHandler;
}
////////////////////////////////////////////////////////////////////////////////

View File

@@ -62,6 +62,7 @@ namespace TagLib {
TAGLIB_IGNORE_MISSING_DESTRUCTOR
public:
// BIC: Add virtual destructor.
StringHandler();
/*!
* Decode a string from \a data. The default implementation assumes that
@@ -153,6 +154,11 @@ namespace TagLib {
/*!
* Sets the string handler that decides how the ID3v1 data will be
* converted to and from binary data.
* If the parameter \a handler is null, the previous handler is
* released and default ISO-8859-1 handler is restored.
*
* \note The caller is responsible for deleting the previous handler
* as needed after it is released.
*
* \see StringHandler
*/

View File

@@ -29,6 +29,7 @@
#include <tstringlist.h>
#include "commentsframe.h"
#include "tpropertymap.h"
using namespace TagLib;
using namespace ID3v2;
@@ -109,6 +110,19 @@ void CommentsFrame::setTextEncoding(String::Type encoding)
d->textEncoding = encoding;
}
PropertyMap CommentsFrame::asProperties() const
{
String key = description().upper();
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;
}
CommentsFrame *CommentsFrame::findByDescription(const ID3v2::Tag *tag, const String &d) // static
{
ID3v2::FrameList comments = tag->frameList("COMM");
@@ -144,8 +158,13 @@ void CommentsFrame::parseFields(const ByteVector &data)
ByteVectorList l = ByteVectorList::split(data.mid(4), textDelimiter(d->textEncoding), byteAlign, 2);
if(l.size() == 2) {
d->description = String(l.front(), d->textEncoding);
d->text = String(l.back(), d->textEncoding);
if(d->textEncoding == String::Latin1) {
d->description = Tag::latin1StringHandler()->parse(l.front());
d->text = Tag::latin1StringHandler()->parse(l.back());
} else {
d->description = String(l.front(), d->textEncoding);
d->text = String(l.back(), d->textEncoding);
}
}
}

View File

@@ -136,6 +136,17 @@ namespace TagLib {
*/
void setTextEncoding(String::Type encoding);
/*!
* Parses this frame as PropertyMap with a single key.
* - if description() is empty or "COMMENT", the key will be "COMMENT"
* - if description() is not a valid PropertyMap key, the frame will be
* marked unsupported by an entry "COMM/<description>" in the unsupportedData()
* attribute of the returned map.
* - otherwise, the key will be "COMMENT:<description>"
* - The single value will be the frame's text().
*/
PropertyMap asProperties() const;
/*!
* 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

View File

@@ -0,0 +1,162 @@
/***************************************************************************
copyright : (C) 2012 by Rupert Daniel
email : rupert@cancelmonday.com
***************************************************************************/
/***************************************************************************
* This library is free software; you can redistribute it and/or modify *
* it under the terms of the GNU Lesser General Public License version *
* 2.1 as published by the Free Software Foundation. *
* *
* This library is distributed in the hope that it will be useful, but *
* WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
* Lesser General Public License for more details. *
* *
* You should have received a copy of the GNU Lesser General Public *
* License along with this library; if not, write to the Free Software *
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
* 02110-1301 USA *
* *
* Alternatively, this file is available under the Mozilla Public *
* License Version 1.1. You may obtain a copy of the License at *
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#include <tdebug.h>
#include "ownershipframe.h"
#include <id3v2tag.h>
using namespace TagLib;
using namespace ID3v2;
class OwnershipFrame::OwnershipFramePrivate
{
public:
String pricePaid;
String datePurchased;
String seller;
String::Type textEncoding;
};
////////////////////////////////////////////////////////////////////////////////
// public members
////////////////////////////////////////////////////////////////////////////////
OwnershipFrame::OwnershipFrame(String::Type encoding) : Frame("OWNE")
{
d = new OwnershipFramePrivate;
d->textEncoding = encoding;
}
OwnershipFrame::OwnershipFrame(const ByteVector &data) : Frame(data)
{
d = new OwnershipFramePrivate;
setData(data);
}
OwnershipFrame::~OwnershipFrame()
{
delete d;
}
String OwnershipFrame::toString() const
{
return "pricePaid=" + d->pricePaid + " datePurchased=" + d->datePurchased + " seller=" + d->seller;
}
String OwnershipFrame::pricePaid() const
{
return d->pricePaid;
}
void OwnershipFrame::setPricePaid(const String &s)
{
d->pricePaid = s;
}
String OwnershipFrame::datePurchased() const
{
return d->datePurchased;
}
void OwnershipFrame::setDatePurchased(const String &s)
{
d->datePurchased = s;
}
String OwnershipFrame::seller() const
{
return d->seller;
}
void OwnershipFrame::setSeller(const String &s)
{
d->seller = s;
}
String::Type OwnershipFrame::textEncoding() const
{
return d->textEncoding;
}
void OwnershipFrame::setTextEncoding(String::Type encoding)
{
d->textEncoding = encoding;
}
////////////////////////////////////////////////////////////////////////////////
// protected members
////////////////////////////////////////////////////////////////////////////////
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));
else
d->seller = String(data.mid(pos), d->textEncoding);
}
ByteVector OwnershipFrame::renderFields() const
{
ByteVector v;
v.append(char(d->textEncoding));
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));
return v;
}
////////////////////////////////////////////////////////////////////////////////
// private members
////////////////////////////////////////////////////////////////////////////////
OwnershipFrame::OwnershipFrame(const ByteVector &data, Header *h) : Frame(h)
{
d = new OwnershipFramePrivate;
parseFields(fieldData(data));
}

View File

@@ -0,0 +1,151 @@
/***************************************************************************
copyright : (C) 2012 by Rupert Daniel
email : rupert@cancelmonday.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_OWNERSHIPFRAME_H
#define TAGLIB_OWNERSHIPFRAME_H
#include "id3v2frame.h"
#include "taglib_export.h"
namespace TagLib {
namespace ID3v2 {
//! An implementation of ID3v2 "ownership"
/*!
* This implements the ID3v2 ownership (OWNE frame). It consists of
* a price paid, a date purchased (YYYYMMDD) and the name of the seller.
*/
class TAGLIB_EXPORT OwnershipFrame : public Frame
{
friend class FrameFactory;
public:
/*!
* Construct an empty ownership frame.
*/
explicit OwnershipFrame(String::Type encoding = String::Latin1);
/*!
* Construct a ownership based on the data in \a data.
*/
explicit OwnershipFrame(const ByteVector &data);
/*!
* Destroys this OwnershipFrame instance.
*/
virtual ~OwnershipFrame();
/*!
* Returns the text of this popularimeter.
*
* \see text()
*/
virtual String toString() const;
/*!
* Returns the date purchased.
*
* \see setDatePurchased()
*/
String datePurchased() const;
/*!
* Set the date purchased.
*
* \see datePurchased()
*/
void setDatePurchased(const String &datePurchased);
/*!
* Returns the price paid.
*
* \see setPricePaid()
*/
String pricePaid() const;
/*!
* Set the price paid.
*
* \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
* or read from the frame when parsed.
*
* \see setTextEncoding()
* \see render()
*/
String::Type textEncoding() const;
/*!
* Sets the text encoding to be used when rendering this frame to
* \a encoding.
*
* \see textEncoding()
* \see render()
*/
void setTextEncoding(String::Type encoding);
protected:
// Reimplementations.
virtual void parseFields(const ByteVector &data);
virtual ByteVector renderFields() const;
private:
/*!
* The constructor used by the FrameFactory.
*/
OwnershipFrame(const ByteVector &data, Header *h);
OwnershipFrame(const OwnershipFrame &);
OwnershipFrame &operator=(const OwnershipFrame &);
class OwnershipFramePrivate;
OwnershipFramePrivate *d;
};
}
}
#endif

View File

@@ -98,7 +98,7 @@ void PrivateFrame::parseFields(const ByteVector &data)
}
// Owner identifier is assumed to be Latin1
const int byteAlign = 1;
const int endOfOwner = data.find(textDelimiter(String::Latin1), 0, byteAlign);

View File

@@ -25,8 +25,9 @@
#include <tbytevectorlist.h>
#include <id3v2tag.h>
#include "textidentificationframe.h"
#include "tpropertymap.h"
#include "id3v1genres.h"
using namespace TagLib;
using namespace ID3v2;
@@ -57,6 +58,32 @@ TextIdentificationFrame::TextIdentificationFrame(const ByteVector &data) :
setData(data);
}
TextIdentificationFrame *TextIdentificationFrame::createTIPLFrame(const PropertyMap &properties) // static
{
TextIdentificationFrame *frame = new TextIdentificationFrame("TIPL");
StringList l;
for(PropertyMap::ConstIterator it = properties.begin(); it != properties.end(); ++it){
l.append(it->first);
l.append(it->second.toString(",")); // comma-separated list of names
}
frame->setText(l);
return frame;
}
TextIdentificationFrame *TextIdentificationFrame::createTMCLFrame(const PropertyMap &properties) // static
{
TextIdentificationFrame *frame = new TextIdentificationFrame("TMCL");
StringList l;
for(PropertyMap::ConstIterator it = properties.begin(); it != properties.end(); ++it){
if(!it->first.startsWith(instrumentPrefix)) // should not happen
continue;
l.append(it->first.substr(instrumentPrefix.size()));
l.append(it->second.toString(","));
}
frame->setText(l);
return frame;
}
TextIdentificationFrame::~TextIdentificationFrame()
{
delete d;
@@ -92,6 +119,61 @@ void TextIdentificationFrame::setTextEncoding(String::Type encoding)
d->textEncoding = encoding;
}
// array of allowed TIPL prefixes and their corresponding key value
static const TagLib::uint involvedPeopleSize = 5;
static const char* involvedPeople[][2] = {
{"ARRANGER", "ARRANGER"},
{"ENGINEER", "ENGINEER"},
{"PRODUCER", "PRODUCER"},
{"DJ-MIX", "DJMIXER"},
{"MIX", "MIXER"},
};
const KeyConversionMap &TextIdentificationFrame::involvedPeopleMap() // static
{
static KeyConversionMap m;
if(m.isEmpty())
for(uint i = 0; i < involvedPeopleSize; ++i)
m.insert(involvedPeople[i][1], involvedPeople[i][0]);
return m;
}
PropertyMap TextIdentificationFrame::asProperties() const
{
if(frameID() == "TIPL")
return makeTIPLProperties();
if(frameID() == "TMCL")
return makeTMCLProperties();
PropertyMap map;
String tagName = frameIDToKey(frameID());
if(tagName.isNull()) {
map.unsupportedData().append(frameID());
return map;
}
StringList values = fieldList();
if(tagName == "GENRE") {
// Special case: Support ID3v1-style genre numbers. They are not officially supported in
// ID3v2, however it seems that still a lot of programs use them.
for(StringList::Iterator it = values.begin(); it != values.end(); ++it) {
bool ok = false;
int test = it->toInt(&ok); // test if the genre value is an integer
if(ok)
*it = ID3v1::genre(test);
}
} else if(tagName == "DATE") {
for(StringList::Iterator it = values.begin(); it != values.end(); ++it) {
// ID3v2 specifies ISO8601 timestamps which contain a 'T' as separator between date and time.
// Since this is unusual in other formats, the T is removed.
int tpos = it->find("T");
if(tpos != -1)
(*it)[tpos] = ' ';
}
}
PropertyMap ret;
ret.insert(tagName, values);
return ret;
}
////////////////////////////////////////////////////////////////////////////////
// TextIdentificationFrame protected members
////////////////////////////////////////////////////////////////////////////////
@@ -131,8 +213,10 @@ void TextIdentificationFrame::parseFields(const ByteVector &data)
for(ByteVectorList::Iterator it = l.begin(); it != l.end(); it++) {
if(!(*it).isEmpty()) {
String s(*it, d->textEncoding);
d->fieldList.append(s);
if(d->textEncoding == String::Latin1)
d->fieldList.append(Tag::latin1StringHandler()->parse(*it));
else
d->fieldList.append(String(*it, d->textEncoding));
}
}
}
@@ -170,6 +254,55 @@ TextIdentificationFrame::TextIdentificationFrame(const ByteVector &data, Header
parseFields(fieldData(data));
}
PropertyMap TextIdentificationFrame::makeTIPLProperties() const
{
PropertyMap map;
if(fieldList().size() % 2 != 0){
// according to the ID3 spec, TIPL must contain an even number of entries
map.unsupportedData().append(frameID());
return map;
}
StringList l = fieldList();
for(StringList::ConstIterator it = l.begin(); it != l.end(); ++it) {
bool found = false;
for(uint i = 0; i < involvedPeopleSize; ++i)
if(*it == involvedPeople[i][0]) {
map.insert(involvedPeople[i][1], (++it)->split(","));
found = true;
break;
}
if(!found){
// invalid involved role -> mark whole frame as unsupported in order to be consisten with writing
map.clear();
map.unsupportedData().append(frameID());
return map;
}
}
return map;
}
PropertyMap TextIdentificationFrame::makeTMCLProperties() const
{
PropertyMap map;
if(fieldList().size() % 2 != 0){
// according to the ID3 spec, TMCL must contain an even number of entries
map.unsupportedData().append(frameID());
return map;
}
StringList l = fieldList();
for(StringList::ConstIterator it = l.begin(); it != l.end(); ++it) {
String instrument = it->upper();
if(instrument.isNull()) {
// instrument is not a valid key -> frame unsupported
map.clear();
map.unsupportedData().append(frameID());
return map;
}
map.insert(L"PERFORMER:" + instrument, (++it)->split(","));
}
return map;
}
////////////////////////////////////////////////////////////////////////////////
// UserTextIdentificationFrame public members
////////////////////////////////////////////////////////////////////////////////
@@ -191,6 +324,14 @@ UserTextIdentificationFrame::UserTextIdentificationFrame(const ByteVector &data)
checkFields();
}
UserTextIdentificationFrame::UserTextIdentificationFrame(const String &description, const StringList &values, String::Type encoding) :
TextIdentificationFrame("TXXX", encoding),
d(0)
{
setDescription(description);
setText(values);
}
String UserTextIdentificationFrame::toString() const
{
return "[" + description() + "] " + fieldList().toString();
@@ -238,6 +379,23 @@ void UserTextIdentificationFrame::setDescription(const String &s)
TextIdentificationFrame::setText(l);
}
PropertyMap UserTextIdentificationFrame::asProperties() const
{
String tagName = description();
PropertyMap map;
String key = tagName.upper();
if(key.isNull()) // this frame's description is not a valid PropertyMap key -> add to unsupported list
map.unsupportedData().append(L"TXXX/" + description());
else {
StringList v = fieldList();
for(StringList::ConstIterator it = v.begin(); it != v.end(); ++it)
if(*it != description())
map.insert(key, *it);
}
return map;
}
UserTextIdentificationFrame *UserTextIdentificationFrame::find(
ID3v2::Tag *tag, const String &description) // static
{

View File

@@ -27,6 +27,7 @@
#define TAGLIB_TEXTIDENTIFICATIONFRAME_H
#include "tstringlist.h"
#include "tmap.h"
#include "taglib_export.h"
#include "id3v2frame.h"
@@ -36,6 +37,7 @@ namespace TagLib {
namespace ID3v2 {
class Tag;
typedef Map<String, String> KeyConversionMap;
//! An ID3v2 text identification frame implementation
@@ -123,6 +125,20 @@ namespace TagLib {
*/
explicit TextIdentificationFrame(const ByteVector &data);
/*!
* This is a special factory method to create a TIPL (involved people list)
* frame from the given \a properties. Will parse key=[list of values] data
* into the TIPL format as specified in the ID3 standard.
*/
static TextIdentificationFrame *createTIPLFrame(const PropertyMap &properties);
/*!
* This is a special factory method to create a TMCL (musician credits list)
* frame from the given \a properties. Will parse key=[list of values] data
* into the TMCL format as specified in the ID3 standard, where key should be
* of the form instrumentPrefix:instrument.
*/
static TextIdentificationFrame *createTMCLFrame(const PropertyMap &properties);
/*!
* Destroys this TextIdentificationFrame instance.
*/
@@ -173,6 +189,14 @@ namespace TagLib {
*/
StringList fieldList() const;
/*!
* Returns a KeyConversionMap mapping a role as it would be used in a PropertyMap
* to the corresponding key used in a TIPL ID3 frame to describe that role.
*/
static const KeyConversionMap &involvedPeopleMap();
PropertyMap asProperties() const;
protected:
// Reimplementations.
@@ -188,6 +212,16 @@ namespace TagLib {
TextIdentificationFrame(const TextIdentificationFrame &);
TextIdentificationFrame &operator=(const TextIdentificationFrame &);
/*!
* Parses the special structure of a TIPL frame
* Only the whitelisted roles "ARRANGER", "ENGINEER", "PRODUCER",
* "DJMIXER" (ID3: "DJ-MIX") and "MIXER" (ID3: "MIX") are allowed.
*/
PropertyMap makeTIPLProperties() const;
/*!
* Parses the special structure of a TMCL frame.
*/
PropertyMap makeTMCLProperties() const;
class TextIdentificationFramePrivate;
TextIdentificationFramePrivate *d;
};
@@ -218,6 +252,12 @@ namespace TagLib {
*/
explicit UserTextIdentificationFrame(const ByteVector &data);
/*!
* Creates a user defined text identification frame with the given \a description
* and \a values.
*/
UserTextIdentificationFrame(const String &description, const StringList &values, String::Type encoding = String::UTF8);
virtual String toString() const;
/*!
@@ -236,6 +276,21 @@ namespace TagLib {
void setText(const String &text);
void setText(const StringList &fields);
/*!
* A UserTextIdentificationFrame is parsed into a PropertyMap as follows:
* - the key is the frame's description, uppercased
* - if the description contains '::', only the substring after that
* separator is considered as key (compatibility with exfalso)
* - if the above rules don't yield a valid key (e.g. containing non-ASCII
* characters), the returned map will contain an entry "TXXX/<description>"
* in its unsupportedData() list.
* - The values will be copies of the fieldList().
* - If the description() appears as value in fieldList(), it will be omitted
* in the value list, in order to be compatible with TagLib which copies
* the description() into the fieldList().
*/
PropertyMap asProperties() const;
/*!
* Searches for the user defined text frame with the description \a description
* in \a tag. This returns null if no matching frames were found.

View File

@@ -100,7 +100,7 @@ namespace TagLib {
private:
UniqueFileIdentifierFrame(const UniqueFileIdentifierFrame &);
UniqueFileIdentifierFrame &operator=(UniqueFileIdentifierFrame &);
UniqueFileIdentifierFrame &operator=(const UniqueFileIdentifierFrame &);
UniqueFileIdentifierFrame(const ByteVector &data, Header *h);

View File

@@ -27,7 +27,9 @@
#include "unsynchronizedlyricsframe.h"
#include <tbytevectorlist.h>
#include <id3v2tag.h>
#include <tdebug.h>
#include <tpropertymap.h>
using namespace TagLib;
using namespace ID3v2;
@@ -111,6 +113,30 @@ void UnsynchronizedLyricsFrame::setTextEncoding(String::Type encoding)
d->textEncoding = encoding;
}
PropertyMap UnsynchronizedLyricsFrame::asProperties() const
{
PropertyMap map;
String key = description().upper();
if(key.isEmpty() || key.upper() == "LYRICS")
map.insert("LYRICS", text());
else if(key.isNull())
map.unsupportedData().append(L"USLT/" + description());
else
map.insert("LYRICS:" + key, text());
return map;
}
UnsynchronizedLyricsFrame *UnsynchronizedLyricsFrame::findByDescription(const ID3v2::Tag *tag, const String &d) // static
{
ID3v2::FrameList lyrics = tag->frameList("USLT");
for(ID3v2::FrameList::ConstIterator it = lyrics.begin(); it != lyrics.end(); ++it){
UnsynchronizedLyricsFrame *frame = dynamic_cast<UnsynchronizedLyricsFrame *>(*it);
if(frame && frame->description() == d)
return frame;
}
return 0;
}
////////////////////////////////////////////////////////////////////////////////
// protected members
////////////////////////////////////////////////////////////////////////////////
@@ -132,8 +158,13 @@ void UnsynchronizedLyricsFrame::parseFields(const ByteVector &data)
ByteVectorList::split(data.mid(4), textDelimiter(d->textEncoding), byteAlign, 2);
if(l.size() == 2) {
d->description = String(l.front(), d->textEncoding);
d->text = String(l.back(), d->textEncoding);
if(d->textEncoding == String::Latin1) {
d->description = Tag::latin1StringHandler()->parse(l.front());
d->text = Tag::latin1StringHandler()->parse(l.back());
} else {
d->description = String(l.front(), d->textEncoding);
d->text = String(l.back(), d->textEncoding);
}
}
}

View File

@@ -134,6 +134,28 @@ namespace TagLib {
*/
void setTextEncoding(String::Type encoding);
/*! Parses this frame as PropertyMap with a single key.
* - if description() is empty or "LYRICS", the key will be "LYRICS"
* - if description() is not a valid PropertyMap key, the frame will be
* marked unsupported by an entry "USLT/<description>" in the unsupportedData()
* attribute of the returned map.
* - otherwise, the key will be "LYRICS:<description>"
* - The single value will be the frame's text().
* Note that currently the language() field is not supported by the PropertyMap
* interface.
*/
PropertyMap asProperties() const;
/*!
* LyricsFrames each have a unique description. This searches for a lyrics
* frame with the decription \a d and returns a pointer to it. If no
* frame is found that matches the given description null is returned.
*
* \see description()
*/
static UnsynchronizedLyricsFrame *findByDescription(const Tag *tag, const String &d);
protected:
// Reimplementations.

View File

@@ -26,8 +26,10 @@
***************************************************************************/
#include "urllinkframe.h"
#include "id3v2tag.h"
#include <tdebug.h>
#include <tstringlist.h>
#include <tpropertymap.h>
using namespace TagLib;
using namespace ID3v2;
@@ -78,6 +80,18 @@ String UrlLinkFrame::toString() const
return url();
}
PropertyMap UrlLinkFrame::asProperties() const
{
String key = frameIDToKey(frameID());
PropertyMap map;
if(key.isNull())
// unknown W*** frame - this normally shouldn't happen
map.unsupportedData().append(frameID());
else
map.insert(key, url());
return map;
}
void UrlLinkFrame::parseFields(const ByteVector &data)
{
d->url = String(data);
@@ -139,6 +153,30 @@ void UserUrlLinkFrame::setDescription(const String &s)
d->description = s;
}
PropertyMap UserUrlLinkFrame::asProperties() const
{
PropertyMap map;
String key = description().upper();
if(key.isEmpty() || key.upper() == "URL")
map.insert("URL", url());
else if(key.isNull())
map.unsupportedData().append(L"WXXX/" + description());
else
map.insert("URL:" + key, url());
return map;
}
UserUrlLinkFrame *UserUrlLinkFrame::find(ID3v2::Tag *tag, const String &description) // static
{
FrameList l = tag->frameList("WXXX");
for(FrameList::Iterator it = l.begin(); it != l.end(); ++it) {
UserUrlLinkFrame *f = dynamic_cast<UserUrlLinkFrame *>(*it);
if(f && f->description() == description)
return f;
}
return 0;
}
void UserUrlLinkFrame::parseFields(const ByteVector &data)
{
if(data.size() < 2) {

View File

@@ -68,6 +68,7 @@ namespace TagLib {
virtual void setText(const String &s);
virtual String toString() const;
PropertyMap asProperties() const;
protected:
virtual void parseFields(const ByteVector &data);
@@ -150,6 +151,22 @@ namespace TagLib {
*/
void setDescription(const String &s);
/*!
* Parses the UserUrlLinkFrame as PropertyMap. The description() is taken as key,
* and the URL as single value.
* - if description() is empty, the key will be "URL".
* - otherwise, if description() is not a valid key (e.g. containing non-ASCII
* characters), the returned map will contain an entry "WXXX/<description>"
* in its unsupportedData() list.
*/
PropertyMap asProperties() const;
/*!
* Searches for the user defined url frame with the description \a description
* in \a tag. This returns null if no matching frames were found.
*/
static UserUrlLinkFrame *find(Tag *tag, const String &description);
protected:
virtual void parseFields(const ByteVector &data);
virtual ByteVector renderFields() const;

View File

@@ -23,7 +23,7 @@
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#ifndef HAVE_ZLIB
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
@@ -36,8 +36,15 @@
#include <tdebug.h>
#include <tstringlist.h>
#include "id3v2tag.h"
#include "id3v2frame.h"
#include "id3v2synchdata.h"
#include "tpropertymap.h"
#include "frames/textidentificationframe.h"
#include "frames/urllinkframe.h"
#include "frames/unsynchronizedlyricsframe.h"
#include "frames/commentsframe.h"
#include "frames/unknownframe.h"
using namespace TagLib;
using namespace ID3v2;
@@ -65,7 +72,7 @@ namespace
return false;
for(ByteVector::ConstIterator it = frameID.begin(); it != frameID.end(); it++) {
if( (*it < 'A' || *it > 'Z') && (*it < '1' || *it > '9') ) {
if( (*it < 'A' || *it > 'Z') && (*it < '0' || *it > '9') ) {
return false;
}
}
@@ -95,10 +102,56 @@ ByteVector Frame::textDelimiter(String::Type t)
return d;
}
const String Frame::instrumentPrefix("PERFORMER:");
const String Frame::commentPrefix("COMMENT:");
const String Frame::lyricsPrefix("LYRICS:");
const String Frame::urlPrefix("URL:");
////////////////////////////////////////////////////////////////////////////////
// public members
////////////////////////////////////////////////////////////////////////////////
Frame *Frame::createTextualFrame(const String &key, const StringList &values) //static
{
// check if the key is contained in the key<=>frameID mapping
ByteVector frameID = keyToFrameID(key);
if(!frameID.isNull()) {
if(frameID[0] == 'T'){ // text frame
TextIdentificationFrame *frame = new TextIdentificationFrame(frameID, String::UTF8);
frame->setText(values);
return frame;
} else if(values.size() == 1){ // URL frame (not WXXX); support only one value
UrlLinkFrame* frame = new UrlLinkFrame(frameID);
frame->setUrl(values.front());
return frame;
}
}
// now we check if it's one of the "special" cases:
// -LYRICS: depending on the number of values, use USLT or TXXX (with description=LYRICS)
if((key == "LYRICS" || key.startsWith(lyricsPrefix)) && values.size() == 1){
UnsynchronizedLyricsFrame *frame = new UnsynchronizedLyricsFrame();
frame->setDescription(key == "LYRICS" ? key : key.substr(lyricsPrefix.size()));
frame->setText(values.front());
return frame;
}
// -URL: depending on the number of values, use WXXX or TXXX (with description=URL)
if((key == "URL" || key.startsWith(urlPrefix)) && values.size() == 1){
UserUrlLinkFrame *frame = new UserUrlLinkFrame(String::UTF8);
frame->setDescription(key == "URL" ? key : key.substr(urlPrefix.size()));
frame->setUrl(values.front());
return frame;
}
// -COMMENT: depending on the number of values, use COMM or TXXX (with description=COMMENT)
if((key == "COMMENT" || key.startsWith(commentPrefix)) && values.size() == 1){
CommentsFrame *frame = new CommentsFrame(String::UTF8);
frame->setDescription(key == "COMMENT" ? key : key.substr(commentPrefix.size()));
frame->setText(values.front());
return frame;
}
// if non of the above cases apply, we use a TXXX frame with the key as description
return new UserTextIdentificationFrame(key, values, String::UTF8);
}
Frame::~Frame()
{
delete d;
@@ -221,7 +274,11 @@ String Frame::readStringField(const ByteVector &data, String::Type encoding, int
if(end < *position)
return String::null;
String str = String(data.mid(*position, end - *position), encoding);
String str;
if(encoding == String::Latin1)
str = Tag::latin1StringHandler()->parse(data.mid(*position, end - *position));
else
str = String(data.mid(*position, end - *position), encoding);
*position = end + delimiter.size();
@@ -262,6 +319,163 @@ String::Type Frame::checkTextEncoding(const StringList &fields, String::Type enc
return checkEncoding(fields, encoding, header()->version());
}
static const TagLib::uint frameTranslationSize = 51;
static const char *frameTranslation[][2] = {
// Text information frames
{ "TALB", "ALBUM"},
{ "TBPM", "BPM" },
{ "TCOM", "COMPOSER" },
{ "TCON", "GENRE" },
{ "TCOP", "COPYRIGHT" },
{ "TDEN", "ENCODINGTIME" },
{ "TDLY", "PLAYLISTDELAY" },
{ "TDOR", "ORIGINALDATE" },
{ "TDRC", "DATE" },
// { "TRDA", "DATE" }, // id3 v2.3, replaced by TDRC in v2.4
// { "TDAT", "DATE" }, // id3 v2.3, replaced by TDRC in v2.4
// { "TYER", "DATE" }, // id3 v2.3, replaced by TDRC in v2.4
// { "TIME", "DATE" }, // id3 v2.3, replaced by TDRC in v2.4
{ "TDRL", "RELEASEDATE" },
{ "TDTG", "TAGGINGDATE" },
{ "TENC", "ENCODEDBY" },
{ "TEXT", "LYRICIST" },
{ "TFLT", "FILETYPE" },
//{ "TIPL", "INVOLVEDPEOPLE" }, handled separately
{ "TIT1", "CONTENTGROUP" },
{ "TIT2", "TITLE"},
{ "TIT3", "SUBTITLE" },
{ "TKEY", "INITIALKEY" },
{ "TLAN", "LANGUAGE" },
{ "TLEN", "LENGTH" },
//{ "TMCL", "MUSICIANCREDITS" }, handled separately
{ "TMED", "MEDIATYPE" },
{ "TMOO", "MOOD" },
{ "TOAL", "ORIGINALALBUM" },
{ "TOFN", "ORIGINALFILENAME" },
{ "TOLY", "ORIGINALLYRICIST" },
{ "TOPE", "ORIGINALARTIST" },
{ "TOWN", "OWNER" },
{ "TPE1", "ARTIST"},
{ "TPE2", "ALBUMARTIST" }, // id3's spec says 'PERFORMER', but most programs use 'ALBUMARTIST'
{ "TPE3", "CONDUCTOR" },
{ "TPE4", "REMIXER" }, // could also be ARRANGER
{ "TPOS", "DISCNUMBER" },
{ "TPRO", "PRODUCEDNOTICE" },
{ "TPUB", "PUBLISHER" },
{ "TRCK", "TRACKNUMBER" },
{ "TRSN", "RADIOSTATION" },
{ "TRSO", "RADIOSTATIONOWNER" },
{ "TSOA", "ALBUMSORT" },
{ "TSOP", "ARTISTSORT" },
{ "TSOT", "TITLESORT" },
{ "TSO2", "ALBUMARTISTSORT" }, // non-standard, used by iTunes
{ "TSRC", "ISRC" },
{ "TSSE", "ENCODING" },
// URL frames
{ "WCOP", "COPYRIGHTURL" },
{ "WOAF", "FILEWEBPAGE" },
{ "WOAR", "ARTISTWEBPAGE" },
{ "WOAS", "AUDIOSOURCEWEBPAGE" },
{ "WORS", "RADIOSTATIONWEBPAGE" },
{ "WPAY", "PAYMENTWEBPAGE" },
{ "WPUB", "PUBLISHERWEBPAGE" },
//{ "WXXX", "URL"}, handled specially
// Other frames
{ "COMM", "COMMENT" },
//{ "USLT", "LYRICS" }, handled specially
};
Map<ByteVector, String> &idMap()
{
static Map<ByteVector, String> m;
if(m.isEmpty())
for(size_t i = 0; i < frameTranslationSize; ++i)
m[frameTranslation[i][0]] = frameTranslation[i][1];
return m;
}
// list of deprecated frames and their successors
static const TagLib::uint deprecatedFramesSize = 4;
static const char *deprecatedFrames[][2] = {
{"TRDA", "TDRC"}, // 2.3 -> 2.4 (http://en.wikipedia.org/wiki/ID3)
{"TDAT", "TDRC"}, // 2.3 -> 2.4
{"TYER", "TDRC"}, // 2.3 -> 2.4
{"TIME", "TDRC"}, // 2.3 -> 2.4
};
Map<ByteVector,ByteVector> &deprecationMap()
{
static Map<ByteVector,ByteVector> depMap;
if(depMap.isEmpty())
for(TagLib::uint i = 0; i < deprecatedFramesSize; ++i)
depMap[deprecatedFrames[i][0]] = deprecatedFrames[i][1];
return depMap;
}
String Frame::frameIDToKey(const ByteVector &id)
{
Map<ByteVector, String> &m = idMap();
if(m.contains(id))
return m[id];
if(deprecationMap().contains(id))
return m[deprecationMap()[id]];
return String::null;
}
ByteVector Frame::keyToFrameID(const String &s)
{
static Map<String, ByteVector> m;
if(m.isEmpty())
for(size_t i = 0; i < frameTranslationSize; ++i)
m[frameTranslation[i][1]] = frameTranslation[i][0];
if(m.contains(s.upper()))
return m[s];
return ByteVector::null;
}
PropertyMap Frame::asProperties() const
{
if(dynamic_cast< const UnknownFrame *>(this)) {
PropertyMap m;
m.unsupportedData().append("UNKNOWN/" + frameID());
return m;
}
const ByteVector &id = frameID();
// workaround until this function is virtual
if(id == "TXXX")
return dynamic_cast< const UserTextIdentificationFrame* >(this)->asProperties();
else if(id[0] == 'T')
return dynamic_cast< const TextIdentificationFrame* >(this)->asProperties();
else if(id == "WXXX")
return dynamic_cast< const UserUrlLinkFrame* >(this)->asProperties();
else if(id[0] == 'W')
return dynamic_cast< const UrlLinkFrame* >(this)->asProperties();
else if(id == "COMM")
return dynamic_cast< const CommentsFrame* >(this)->asProperties();
else if(id == "USLT")
return dynamic_cast< const UnsynchronizedLyricsFrame* >(this)->asProperties();
PropertyMap m;
m.unsupportedData().append(id);
return m;
}
void Frame::splitProperties(const PropertyMap &original, PropertyMap &singleFrameProperties,
PropertyMap &tiplProperties, PropertyMap &tmclProperties)
{
singleFrameProperties.clear();
tiplProperties.clear();
tmclProperties.clear();
for(PropertyMap::ConstIterator it = original.begin(); it != original.end(); ++it) {
if(TextIdentificationFrame::involvedPeopleMap().contains(it->first))
tiplProperties.insert(it->first, it->second);
else if(it->first.startsWith(TextIdentificationFrame::instrumentPrefix))
tmclProperties.insert(it->first, it->second);
else
singleFrameProperties.insert(it->first, it->second);
}
}
////////////////////////////////////////////////////////////////////////////////
// Frame::Header class
////////////////////////////////////////////////////////////////////////////////

View File

@@ -33,6 +33,7 @@
namespace TagLib {
class StringList;
class PropertyMap;
namespace ID3v2 {
@@ -56,6 +57,14 @@ namespace TagLib {
friend class FrameFactory;
public:
/*!
* Creates a textual frame which corresponds to a single key in the PropertyMap
* interface. These are all (User)TextIdentificationFrames except TIPL and TMCL,
* all (User)URLLinkFrames, CommentsFrames, and UnsynchronizedLyricsFrame.
*/
static Frame *createTextualFrame(const String &key, const StringList &values);
/*!
* Destroys this Frame instance.
*/
@@ -126,6 +135,28 @@ namespace TagLib {
*/
static ByteVector textDelimiter(String::Type t);
/*!
* The string with which an instrument name is prefixed to build a key in a PropertyMap;
* used to translate PropertyMaps to TMCL frames. In the current implementation, this
* is "PERFORMER:".
*/
static const String instrumentPrefix;
/*!
* The PropertyMap key prefix which triggers the use of a COMM frame instead of a TXXX
* frame for a non-standard key. In the current implementation, this is "COMMENT:".
*/
static const String commentPrefix;
/*!
* The PropertyMap key prefix which triggers the use of a USLT frame instead of a TXXX
* frame for a non-standard key. In the current implementation, this is "LYRICS:".
*/
static const String lyricsPrefix;
/*!
* The PropertyMap key prefix which triggers the use of a WXXX frame instead of a TXX
* frame for a non-standard key. In the current implementation, this is "URL:".
*/
static const String urlPrefix;
protected:
class Header;
@@ -222,6 +253,44 @@ namespace TagLib {
String::Type checkTextEncoding(const StringList &fields,
String::Type encoding) const;
/*!
* Parses the contents of this frame as PropertyMap. If that fails, the returend
* PropertyMap will be empty, and its unsupportedData() will contain this frame's
* ID.
* BIC: Will be a virtual function in future releases.
*/
PropertyMap asProperties() const;
/*!
* Returns an appropriate ID3 frame ID for the given free-form tag key. This method
* will return ByteVector::null if no specialized translation is found.
*/
static ByteVector keyToFrameID(const String &);
/*!
* Returns a free-form tag name for the given ID3 frame ID. Note that this does not work
* for general frame IDs such as TXXX or WXXX; in such a case String::null is returned.
*/
static String frameIDToKey(const ByteVector &);
/*!
* This helper function splits the PropertyMap \a original into three ProperytMaps
* \a singleFrameProperties, \a tiplProperties, and \a tmclProperties, such that:
* - \a singleFrameProperties contains only of keys which can be represented with
* exactly one ID3 frame per key. In the current implementation
* this is everything except for the fixed "involved people" keys and keys of the
* form "TextIdentificationFrame::instrumentPrefix" + "instrument", which are
* mapped to a TMCL frame.
* - \a tiplProperties will consist of those keys that are present in
* TextIdentificationFrame::involvedPeopleMap()
* - \a tmclProperties contains the "musician credits" keys which should be mapped
* to a TMCL frame
*/
static void splitProperties(const PropertyMap &original, PropertyMap &singleFrameProperties,
PropertyMap &tiplProperties, PropertyMap &tmclProperties);
private:
Frame(const Frame &);
Frame &operator=(const Frame &);

View File

@@ -23,7 +23,7 @@
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#ifndef HAVE_ZLIB
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
@@ -44,6 +44,7 @@
#include "frames/unsynchronizedlyricsframe.h"
#include "frames/popularimeterframe.h"
#include "frames/privateframe.h"
#include "frames/ownershipframe.h"
using namespace TagLib;
using namespace ID3v2;
@@ -65,7 +66,7 @@ public:
}
};
FrameFactory *FrameFactory::factory = 0;
FrameFactory FrameFactory::factory;
////////////////////////////////////////////////////////////////////////////////
// public members
@@ -73,9 +74,7 @@ FrameFactory *FrameFactory::factory = 0;
FrameFactory *FrameFactory::instance()
{
if(!factory)
factory = new FrameFactory;
return factory;
return &factory;
}
Frame *FrameFactory::createFrame(const ByteVector &data, bool synchSafeInts) const
@@ -109,7 +108,7 @@ Frame *FrameFactory::createFrame(const ByteVector &origData, Header *tagHeader)
}
for(ByteVector::ConstIterator it = frameID.begin(); it != frameID.end(); it++) {
if( (*it < 'A' || *it > 'Z') && (*it < '1' || *it > '9') ) {
if( (*it < 'A' || *it > 'Z') && (*it < '0' || *it > '9') ) {
delete header;
return 0;
}
@@ -240,6 +239,14 @@ Frame *FrameFactory::createFrame(const ByteVector &origData, Header *tagHeader)
if(frameID == "PRIV")
return new PrivateFrame(data, header);
// Ownership (frames 4.22)
if(frameID == "OWNE") {
OwnershipFrame *f = new OwnershipFrame(data, header);
d->setTextEncoding(f);
return f;
}
return new UnknownFrame(data, header);
}
@@ -310,6 +317,7 @@ bool FrameFactory::updateFrame(Frame::Header *header) const
convertFrame("TBP", "TBPM", header);
convertFrame("TCM", "TCOM", header);
convertFrame("TCO", "TCON", header);
convertFrame("TCP", "TCMP", header);
convertFrame("TCR", "TCOP", header);
convertFrame("TDY", "TDLY", header);
convertFrame("TEN", "TENC", header);
@@ -332,7 +340,12 @@ bool FrameFactory::updateFrame(Frame::Header *header) const
convertFrame("TRC", "TSRC", header);
convertFrame("TRD", "TDRC", header);
convertFrame("TRK", "TRCK", header);
convertFrame("TS2", "TSO2", header);
convertFrame("TSA", "TSOA", header);
convertFrame("TSC", "TSOC", header);
convertFrame("TSP", "TSOP", header);
convertFrame("TSS", "TSSE", header);
convertFrame("TST", "TSOT", header);
convertFrame("TT1", "TIT1", header);
convertFrame("TT2", "TIT2", header);
convertFrame("TT3", "TIT3", header);

View File

@@ -123,8 +123,7 @@ namespace TagLib {
FrameFactory();
/*!
* Destroys the frame factory. In most cases this will never be called (as
* is typical of singletons).
* Destroys the frame factory.
*/
virtual ~FrameFactory();
@@ -155,7 +154,7 @@ namespace TagLib {
void updateGenre(TextIdentificationFrame *frame) const;
static FrameFactory *factory;
static FrameFactory factory;
class FrameFactoryPrivate;
FrameFactoryPrivate *d;

View File

@@ -23,7 +23,7 @@
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#include <ostream>
#include <iostream>
#include <bitset>
#include <tstring.h>

View File

@@ -23,7 +23,7 @@
* http://www.mozilla.org/MPL/ *
***************************************************************************/
#include <ostream>
#include <iostream>
#include "id3v2synchdata.h"

View File

@@ -24,18 +24,23 @@
***************************************************************************/
#include <tfile.h>
#include <tdebug.h>
#include "id3v2tag.h"
#include "id3v2header.h"
#include "id3v2extendedheader.h"
#include "id3v2footer.h"
#include "id3v2synchdata.h"
#include "tbytevector.h"
#include "id3v1genres.h"
#include "tpropertymap.h"
#include <tdebug.h>
#include "frames/textidentificationframe.h"
#include "frames/commentsframe.h"
#include "frames/urllinkframe.h"
#include "frames/uniquefileidentifierframe.h"
#include "frames/unsynchronizedlyricsframe.h"
#include "frames/unknownframe.h"
using namespace TagLib;
using namespace ID3v2;
@@ -65,8 +70,30 @@ public:
FrameListMap frameListMap;
FrameList frameList;
static const Latin1StringHandler *stringHandler;
};
static const Latin1StringHandler defaultStringHandler;
const ID3v2::Latin1StringHandler *ID3v2::Tag::TagPrivate::stringHandler = &defaultStringHandler;
////////////////////////////////////////////////////////////////////////////////
// StringHandler implementation
////////////////////////////////////////////////////////////////////////////////
Latin1StringHandler::Latin1StringHandler()
{
}
Latin1StringHandler::~Latin1StringHandler()
{
}
String Latin1StringHandler::parse(const ByteVector &data) const
{
return String(data, String::Latin1);
}
////////////////////////////////////////////////////////////////////////////////
// public members
////////////////////////////////////////////////////////////////////////////////
@@ -324,9 +351,99 @@ void ID3v2::Tag::removeFrame(Frame *frame, bool del)
void ID3v2::Tag::removeFrames(const ByteVector &id)
{
FrameList l = d->frameListMap[id];
for(FrameList::Iterator it = l.begin(); it != l.end(); ++it)
removeFrame(*it, true);
FrameList l = d->frameListMap[id];
for(FrameList::Iterator it = l.begin(); it != l.end(); ++it)
removeFrame(*it, true);
}
PropertyMap ID3v2::Tag::properties() const
{
PropertyMap properties;
for(FrameList::ConstIterator it = frameList().begin(); it != frameList().end(); ++it) {
PropertyMap props = (*it)->asProperties();
properties.merge(props);
}
return properties;
}
void ID3v2::Tag::removeUnsupportedProperties(const StringList &properties)
{
for(StringList::ConstIterator it = properties.begin(); it != properties.end(); ++it){
if(it->startsWith("UNKNOWN/")) {
String frameID = it->substr(String("UNKNOWN/").size());
if(frameID.size() != 4)
continue; // invalid specification
ByteVector id = frameID.data(String::Latin1);
// delete all unknown frames of given type
FrameList l = frameList(id);
for(FrameList::ConstIterator fit = l.begin(); fit != l.end(); fit++)
if (dynamic_cast<const UnknownFrame *>(*fit) != 0)
removeFrame(*fit);
} else if(it->size() == 4){
ByteVector id = it->data(String::Latin1);
removeFrames(id);
} else {
ByteVector id = it->substr(0,4).data(String::Latin1);
if(it->size() <= 5)
continue; // invalid specification
String description = it->substr(5);
Frame *frame = 0;
if(id == "TXXX")
frame = UserTextIdentificationFrame::find(this, description);
else if(id == "WXXX")
frame = UserUrlLinkFrame::find(this, description);
else if(id == "COMM")
frame = CommentsFrame::findByDescription(this, description);
else if(id == "USLT")
frame = UnsynchronizedLyricsFrame::findByDescription(this, description);
if(frame)
removeFrame(frame);
}
}
}
PropertyMap ID3v2::Tag::setProperties(const PropertyMap &origProps)
{
FrameList framesToDelete;
// we split up the PropertyMap into the "normal" keys and the "complicated" ones,
// which are those according to TIPL or TMCL frames.
PropertyMap properties;
PropertyMap tiplProperties;
PropertyMap tmclProperties;
Frame::splitProperties(origProps, properties, tiplProperties, tmclProperties);
for(FrameListMap::ConstIterator it = frameListMap().begin(); it != frameListMap().end(); ++it){
for(FrameList::ConstIterator lit = it->second.begin(); lit != it->second.end(); ++lit){
PropertyMap frameProperties = (*lit)->asProperties();
if(it->first == "TIPL") {
if (tiplProperties != frameProperties)
framesToDelete.append(*lit);
else
tiplProperties.erase(frameProperties);
} else if(it->first == "TMCL") {
if (tmclProperties != frameProperties)
framesToDelete.append(*lit);
else
tmclProperties.erase(frameProperties);
} else if(!properties.contains(frameProperties))
framesToDelete.append(*lit);
else
properties.erase(frameProperties);
}
}
for(FrameList::ConstIterator it = framesToDelete.begin(); it != framesToDelete.end(); ++it)
removeFrame(*it);
// now create remaining frames:
// start with the involved people list (TIPL)
if(!tiplProperties.isEmpty())
addFrame(TextIdentificationFrame::createTIPLFrame(tiplProperties));
// proceed with the musician credit list (TMCL)
if(!tmclProperties.isEmpty())
addFrame(TextIdentificationFrame::createTMCLFrame(tmclProperties));
// now create the "one key per frame" frames
for(PropertyMap::ConstIterator it = properties.begin(); it != properties.end(); ++it)
addFrame(Frame::createTextualFrame(it->first, it->second));
return PropertyMap(); // ID3 implements the complete PropertyMap interface, so an empty map is returned
}
ByteVector ID3v2::Tag::render() const
@@ -489,6 +606,19 @@ ByteVector ID3v2::Tag::render(int version) const
return d->header.render() + tagData;
}
Latin1StringHandler const *ID3v2::Tag::latin1StringHandler()
{
return TagPrivate::stringHandler;
}
void ID3v2::Tag::setLatin1StringHandler(const Latin1StringHandler *handler)
{
if(handler)
TagPrivate::stringHandler = handler;
else
TagPrivate::stringHandler = &defaultStringHandler;
}
////////////////////////////////////////////////////////////////////////////////
// protected members
////////////////////////////////////////////////////////////////////////////////
@@ -550,8 +680,9 @@ void ID3v2::Tag::parse(const ByteVector &origData)
// portion of the frame data.
if(data.at(frameDataPosition) == 0) {
if(d->header.footerPresent())
if(d->header.footerPresent()) {
debug("Padding *and* a footer found. This is not allowed by the spec.");
}
d->paddingSize = frameDataLength - frameDataPosition;
return;

View File

@@ -57,6 +57,36 @@ namespace TagLib {
typedef List<Frame *> FrameList;
typedef Map<ByteVector, FrameList> FrameListMap;
//! An abstraction for the ISO-8859-1 string to data encoding in ID3v2 tags.
/*!
* ID3v2 tag can store strings in ISO-8859-1 (Latin1), and TagLib only
* supports genuine ISO-8859-1 by default. However, in practice, non
* ISO-8859-1 encodings are often used instead of ISO-8859-1, such as
* Windows-1252 for western languages, Shift_JIS for Japanese and so on.
*
* Here is an option to read such tags by subclassing this class,
* reimplementing parse() and setting your reimplementation as the default
* with ID3v2::Tag::setStringHandler().
*
* \note Writing non-ISO-8859-1 tags is not implemented intentionally.
* Use UTF-16 or UTF-8 instead.
*
* \see ID3v2::Tag::setStringHandler()
*/
class TAGLIB_EXPORT Latin1StringHandler
{
public:
Latin1StringHandler();
virtual ~Latin1StringHandler();
/*!
* Decode a string from \a data. The default implementation assumes that
* \a data is an ISO-8859-1 (Latin1) character array.
*/
virtual String parse(const ByteVector &data) const;
};
//! The main class in the ID3v2 implementation
/*!
@@ -260,6 +290,56 @@ namespace TagLib {
*/
void removeFrames(const ByteVector &id);
/*!
* Implements the unified property interface -- export function.
* This function does some work to translate the hard-specified ID3v2
* frame types into a free-form string-to-stringlist PropertyMap:
* - if ID3v2 frame ID is known by Frame::frameIDToKey(), the returned
* key is used
* - if the frame ID is "TXXX" (user text frame), the description() is
* used as key
* - if the frame ID is "WXXX" (user url frame),
* - if the description is empty or "URL", the key "URL" is used
* - otherwise, the key "URL:<description>" is used;
* - if the frame ID is "COMM" (comments frame),
* - if the description is empty or "COMMENT", the key "COMMENT"
* is used
* - otherwise, the key "COMMENT:<description>" is used;
* - if the frame ID is "USLT" (unsynchronized lyrics),
* - if the description is empty or "LYRICS", the key "LYRICS" is used
* - otherwise, the key "LYRICS:<description>" is used;
* - if the frame ID is "TIPL" (involved peoples list), and if all the
* roles defined in the frame are known in TextIdentificationFrame::involvedPeopleMap(),
* then "<role>=<name>" will be contained in the returned obejct for each
* - if the frame ID is "TMCL" (musician credit list), then
* "PERFORMER:<instrument>=<name>" will be contained in the returned
* PropertyMap for each defined musician
* In any other case, the unsupportedData() of the returned object will contain
* the frame's ID and, in case of a frame ID which is allowed to appear more than
* once, the description, separated by a "/".
*
*/
PropertyMap properties() const;
/*!
* Removes unsupported frames given by \a properties. The elements of
* \a properties must be taken from properties().unsupportedData(); they
* are of one of the following forms:
* - a four-character frame ID, if the ID3 specification allows only one
* frame with that ID (thus, the frame is uniquely determined)
* - frameID + "/" + description(), when the ID is one of "TXXX", "WXXX",
* "COMM", or "USLT",
* - "UNKNOWN/" + frameID, for frames that could not be parsed by TagLib.
* In that case, *all* unknown frames with the given ID will be removed.
*/
void removeUnsupportedProperties(const StringList &properties);
/*!
* Implements the unified property interface -- import function.
* See the comments in properties().
*/
PropertyMap setProperties(const PropertyMap &);
/*!
* Render the tag back to binary data, suitable to be written to disk.
*/
@@ -273,6 +353,27 @@ namespace TagLib {
*/
// BIC: combine with the above method
ByteVector render(int version) const;
/*!
* Gets the current string handler that decides how the "Latin-1" data
* will be converted to and from binary data.
*
* \see Latin1StringHandler
*/
static Latin1StringHandler const *latin1StringHandler();
/*!
* Sets the string handler that decides how the "Latin-1" data will be
* converted to and from binary data.
* If the parameter \a handler is null, the previous handler is
* released and default ISO-8859-1 handler is restored.
*
* \note The caller is responsible for deleting the previous handler
* as needed after it is released.
*
* \see Latin1StringHandler
*/
static void setLatin1StringHandler(const Latin1StringHandler *handler);
protected:
/*!

View File

@@ -35,6 +35,7 @@
#include "mpegfile.h"
#include "mpegheader.h"
#include "tpropertymap.h"
using namespace TagLib;
@@ -133,6 +134,40 @@ TagLib::Tag *MPEG::File::tag() const
return &d->tag;
}
PropertyMap MPEG::File::properties() const
{
// once Tag::properties() is virtual, this case distinction could actually be done
// within TagUnion.
if(d->hasID3v2)
return d->tag.access<ID3v2::Tag>(ID3v2Index, false)->properties();
if(d->hasAPE)
return d->tag.access<APE::Tag>(APEIndex, false)->properties();
if(d->hasID3v1)
return d->tag.access<ID3v1::Tag>(ID3v1Index, false)->properties();
return PropertyMap();
}
void MPEG::File::removeUnsupportedProperties(const StringList &properties)
{
if(d->hasID3v2)
d->tag.access<ID3v2::Tag>(ID3v2Index, false)->removeUnsupportedProperties(properties);
else if(d->hasAPE)
d->tag.access<APE::Tag>(APEIndex, false)->removeUnsupportedProperties(properties);
else if(d->hasID3v1)
d->tag.access<ID3v1::Tag>(ID3v1Index, false)->removeUnsupportedProperties(properties);
}
PropertyMap MPEG::File::setProperties(const PropertyMap &properties)
{
if(d->hasID3v2)
return d->tag.access<ID3v2::Tag>(ID3v2Index, false)->setProperties(properties);
else if(d->hasAPE)
return d->tag.access<APE::Tag>(APEIndex, false)->setProperties(properties);
else if(d->hasID3v1)
return d->tag.access<ID3v1::Tag>(ID3v1Index, false)->setProperties(properties);
else
return d->tag.access<ID3v2::Tag>(ID3v2Index, true)->setProperties(properties);
}
MPEG::Properties *MPEG::File::audioProperties() const
{
return d->properties;
@@ -172,12 +207,12 @@ bool MPEG::File::save(int tags, bool stripOthers, int id3v2Version)
}
// Create the tags if we've been asked to. Copy the values from the tag that
// does exist into the new tag.
// does exist into the new tag, except if the existing tag is to be stripped.
if((tags & ID3v2) && ID3v1Tag())
if((tags & ID3v2) && ID3v1Tag() && !(stripOthers && !(tags & ID3v1)))
Tag::duplicate(ID3v1Tag(), ID3v2Tag(true), false);
if((tags & ID3v1) && d->tag[ID3v2Index])
if((tags & ID3v1) && d->tag[ID3v2Index] && !(stripOthers && !(tags & ID3v2)))
Tag::duplicate(ID3v2Tag(), ID3v1Tag(true), false);
bool success = true;

View File

@@ -28,6 +28,7 @@
#include "taglib_export.h"
#include "tfile.h"
#include "tag.h"
#include "mpegproperties.h"
@@ -98,6 +99,9 @@ namespace TagLib {
* file's audio properties will also be read using \a propertiesStyle. If
* false, \a propertiesStyle is ignored. The frames will be created using
* \a frameFactory.
*
* \note TagLib will *not* take ownership of the stream, the caller is
* responsible for deleting it after the File object.
*/
// BIC: merge with the above constructor
File(IOStream *stream, ID3v2::FrameFactory *frameFactory,
@@ -128,6 +132,23 @@ namespace TagLib {
*/
virtual Tag *tag() const;
/*!
* Implements the unified property interface -- export function.
* If the file contains more than one tag, only the
* first one (in the order ID3v2, APE, ID3v1) will be converted to the
* PropertyMap.
*/
PropertyMap properties() const;
void removeUnsupportedProperties(const StringList &properties);
/*!
* Implements the unified tag dictionary interface -- import function.
* As with the export, only one tag is taken into account. If the file
* has no tag at all, ID3v2 will be created.
*/
PropertyMap setProperties(const PropertyMap &);
/*!
* Returns the MPEG::Properties for this file. If no audio properties
* were read then this will return a null pointer.
@@ -192,9 +213,9 @@ namespace TagLib {
/*!
* Returns a pointer to the ID3v2 tag of the file.
*
* If \a create is false (the default) this will return a null pointer
* if there is no valid ID3v2 tag. If \a create is true it will create
* an ID3v2 tag if one does not exist.
* A tag will always be returned, regardless of whether there is a
* tag in the file or not. Use ID3v2::Tag::isEmpty() to check if
* the tag contains no data.
*
* \note The Tag <b>is still</b> owned by the MPEG::File and should not be
* deleted by the user. It will be deleted when the file (object) is
@@ -205,9 +226,9 @@ namespace TagLib {
/*!
* Returns a pointer to the ID3v1 tag of the file.
*
* If \a create is false (the default) this will return a null pointer
* if there is no valid ID3v1 tag. If \a create is true it will create
* an ID3v1 tag if one does not exist.
* A tag will always be returned, regardless of whether there is a
* tag in the file or not. Use Tag::isEmpty() to check if
* the tag contains no data.
*
* \note The Tag <b>is still</b> owned by the MPEG::File and should not be
* deleted by the user. It will be deleted when the file (object) is

View File

@@ -221,7 +221,7 @@ void MPEG::Properties::read()
double length = timePerFrame * d->xingHeader->totalFrames();
d->length = int(length);
d->bitrate = d->length > 0 ? d->xingHeader->totalSize() * 8 / length / 1000 : 0;
d->bitrate = d->length > 0 ? (int)(d->xingHeader->totalSize() * 8 / length / 1000) : 0;
}
else {
// Since there was no valid Xing header found, we hope that we're in a constant

View File

@@ -263,9 +263,9 @@ void Ogg::FLAC::File::scan()
d->hasXiphComment = true;
d->commentPacket = ipacket;
}
else if(blockType > 5)
else if(blockType > 5) {
debug("Ogg::FLAC::File::scan() -- Unknown metadata block");
}
}
// End of metadata, now comes the datastream

View File

@@ -75,6 +75,9 @@ namespace TagLib {
* Contructs an Ogg/FLAC file from \a file. If \a readProperties is true
* the file's audio properties will also be read using \a propertiesStyle.
* If false, \a propertiesStyle is ignored.
*
* \note 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,
Properties::ReadStyle propertiesStyle = Properties::Average);

View File

@@ -281,8 +281,8 @@ void Ogg::File::writePageGroup(const List<int> &thePageGroup)
return;
// pages in the pageGroup and packets must be equivalent
// (originalSize and size of packets would not work together),
// pages in the pageGroup and packets must be equivalent
// (originalSize and size of packets would not work together),
// therefore we sometimes have to add pages to the group
List<int> pageGroup(thePageGroup);
while (!d->pages[pageGroup.back()]->header()->lastPacketCompleted()) {
@@ -341,7 +341,7 @@ void Ogg::File::writePageGroup(const List<int> &thePageGroup)
if (pages.back()->header()->pageSequenceNumber() != pageGroup.back()) {
// TODO: change the internal data structure so that we don't need to hold the
// TODO: change the internal data structure so that we don't need to hold the
// complete file in memory (is unavoidable at the moment)
// read the complete stream

View File

@@ -100,6 +100,9 @@ namespace TagLib {
* \note This constructor is protected since Ogg::File shouldn't be
* instantiated directly but rather should be used through the codec
* specific subclasses.
*
* \note TagLib will *not* take ownership of the stream, the caller is
* responsible for deleting it after the File object.
*/
File(IOStream *stream);

View File

@@ -116,9 +116,9 @@ Ogg::Page::ContainsPacketFlags Ogg::Page::containsPacket(int index) const
flags = ContainsPacketFlags(flags | CompletePacket);
}
// Or if there is more than one page and the page is
// (a) the first page and it's complete or
// (b) the last page and it's complete or
// Or if there is more than one page and the page is
// (a) the first page and it's complete or
// (b) the last page and it's complete or
// (c) a page in the middle.
else if(packetCount() > 1 &&
((flags & BeginsWithPacket && !d->header.firstPacketContinued()) ||
@@ -266,7 +266,7 @@ List<Ogg::Page *> Ogg::Page::paginate(const ByteVectorList &packets,
}
Page *p = new Page(packetList, streamSerialNumber, firstPage+pageIndex, continued,
lastPacketInList ? lastPacketCompleted : true,
lastPacketInList ? lastPacketCompleted : true,
isVeryLastPacket);
pageIndex++;

View File

@@ -70,9 +70,9 @@ namespace TagLib {
*/
const PageHeader *header() const;
/*!
/*!
* Returns a copy of the page with \a sequenceNumber set as sequence number.
*
*
* \see header()
* \see PageHeader::setPageSequenceNumber()
*/

View File

@@ -67,6 +67,9 @@ namespace TagLib {
* Contructs a Speex file from \a file. If \a readProperties is true the
* file's audio properties will also be read using \a propertiesStyle. If
* false, \a propertiesStyle is ignored.
*
* \note 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,
Properties::ReadStyle propertiesStyle = Properties::Average);

View File

@@ -27,9 +27,11 @@
#include <tstring.h>
#include <tdebug.h>
#include <tpropertymap.h>
#include "vorbisfile.h"
using namespace TagLib;
class Vorbis::File::FilePrivate
@@ -85,6 +87,16 @@ Ogg::XiphComment *Vorbis::File::tag() const
return d->comment;
}
PropertyMap Vorbis::File::properties() const
{
return d->comment->properties();
}
PropertyMap Vorbis::File::setProperties(const PropertyMap &properties)
{
return d->comment->setProperties(properties);
}
Vorbis::Properties *Vorbis::File::audioProperties() const
{
return d->properties;

View File

@@ -74,6 +74,9 @@ namespace TagLib {
* Contructs a Vorbis file from \a file. If \a readProperties is true the
* file's audio properties will also be read using \a propertiesStyle. If
* false, \a propertiesStyle is ignored.
*
* \note 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,
Properties::ReadStyle propertiesStyle = Properties::Average);
@@ -90,6 +93,19 @@ namespace TagLib {
*/
virtual Ogg::XiphComment *tag() const;
/*!
* Implements the unified property interface -- export function.
* This forwards directly to XiphComment::properties().
*/
PropertyMap properties() const;
/*!
* Implements the unified tag dictionary interface -- import function.
* Like properties(), this is a forwarder to the file's XiphComment.
*/
PropertyMap setProperties(const PropertyMap &);
/*!
* Returns the Vorbis::Properties for this file. If no audio properties
* were read then this will return a null pointer.

View File

@@ -173,7 +173,7 @@ void Vorbis::Properties::read()
long long end = last->absoluteGranularPosition();
if(start >= 0 && end >= 0 && d->sampleRate > 0)
d->length = (end - start) / (long long) d->sampleRate;
d->length = (int)((end - start) / (long long) d->sampleRate);
else
debug("Vorbis::Properties::read() -- Either the PCM values for the start or "
"end of this file was incorrect or the sample rate is zero.");

View File

@@ -27,6 +27,7 @@
#include <tdebug.h>
#include <xiphcomment.h>
#include <tpropertymap.h>
using namespace TagLib;
@@ -188,6 +189,58 @@ const Ogg::FieldListMap &Ogg::XiphComment::fieldListMap() const
return d->fieldListMap;
}
PropertyMap Ogg::XiphComment::properties() const
{
return d->fieldListMap;
}
PropertyMap Ogg::XiphComment::setProperties(const PropertyMap &properties)
{
// check which keys are to be deleted
StringList toRemove;
for(FieldListMap::ConstIterator it = d->fieldListMap.begin(); it != d->fieldListMap.end(); ++it)
if (!properties.contains(it->first))
toRemove.append(it->first);
for(StringList::ConstIterator it = toRemove.begin(); it != toRemove.end(); ++it)
removeField(*it);
// now go through keys in \a properties and check that the values match those in the xiph comment
PropertyMap invalid;
PropertyMap::ConstIterator it = properties.begin();
for(; it != properties.end(); ++it)
{
if(!checkKey(it->first))
invalid.insert(it->first, it->second);
else if(!d->fieldListMap.contains(it->first) || !(it->second == d->fieldListMap[it->first])) {
const StringList &sl = it->second;
if(sl.size() == 0)
// zero size string list -> remove the tag with all values
removeField(it->first);
else {
// replace all strings in the list for the tag
StringList::ConstIterator valueIterator = sl.begin();
addField(it->first, *valueIterator, true);
++valueIterator;
for(; valueIterator != sl.end(); ++valueIterator)
addField(it->first, *valueIterator, false);
}
}
}
return invalid;
}
bool Ogg::XiphComment::checkKey(const String &key)
{
if(key.size() < 1)
return false;
for(String::ConstIterator it = key.begin(); it != key.end(); it++)
// forbid non-printable, non-ascii, '=' (#61) and '~' (#126)
if (*it < 32 || *it >= 128 || *it == 61 || *it == 126)
return false;
return true;
}
String Ogg::XiphComment::vendorID() const
{
return d->vendorID;
@@ -287,7 +340,7 @@ void Ogg::XiphComment::parse(const ByteVector &data)
uint pos = 0;
int vendorLength = data.mid(0, 4).toUInt(false);
uint vendorLength = data.mid(0, 4).toUInt(false);
pos += 4;
d->vendorID = String(data.mid(pos, vendorLength), String::UTF8);

View File

@@ -140,6 +140,29 @@ namespace TagLib {
*/
const FieldListMap &fieldListMap() const;
/*!
* Implements the unified property interface -- export function.
* The result is a one-to-one match of the Xiph comment, since it is
* completely compatible with the property interface (in fact, a Xiph
* comment is nothing more than a map from tag names to list of values,
* as is the dict interface).
*/
PropertyMap properties() const;
/*!
* Implements the unified property interface -- import function.
* The tags from the given map will be stored one-to-one in the file,
* except for invalid keys (less than one character, non-ASCII, or
* containing '=' or '~') in which case the according values will
* be contained in the returned PropertyMap.
*/
PropertyMap setProperties(const PropertyMap&);
/*!
* Check if the given String is a valid Xiph comment key.
*/
static bool checkKey(const String&);
/*!
* Returns the vendor ID of the Ogg Vorbis encoder. libvorbis 1.0 as the
* most common case always returns "Xiph.Org libVorbis I 20020717".

View File

@@ -26,6 +26,8 @@
#include <tbytevector.h>
#include <tdebug.h>
#include <id3v2tag.h>
#include <tstringlist.h>
#include <tpropertymap.h>
#include "aifffile.h"
@@ -83,6 +85,17 @@ ID3v2::Tag *RIFF::AIFF::File::tag() const
return d->tag;
}
PropertyMap RIFF::AIFF::File::properties() const
{
return d->tag->properties();
}
PropertyMap RIFF::AIFF::File::setProperties(const PropertyMap &properties)
{
return d->tag->setProperties(properties);
}
RIFF::AIFF::Properties *RIFF::AIFF::File::audioProperties() const
{
return d->properties;
@@ -95,6 +108,11 @@ bool RIFF::AIFF::File::save()
return false;
}
if(!isValid()) {
debug("RIFF::AIFF::File::save() -- Trying to save invalid file.");
return false;
}
setChunkData(d->tagChunkID, d->tag->render());
return true;

View File

@@ -69,6 +69,9 @@ namespace TagLib {
* Contructs an AIFF file from \a file. If \a readProperties is true the
* file's audio properties will also be read using \a propertiesStyle. If
* false, \a propertiesStyle is ignored.
*
* \note 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,
Properties::ReadStyle propertiesStyle = Properties::Average);
@@ -83,6 +86,18 @@ namespace TagLib {
*/
virtual ID3v2::Tag *tag() const;
/*!
* Implements the unified property interface -- export function.
* This method forwards to ID3v2::Tag::properties().
*/
PropertyMap properties() const;
/*!
* Implements the unified property interface -- import function.
* This method forwards to ID3v2::Tag::setProperties().
*/
PropertyMap setProperties(const PropertyMap &);
/*!
* Returns the AIFF::Properties for this file. If no audio properties
* were read then this will return a null pointer.

View File

@@ -139,7 +139,7 @@ int RIFF::AIFF::Properties::sampleWidth() const
return d->sampleWidth;
}
uint RIFF::AIFF::Properties::sampleFrames() const
TagLib::uint RIFF::AIFF::Properties::sampleFrames() const
{
return d->sampleFrames;
}
@@ -154,7 +154,7 @@ void RIFF::AIFF::Properties::read(const ByteVector &data)
d->sampleFrames = data.mid(2, 4).toUInt();
d->sampleWidth = data.mid(6, 2).toShort();
double sampleRate = ConvertFromIeeeExtended(reinterpret_cast<unsigned char *>(data.mid(8, 10).data()));
d->sampleRate = sampleRate;
d->bitrate = (sampleRate * d->sampleWidth * d->channels) / 1000.0;
d->length = d->sampleFrames / d->sampleRate;
d->sampleRate = (int)sampleRate;
d->bitrate = (int)((sampleRate * d->sampleWidth * d->channels) / 1000.0);
d->length = d->sampleRate > 0 ? d->sampleFrames / d->sampleRate : 0;
}

View File

@@ -189,7 +189,7 @@ void RIFF::File::setChunkData(const ByteVector &name, const ByteVector &data)
d->chunks[i].padding = 1;
offset++;
}
Chunk chunk;
chunk.name = name;
chunk.size = data.size();
@@ -203,6 +203,19 @@ void RIFF::File::setChunkData(const ByteVector &name, const ByteVector &data)
// private members
////////////////////////////////////////////////////////////////////////////////
static bool isValidChunkID(const ByteVector &name)
{
if(name.size() != 4) {
return false;
}
for(int i = 0; i < 4; i++) {
if(name[i] < 32 || name[i] > 127) {
return false;
}
}
return true;
}
void RIFF::File::read()
{
bool bigEndian = (d->endianness == BigEndian);
@@ -216,8 +229,15 @@ void RIFF::File::read()
ByteVector chunkName = readBlock(4);
uint chunkSize = readBlock(4).toUInt(bigEndian);
if(!isValidChunkID(chunkName)) {
debug("RIFF::File::read() -- Chunk '" + chunkName + "' has invalid ID");
setValid(false);
break;
}
if(tell() + chunkSize > uint(length())) {
// something wrong
debug("RIFF::File::read() -- Chunk '" + chunkName + "' has invalid size (larger than the file size)");
setValid(false);
break;
}

View File

@@ -26,6 +26,8 @@
#include <tbytevector.h>
#include <tdebug.h>
#include <id3v2tag.h>
#include <tstringlist.h>
#include <tpropertymap.h>
#include "wavfile.h"
@@ -83,6 +85,17 @@ ID3v2::Tag *RIFF::WAV::File::tag() const
return d->tag;
}
PropertyMap RIFF::WAV::File::properties() const
{
return d->tag->properties();
}
PropertyMap RIFF::WAV::File::setProperties(const PropertyMap &properties)
{
return d->tag->setProperties(properties);
}
RIFF::WAV::Properties *RIFF::WAV::File::audioProperties() const
{
return d->properties;
@@ -95,6 +108,11 @@ bool RIFF::WAV::File::save()
return false;
}
if(!isValid()) {
debug("RIFF::WAV::File::save() -- Trying to save invalid file.");
return false;
}
setChunkData(d->tagChunkID, d->tag->render());
return true;

View File

@@ -69,6 +69,9 @@ namespace TagLib {
* Contructs an WAV file from \a file. If \a readProperties is true the
* file's audio properties will also be read using \a propertiesStyle. If
* false, \a propertiesStyle is ignored.
*
* \note 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,
Properties::ReadStyle propertiesStyle = Properties::Average);
@@ -83,6 +86,18 @@ namespace TagLib {
*/
virtual ID3v2::Tag *tag() const;
/*!
* Implements the unified property interface -- export function.
* This method forwards to ID3v2::Tag::properties().
*/
PropertyMap properties() const;
/*!
* Implements the unified property interface -- import function.
* This method forwards to ID3v2::Tag::setProperties().
*/
PropertyMap setProperties(const PropertyMap &);
/*!
* Returns the WAV::Properties for this file. If no audio properties
* were read then this will return a null pointer.

View File

@@ -104,7 +104,7 @@ int RIFF::WAV::Properties::sampleWidth() const
return d->sampleWidth;
}
uint RIFF::WAV::Properties::sampleFrames() const
TagLib::uint RIFF::WAV::Properties::sampleFrames() const
{
return d->sampleFrames;
}
@@ -124,5 +124,6 @@ void RIFF::WAV::Properties::read(const ByteVector &data)
d->bitrate = byteRate * 8 / 1000;
d->length = byteRate > 0 ? d->streamLength / byteRate : 0;
d->sampleFrames = d->streamLength / (d->channels * (d->sampleWidth / 8));
if(d->channels > 0 && d->sampleWidth > 0)
d->sampleFrames = d->streamLength / (d->channels * ((d->sampleWidth + 7) / 8));
}

View File

@@ -23,6 +23,7 @@
#include "tstringlist.h"
#include "tdebug.h"
#include "modfileprivate.h"
#include "tpropertymap.h"
#include <iostream>
@@ -67,6 +68,16 @@ Mod::Tag *S3M::File::tag() const
return &d->tag;
}
PropertyMap S3M::File::properties() const
{
return d->tag.properties();
}
PropertyMap S3M::File::setProperties(const PropertyMap &properties)
{
return d->tag.setProperties(properties);
}
S3M::Properties *S3M::File::audioProperties() const
{
return &d->properties;
@@ -107,7 +118,7 @@ bool S3M::File::save()
}
seek(channels, Current);
StringList lines = d->tag.comment().split("\n");
// write comment as sample names:
for(ushort i = 0; i < sampleCount; ++ i) {
@@ -178,13 +189,13 @@ void S3M::File::read(bool)
if(setting != 0xff) ++ channels;
}
d->properties.setChannels(channels);
seek(96);
ushort realLength = 0;
for(ushort i = 0; i < length; ++ i) {
READ_BYTE_AS(order);
if(order == 255) break;
if(order != 254) ++ realLength;
READ_BYTE_AS(order);
if(order == 255) break;
if(order != 254) ++ realLength;
}
d->properties.setLengthInPatterns(realLength);

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