mirror of
https://github.com/taglib/taglib.git
synced 2026-06-07 14:59:24 -04:00
Compare commits
557 Commits
v1.11beta2
...
experiment
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
65a6a4e225 | ||
|
|
02090f335d | ||
|
|
96a4d896ba | ||
|
|
15163b7af2 | ||
|
|
19ee7c86c4 | ||
|
|
5cb589a5b8 | ||
|
|
a4d04e0c40 | ||
|
|
4ab0891646 | ||
|
|
d71398c953 | ||
|
|
bfed3797a0 | ||
|
|
e435372146 | ||
|
|
c2f544c9d1 | ||
|
|
8ca75f03b5 | ||
|
|
036a0317b9 | ||
|
|
8f6fe0b16c | ||
|
|
fc31a09c03 | ||
|
|
2c4ae870ec | ||
|
|
2052cda5fb | ||
|
|
d8d56d3937 | ||
|
|
a80093167f | ||
|
|
249f892455 | ||
|
|
cb9f07d9dc | ||
|
|
0b583bafd0 | ||
|
|
6048cdef3e | ||
|
|
ff34e42aef | ||
|
|
bac14180e9 | ||
|
|
c30bae9b00 | ||
|
|
4dcdaaf92a | ||
|
|
507f1a96a7 | ||
|
|
4648394841 | ||
|
|
e026d797e0 | ||
|
|
eeb2f5de09 | ||
|
|
81945efdff | ||
|
|
2075d865cd | ||
|
|
89f06af3f7 | ||
|
|
3ae0d4aa90 | ||
|
|
1792ee9db8 | ||
|
|
1fb310ec1f | ||
|
|
c8bcd153fe | ||
|
|
7e9f019a49 | ||
|
|
10e8866fec | ||
|
|
662f340f93 | ||
|
|
48d8c0a808 | ||
|
|
5ebd3d5276 | ||
|
|
14af861d24 | ||
|
|
3795f277fb | ||
|
|
6045995e65 | ||
|
|
c2fe93c12b | ||
|
|
ea9202d9ee | ||
|
|
3c657d1a44 | ||
|
|
adf0f76360 | ||
|
|
682ea77c2b | ||
|
|
15c3c756ca | ||
|
|
4801fbb927 | ||
|
|
22de22b701 | ||
|
|
48a1a05a88 | ||
|
|
6000a19f70 | ||
|
|
ff28cf276c | ||
|
|
4891ee729d | ||
|
|
9419dab51b | ||
|
|
bd4a45b07e | ||
|
|
45ee18e206 | ||
|
|
bf7ee62dc6 | ||
|
|
f76b1e5429 | ||
|
|
f7b15fad20 | ||
|
|
dd4adf94ce | ||
|
|
d4d8410c08 | ||
|
|
931bb042c3 | ||
|
|
a5d9e49c49 | ||
|
|
179c175a6c | ||
|
|
ba98628919 | ||
|
|
87fc4012f4 | ||
|
|
dd5ab2a08f | ||
|
|
b74ffba4b5 | ||
|
|
4552f2c2eb | ||
|
|
6398796f95 | ||
|
|
2c7ac6d6a9 | ||
|
|
6a61f02f85 | ||
|
|
038b52ae01 | ||
|
|
598ab752bc | ||
|
|
4328f934c8 | ||
|
|
922fd611ae | ||
|
|
3d14ff74b1 | ||
|
|
978b822774 | ||
|
|
0c45c63943 | ||
|
|
586c9bd962 | ||
|
|
fc38a0e401 | ||
|
|
5fc5a2e81a | ||
|
|
a358e87cc4 | ||
|
|
5ba8b740f9 | ||
|
|
c4a3c3ab97 | ||
|
|
6bb92c34fa | ||
|
|
d2e0e55223 | ||
|
|
d64c833359 | ||
|
|
c9c757e0ff | ||
|
|
9b548260f5 | ||
|
|
406e872ac3 | ||
|
|
193cbe3b6b | ||
|
|
13be28a52c | ||
|
|
56a7656c2e | ||
|
|
c97be6630e | ||
|
|
6fcc690233 | ||
|
|
83c72518ab | ||
|
|
de87cd7736 | ||
|
|
14c3ce5737 | ||
|
|
ffa32b19a7 | ||
|
|
8905e7095a | ||
|
|
1c20f92a8f | ||
|
|
d8114059ee | ||
|
|
eea1a1b200 | ||
|
|
ef1ae1a8fe | ||
|
|
7eab1bf6df | ||
|
|
a19a623d4b | ||
|
|
250c59f783 | ||
|
|
8eda5d5053 | ||
|
|
b5115e3497 | ||
|
|
36ccad2bd4 | ||
|
|
b00a5c1aab | ||
|
|
f6a604f54b | ||
|
|
489e2e6cbb | ||
|
|
9336c82da3 | ||
|
|
cfbaf34597 | ||
|
|
046c98230f | ||
|
|
4381bd75f3 | ||
|
|
6df61cf2af | ||
|
|
e9ef40fe7f | ||
|
|
6a2e276767 | ||
|
|
e188a39b5c | ||
|
|
e77e20597d | ||
|
|
0b9a2df3ec | ||
|
|
2786aa7463 | ||
|
|
d3062f3af4 | ||
|
|
7871afec37 | ||
|
|
c9a0754e3b | ||
|
|
6cfb11bb12 | ||
|
|
ad075a56f9 | ||
|
|
f80a7c0d83 | ||
|
|
5e1d9fad31 | ||
|
|
eff28c55bf | ||
|
|
d5b9d7b8a7 | ||
|
|
ce77fbb0e7 | ||
|
|
b98a984b66 | ||
|
|
f9a747dceb | ||
|
|
7b8d576bde | ||
|
|
2651372291 | ||
|
|
bd5688ae5f | ||
|
|
499f6db977 | ||
|
|
9d58e9f8e8 | ||
|
|
56cd3695f7 | ||
|
|
d81d894d41 | ||
|
|
e390cbac52 | ||
|
|
253c61e37d | ||
|
|
1848b3bc6f | ||
|
|
aae23f3c07 | ||
|
|
da9df1b2a8 | ||
|
|
13223ad497 | ||
|
|
70334edd19 | ||
|
|
5c8cb9b86b | ||
|
|
f5ca097379 | ||
|
|
eb6d058ab9 | ||
|
|
3a6f0d46aa | ||
|
|
ef6d76889b | ||
|
|
e6a69e24bc | ||
|
|
dcab8ed90e | ||
|
|
28470221c0 | ||
|
|
f355110d18 | ||
|
|
f5ce498182 | ||
|
|
ee7fa78011 | ||
|
|
873c917081 | ||
|
|
d3bd8fb7ff | ||
|
|
921a68ae55 | ||
|
|
d9df59306f | ||
|
|
005441faaa | ||
|
|
935534aa53 | ||
|
|
fea7e3d4a2 | ||
|
|
b6d21ce890 | ||
|
|
65a24bbc51 | ||
|
|
711b35cc6e | ||
|
|
d53ca6f736 | ||
|
|
aa5f9bb221 | ||
|
|
d2b3547254 | ||
|
|
06ca9a099d | ||
|
|
8d873e4e3e | ||
|
|
b2fa124451 | ||
|
|
ff5b2dc96f | ||
|
|
757f5ebc96 | ||
|
|
e36a9cabb9 | ||
|
|
606f6c0e74 | ||
|
|
1cc047c953 | ||
|
|
cae4f1b804 | ||
|
|
deffe83fd0 | ||
|
|
597dcde72a | ||
|
|
6a96a6426a | ||
|
|
69c65284a5 | ||
|
|
97aaa0f979 | ||
|
|
1b64bb0cb7 | ||
|
|
0dac721ce2 | ||
|
|
bbeeca6fdb | ||
|
|
7e90313690 | ||
|
|
51b85abc0b | ||
|
|
9452970528 | ||
|
|
53ac43b5f6 | ||
|
|
1d3c95f692 | ||
|
|
8c3801f18d | ||
|
|
c9bdd416ef | ||
|
|
9f28e037fe | ||
|
|
92c070ba9e | ||
|
|
a64772a832 | ||
|
|
75e3ec73aa | ||
|
|
c352425ee7 | ||
|
|
0f096af504 | ||
|
|
a16c95b33f | ||
|
|
3e47a036fb | ||
|
|
9b995544e4 | ||
|
|
d8e5077961 | ||
|
|
821ff14a43 | ||
|
|
6422054540 | ||
|
|
1b878102f0 | ||
|
|
0a85f9b227 | ||
|
|
31f3109b47 | ||
|
|
76f8ff388f | ||
|
|
7627ae48ed | ||
|
|
b2a6768704 | ||
|
|
7d270a7e20 | ||
|
|
bf53dc6131 | ||
|
|
ff8b6a91e7 | ||
|
|
1a82419872 | ||
|
|
33c0ece830 | ||
|
|
57e849b0d5 | ||
|
|
b972f24193 | ||
|
|
1a73c82a76 | ||
|
|
c3c862e911 | ||
|
|
75159614fc | ||
|
|
ed0305bd3f | ||
|
|
f72b0ecebf | ||
|
|
f25a54a177 | ||
|
|
ff94a6e4a0 | ||
|
|
1f5ed5fb28 | ||
|
|
0e4f9f4e94 | ||
|
|
6857926da2 | ||
|
|
afa137e1c1 | ||
|
|
d36550f96d | ||
|
|
6f5874a035 | ||
|
|
2f29f0e1d0 | ||
|
|
7baa7e70fe | ||
|
|
faca7ac8f4 | ||
|
|
1063110701 | ||
|
|
973fb49cde | ||
|
|
15775d4135 | ||
|
|
52774d66ab | ||
|
|
b7e3ff8cb5 | ||
|
|
b4a33b0306 | ||
|
|
aa9e5c3c2f | ||
|
|
0556b6ca33 | ||
|
|
b3014f0878 | ||
|
|
6566352728 | ||
|
|
1d0552cab1 | ||
|
|
bb49005267 | ||
|
|
47cb23d738 | ||
|
|
d37c081cda | ||
|
|
dbe3a03b62 | ||
|
|
5da35f5a51 | ||
|
|
176e133f32 | ||
|
|
722f317f97 | ||
|
|
c2753f8d3c | ||
|
|
da7424636b | ||
|
|
0b2a3ce400 | ||
|
|
1ff30e55da | ||
|
|
0e981adcc6 | ||
|
|
a18d402bd9 | ||
|
|
dc994a65f0 | ||
|
|
4998dedcf4 | ||
|
|
794d9fd27b | ||
|
|
35cf4afdba | ||
|
|
8ecfba0c30 | ||
|
|
ace203a6e9 | ||
|
|
ccb1d036f2 | ||
|
|
6dbc340899 | ||
|
|
34931b1d3f | ||
|
|
612c84731c | ||
|
|
9e95156319 | ||
|
|
7ed8763a33 | ||
|
|
3ea69ed165 | ||
|
|
71e8915568 | ||
|
|
9b8f774fb3 | ||
|
|
30551864fa | ||
|
|
ba2167ef92 | ||
|
|
dc0cc4e7fa | ||
|
|
ef09a707b8 | ||
|
|
400fa04b1c | ||
|
|
e6e11c957d | ||
|
|
ab2389819e | ||
|
|
8e35b43e32 | ||
|
|
b85a0d0710 | ||
|
|
87040570c0 | ||
|
|
e1b5e2c9c3 | ||
|
|
51449f131f | ||
|
|
c4c4a28daa | ||
|
|
d52baafa24 | ||
|
|
bdfd7dc003 | ||
|
|
0884945567 | ||
|
|
93c2758ca6 | ||
|
|
2ec7a884c3 | ||
|
|
a5c56d31ac | ||
|
|
f0e8f39de1 | ||
|
|
efcd751a1c | ||
|
|
34f5a7da0d | ||
|
|
7345b7569f | ||
|
|
f93397fa7b | ||
|
|
81f806317f | ||
|
|
8e6e580c60 | ||
|
|
909d28d4fe | ||
|
|
6be3b7a1c7 | ||
|
|
29d5f75d0d | ||
|
|
8936fb2fc3 | ||
|
|
1dc2471d01 | ||
|
|
5687c70d80 | ||
|
|
3f19a522fc | ||
|
|
3cea7f267a | ||
|
|
1a9c49e42b | ||
|
|
76c688bffd | ||
|
|
fbbebfcec6 | ||
|
|
7f20c95cfc | ||
|
|
98c6088f91 | ||
|
|
22f8b527f7 | ||
|
|
65beb8cc06 | ||
|
|
c94d482215 | ||
|
|
d05101153f | ||
|
|
bceb568ea8 | ||
|
|
8390a91e48 | ||
|
|
4dca1ab68c | ||
|
|
3ced5e92b7 | ||
|
|
dce1664f30 | ||
|
|
a303d88782 | ||
|
|
8b77051ecf | ||
|
|
329abdbce7 | ||
|
|
d3eefdde34 | ||
|
|
c5353ed5ef | ||
|
|
5199a3cbb6 | ||
|
|
d93b4af8ca | ||
|
|
dd1a885f27 | ||
|
|
50b55a8a94 | ||
|
|
5d519c5207 | ||
|
|
bd84c8928a | ||
|
|
279d71c1e8 | ||
|
|
d8f5937091 | ||
|
|
be041ef2aa | ||
|
|
d643878e9f | ||
|
|
0c2e21024f | ||
|
|
dad73ebfc5 | ||
|
|
37b0ba6989 | ||
|
|
733b537c63 | ||
|
|
caf705958e | ||
|
|
ebaecc47f4 | ||
|
|
69bcc52ef3 | ||
|
|
644b47910f | ||
|
|
97b0f9e5a5 | ||
|
|
3cdbdfe0b9 | ||
|
|
7ff61ff5ac | ||
|
|
f7d2935d7b | ||
|
|
8a22c7ce75 | ||
|
|
6dc1ce7fe7 | ||
|
|
28cede5965 | ||
|
|
634d2ede3b | ||
|
|
753c63e0e6 | ||
|
|
637a08b8b0 | ||
|
|
0d45ddaf25 | ||
|
|
499d503aa2 | ||
|
|
116b1fcff9 | ||
|
|
85e1ef1fba | ||
|
|
88d55057fc | ||
|
|
cd540cbee2 | ||
|
|
0acdd2379e | ||
|
|
ced5f262ba | ||
|
|
e5c2232b10 | ||
|
|
8fc110dfd0 | ||
|
|
b949e23be3 | ||
|
|
43240b8ecc | ||
|
|
c09ea21ae3 | ||
|
|
57d12de7dd | ||
|
|
0d1ed55c7e | ||
|
|
e7cfbfed76 | ||
|
|
c63dcdc459 | ||
|
|
b7ffa04f20 | ||
|
|
fde99fa2fb | ||
|
|
5c9360afa2 | ||
|
|
60590c0a1a | ||
|
|
70e58dcb21 | ||
|
|
47561d8350 | ||
|
|
829f460c3c | ||
|
|
bb622bc163 | ||
|
|
e17b8e14de | ||
|
|
f6c23ac92c | ||
|
|
dbb4b47be3 | ||
|
|
9f9af997ee | ||
|
|
314cc33585 | ||
|
|
6d40cbc04f | ||
|
|
6d89689c0e | ||
|
|
48191904f1 | ||
|
|
45e8e83041 | ||
|
|
c066ccd32d | ||
|
|
90d43956ec | ||
|
|
09b574a19a | ||
|
|
8be47ec8dc | ||
|
|
2316a9661a | ||
|
|
2c770600c4 | ||
|
|
d3920f4ab6 | ||
|
|
c9904354f9 | ||
|
|
804f882e38 | ||
|
|
c6f323357f | ||
|
|
25590fe70c | ||
|
|
5f5c670f31 | ||
|
|
8fcc992815 | ||
|
|
7c64b1966a | ||
|
|
6e335694c9 | ||
|
|
4be506fae2 | ||
|
|
6bf8177796 | ||
|
|
409bba5936 | ||
|
|
ea6ab84c42 | ||
|
|
d982b846c4 | ||
|
|
1f08a8d7c6 | ||
|
|
c972b0c080 | ||
|
|
a30f992106 | ||
|
|
e2a168af7a | ||
|
|
8f31d882b0 | ||
|
|
a23d41c509 | ||
|
|
ca48435ab6 | ||
|
|
0a4756b527 | ||
|
|
437b61a311 | ||
|
|
6c2f99c89a | ||
|
|
2b5fee2df1 | ||
|
|
dd7758c76e | ||
|
|
5ed8d12478 | ||
|
|
09e7df8509 | ||
|
|
befa5724f1 | ||
|
|
23418c25a4 | ||
|
|
f07bf28f5f | ||
|
|
2bdded5597 | ||
|
|
8826ae2484 | ||
|
|
2d0644dca7 | ||
|
|
4ce7ebe520 | ||
|
|
72bb1a887e | ||
|
|
04e07ad4b0 | ||
|
|
c6315d8371 | ||
|
|
de04f4eb19 | ||
|
|
73e5acdae9 | ||
|
|
7e1f3545cd | ||
|
|
8457be8ab4 | ||
|
|
70044534aa | ||
|
|
3e7dc496be | ||
|
|
43cfbd568b | ||
|
|
b3a3aa8a95 | ||
|
|
36512745cf | ||
|
|
269f34ff2a | ||
|
|
c50a0ae1dc | ||
|
|
6b56510f99 | ||
|
|
91d3b8513d | ||
|
|
fbe329bb70 | ||
|
|
ffc7bcfef0 | ||
|
|
93db8aa135 | ||
|
|
6182378c87 | ||
|
|
36ceaadfaa | ||
|
|
362900c721 | ||
|
|
fb71d7341a | ||
|
|
3dc37a482e | ||
|
|
3d3c97b5ef | ||
|
|
ea968ae3be | ||
|
|
82e616101a | ||
|
|
3d8bb4d4b9 | ||
|
|
454abde101 | ||
|
|
f97a422bc2 | ||
|
|
4fc2a3bdd8 | ||
|
|
10b4bd61e2 | ||
|
|
c3efe19d8a | ||
|
|
1390860929 | ||
|
|
c44351aab5 | ||
|
|
5c96ff42c3 | ||
|
|
bb61b43ba0 | ||
|
|
2f711e26ca | ||
|
|
781aa05f0e | ||
|
|
c265c2a874 | ||
|
|
5171de33e4 | ||
|
|
2aaf41e0bb | ||
|
|
27115557a0 | ||
|
|
86cee56f74 | ||
|
|
843a12a0c6 | ||
|
|
a0e8963fa0 | ||
|
|
265013ef5b | ||
|
|
39e02ad21b | ||
|
|
fc2b69a81e | ||
|
|
1618833a6b | ||
|
|
56c85a2d68 | ||
|
|
83b6fdef72 | ||
|
|
cd082cd8e3 | ||
|
|
b744f66819 | ||
|
|
2ed7a59d95 | ||
|
|
7d5ce5624a | ||
|
|
e93696f573 | ||
|
|
914e5f9fd8 | ||
|
|
b1dcdc5bd8 | ||
|
|
b4df82a0b0 | ||
|
|
be081d23d3 | ||
|
|
0acc22e84b | ||
|
|
62efb9ff17 | ||
|
|
ca5ce53b1e | ||
|
|
8cae2b9f28 | ||
|
|
d719eb0ac6 | ||
|
|
cab68e2152 | ||
|
|
96cf908232 | ||
|
|
42a74babb5 | ||
|
|
4e05923479 | ||
|
|
c86ea7bdff | ||
|
|
a842220fe6 | ||
|
|
19ce4d0dfa | ||
|
|
6e3639de9e | ||
|
|
0792eedd12 | ||
|
|
86b7cabf44 | ||
|
|
de19ad72ab | ||
|
|
9b19453059 | ||
|
|
dbd7c151d6 | ||
|
|
d3af7c0b02 | ||
|
|
64447598e5 | ||
|
|
fba8f42588 | ||
|
|
5578843220 | ||
|
|
90fd336a22 | ||
|
|
fa38c805f5 | ||
|
|
b60b444d7b | ||
|
|
db892c43e7 | ||
|
|
f2b56a5511 | ||
|
|
2a86da4df5 | ||
|
|
f6741b65e4 | ||
|
|
60ba972244 | ||
|
|
e8ae4ecd93 | ||
|
|
f407d1456c | ||
|
|
50e616df8d | ||
|
|
9440055eb1 | ||
|
|
cb8b8d50f6 | ||
|
|
c3f9c63542 | ||
|
|
aa801e58ec | ||
|
|
9e41939eb1 | ||
|
|
0d49e6bff0 | ||
|
|
6e2d5bc1dc | ||
|
|
9cede44af6 | ||
|
|
ae11b3db38 | ||
|
|
b3c112ed99 | ||
|
|
5b604f41be | ||
|
|
8599c1bd38 | ||
|
|
27a078fe9b | ||
|
|
2b31fc2037 | ||
|
|
b01f45e141 | ||
|
|
c46ecd186d | ||
|
|
c2eecc0804 | ||
|
|
4dcf0b41c6 | ||
|
|
9577784bae | ||
|
|
f52bab8a01 | ||
|
|
738e2ae7a7 | ||
|
|
231eb08396 |
3
.gitignore
vendored
3
.gitignore
vendored
@@ -19,8 +19,10 @@ CMakeFiles/
|
||||
/taglib.pc
|
||||
/tests/test_runner
|
||||
/tests/Testing
|
||||
/taglib/libtag.a
|
||||
/taglib_config.h
|
||||
/taglib-config
|
||||
/bindings/c/libtag_c.a
|
||||
/bindings/c/taglib_c.pc
|
||||
/bindings/c/Debug
|
||||
/bindings/c/MinSizeRel
|
||||
@@ -42,5 +44,6 @@ CMakeFiles/
|
||||
/taglib/tag.dir/Release
|
||||
/ALL_BUILD.dir
|
||||
/ZERO_CHECK.dir
|
||||
taglib.h.stamp
|
||||
taglib.xcodeproj
|
||||
CMakeScripts
|
||||
|
||||
@@ -6,6 +6,8 @@ os:
|
||||
- linux
|
||||
- osx
|
||||
|
||||
dist: trusty
|
||||
|
||||
compiler:
|
||||
- gcc
|
||||
- clang
|
||||
@@ -15,6 +17,7 @@ addons:
|
||||
packages:
|
||||
- libcppunit-dev
|
||||
- zlib1g-dev
|
||||
- libboost-dev
|
||||
|
||||
matrix:
|
||||
exclude:
|
||||
@@ -24,5 +27,4 @@ matrix:
|
||||
install:
|
||||
- if [ "$TRAVIS_OS_NAME" = "osx" ]; then brew install cppunit; fi
|
||||
|
||||
script: cmake -DBUILD_TESTS=ON -DBUILD_EXAMPLES=ON -DBUILD_BINDINGS=ON . && make && make check
|
||||
|
||||
script: cmake -DBUILD_TESTS=ON -DBUILD_EXAMPLES=ON -DBUILD_BINDINGS=ON -DCMAKE_CXX_FLAGS="-std=c++11" . && make && make check
|
||||
|
||||
327
3rdparty/utf8-cpp/checked.h
vendored
Normal file
327
3rdparty/utf8-cpp/checked.h
vendored
Normal file
@@ -0,0 +1,327 @@
|
||||
// Copyright 2006-2016 Nemanja Trifunovic
|
||||
|
||||
/*
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef UTF8_FOR_CPP_CHECKED_H_2675DCD0_9480_4c0c_B92A_CC14C027B731
|
||||
#define UTF8_FOR_CPP_CHECKED_H_2675DCD0_9480_4c0c_B92A_CC14C027B731
|
||||
|
||||
#include "core.h"
|
||||
#include <stdexcept>
|
||||
|
||||
namespace utf8
|
||||
{
|
||||
// Base for the exceptions that may be thrown from the library
|
||||
class exception : public ::std::exception {
|
||||
};
|
||||
|
||||
// Exceptions that may be thrown from the library functions.
|
||||
class invalid_code_point : public exception {
|
||||
uint32_t cp;
|
||||
public:
|
||||
invalid_code_point(uint32_t codepoint) : cp(codepoint) {}
|
||||
virtual const char* what() const throw() { return "Invalid code point"; }
|
||||
uint32_t code_point() const {return cp;}
|
||||
};
|
||||
|
||||
class invalid_utf8 : public exception {
|
||||
uint8_t u8;
|
||||
public:
|
||||
invalid_utf8 (uint8_t u) : u8(u) {}
|
||||
virtual const char* what() const throw() { return "Invalid UTF-8"; }
|
||||
uint8_t utf8_octet() const {return u8;}
|
||||
};
|
||||
|
||||
class invalid_utf16 : public exception {
|
||||
uint16_t u16;
|
||||
public:
|
||||
invalid_utf16 (uint16_t u) : u16(u) {}
|
||||
virtual const char* what() const throw() { return "Invalid UTF-16"; }
|
||||
uint16_t utf16_word() const {return u16;}
|
||||
};
|
||||
|
||||
class not_enough_room : public exception {
|
||||
public:
|
||||
virtual const char* what() const throw() { return "Not enough space"; }
|
||||
};
|
||||
|
||||
/// The library API - functions intended to be called by the users
|
||||
|
||||
template <typename octet_iterator>
|
||||
octet_iterator append(uint32_t cp, octet_iterator result)
|
||||
{
|
||||
if (!utf8::internal::is_code_point_valid(cp))
|
||||
throw invalid_code_point(cp);
|
||||
|
||||
if (cp < 0x80) // one octet
|
||||
*(result++) = static_cast<uint8_t>(cp);
|
||||
else if (cp < 0x800) { // two octets
|
||||
*(result++) = static_cast<uint8_t>((cp >> 6) | 0xc0);
|
||||
*(result++) = static_cast<uint8_t>((cp & 0x3f) | 0x80);
|
||||
}
|
||||
else if (cp < 0x10000) { // three octets
|
||||
*(result++) = static_cast<uint8_t>((cp >> 12) | 0xe0);
|
||||
*(result++) = static_cast<uint8_t>(((cp >> 6) & 0x3f) | 0x80);
|
||||
*(result++) = static_cast<uint8_t>((cp & 0x3f) | 0x80);
|
||||
}
|
||||
else { // four octets
|
||||
*(result++) = static_cast<uint8_t>((cp >> 18) | 0xf0);
|
||||
*(result++) = static_cast<uint8_t>(((cp >> 12) & 0x3f) | 0x80);
|
||||
*(result++) = static_cast<uint8_t>(((cp >> 6) & 0x3f) | 0x80);
|
||||
*(result++) = static_cast<uint8_t>((cp & 0x3f) | 0x80);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename octet_iterator, typename output_iterator>
|
||||
output_iterator replace_invalid(octet_iterator start, octet_iterator end, output_iterator out, uint32_t replacement)
|
||||
{
|
||||
while (start != end) {
|
||||
octet_iterator sequence_start = start;
|
||||
internal::utf_error err_code = utf8::internal::validate_next(start, end);
|
||||
switch (err_code) {
|
||||
case internal::UTF8_OK :
|
||||
for (octet_iterator it = sequence_start; it != start; ++it)
|
||||
*out++ = *it;
|
||||
break;
|
||||
case internal::NOT_ENOUGH_ROOM:
|
||||
throw not_enough_room();
|
||||
case internal::INVALID_LEAD:
|
||||
out = utf8::append (replacement, out);
|
||||
++start;
|
||||
break;
|
||||
case internal::INCOMPLETE_SEQUENCE:
|
||||
case internal::OVERLONG_SEQUENCE:
|
||||
case internal::INVALID_CODE_POINT:
|
||||
out = utf8::append (replacement, out);
|
||||
++start;
|
||||
// just one replacement mark for the sequence
|
||||
while (start != end && utf8::internal::is_trail(*start))
|
||||
++start;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
template <typename octet_iterator, typename output_iterator>
|
||||
inline output_iterator replace_invalid(octet_iterator start, octet_iterator end, output_iterator out)
|
||||
{
|
||||
static const uint32_t replacement_marker = utf8::internal::mask16(0xfffd);
|
||||
return utf8::replace_invalid(start, end, out, replacement_marker);
|
||||
}
|
||||
|
||||
template <typename octet_iterator>
|
||||
uint32_t next(octet_iterator& it, octet_iterator end)
|
||||
{
|
||||
uint32_t cp = 0;
|
||||
internal::utf_error err_code = utf8::internal::validate_next(it, end, cp);
|
||||
switch (err_code) {
|
||||
case internal::UTF8_OK :
|
||||
break;
|
||||
case internal::NOT_ENOUGH_ROOM :
|
||||
throw not_enough_room();
|
||||
case internal::INVALID_LEAD :
|
||||
case internal::INCOMPLETE_SEQUENCE :
|
||||
case internal::OVERLONG_SEQUENCE :
|
||||
throw invalid_utf8(*it);
|
||||
case internal::INVALID_CODE_POINT :
|
||||
throw invalid_code_point(cp);
|
||||
}
|
||||
return cp;
|
||||
}
|
||||
|
||||
template <typename octet_iterator>
|
||||
uint32_t peek_next(octet_iterator it, octet_iterator end)
|
||||
{
|
||||
return utf8::next(it, end);
|
||||
}
|
||||
|
||||
template <typename octet_iterator>
|
||||
uint32_t prior(octet_iterator& it, octet_iterator start)
|
||||
{
|
||||
// can't do much if it == start
|
||||
if (it == start)
|
||||
throw not_enough_room();
|
||||
|
||||
octet_iterator end = it;
|
||||
// Go back until we hit either a lead octet or start
|
||||
while (utf8::internal::is_trail(*(--it)))
|
||||
if (it == start)
|
||||
throw invalid_utf8(*it); // error - no lead byte in the sequence
|
||||
return utf8::peek_next(it, end);
|
||||
}
|
||||
|
||||
/// Deprecated in versions that include "prior"
|
||||
template <typename octet_iterator>
|
||||
uint32_t previous(octet_iterator& it, octet_iterator pass_start)
|
||||
{
|
||||
octet_iterator end = it;
|
||||
while (utf8::internal::is_trail(*(--it)))
|
||||
if (it == pass_start)
|
||||
throw invalid_utf8(*it); // error - no lead byte in the sequence
|
||||
octet_iterator temp = it;
|
||||
return utf8::next(temp, end);
|
||||
}
|
||||
|
||||
template <typename octet_iterator, typename distance_type>
|
||||
void advance (octet_iterator& it, distance_type n, octet_iterator end)
|
||||
{
|
||||
for (distance_type i = 0; i < n; ++i)
|
||||
utf8::next(it, end);
|
||||
}
|
||||
|
||||
template <typename octet_iterator>
|
||||
typename std::iterator_traits<octet_iterator>::difference_type
|
||||
distance (octet_iterator first, octet_iterator last)
|
||||
{
|
||||
typename std::iterator_traits<octet_iterator>::difference_type dist;
|
||||
for (dist = 0; first < last; ++dist)
|
||||
utf8::next(first, last);
|
||||
return dist;
|
||||
}
|
||||
|
||||
template <typename u16bit_iterator, typename octet_iterator>
|
||||
octet_iterator utf16to8 (u16bit_iterator start, u16bit_iterator end, octet_iterator result)
|
||||
{
|
||||
while (start != end) {
|
||||
uint32_t cp = utf8::internal::mask16(*start++);
|
||||
// Take care of surrogate pairs first
|
||||
if (utf8::internal::is_lead_surrogate(cp)) {
|
||||
if (start != end) {
|
||||
uint32_t trail_surrogate = utf8::internal::mask16(*start++);
|
||||
if (utf8::internal::is_trail_surrogate(trail_surrogate))
|
||||
cp = (cp << 10) + trail_surrogate + internal::SURROGATE_OFFSET;
|
||||
else
|
||||
throw invalid_utf16(static_cast<uint16_t>(trail_surrogate));
|
||||
}
|
||||
else
|
||||
throw invalid_utf16(static_cast<uint16_t>(cp));
|
||||
|
||||
}
|
||||
// Lone trail surrogate
|
||||
else if (utf8::internal::is_trail_surrogate(cp))
|
||||
throw invalid_utf16(static_cast<uint16_t>(cp));
|
||||
|
||||
result = utf8::append(cp, result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename u16bit_iterator, typename octet_iterator>
|
||||
u16bit_iterator utf8to16 (octet_iterator start, octet_iterator end, u16bit_iterator result)
|
||||
{
|
||||
while (start < end) {
|
||||
uint32_t cp = utf8::next(start, end);
|
||||
if (cp > 0xffff) { //make a surrogate pair
|
||||
*result++ = static_cast<uint16_t>((cp >> 10) + internal::LEAD_OFFSET);
|
||||
*result++ = static_cast<uint16_t>((cp & 0x3ff) + internal::TRAIL_SURROGATE_MIN);
|
||||
}
|
||||
else
|
||||
*result++ = static_cast<uint16_t>(cp);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename octet_iterator, typename u32bit_iterator>
|
||||
octet_iterator utf32to8 (u32bit_iterator start, u32bit_iterator end, octet_iterator result)
|
||||
{
|
||||
while (start != end)
|
||||
result = utf8::append(*(start++), result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename octet_iterator, typename u32bit_iterator>
|
||||
u32bit_iterator utf8to32 (octet_iterator start, octet_iterator end, u32bit_iterator result)
|
||||
{
|
||||
while (start < end)
|
||||
(*result++) = utf8::next(start, end);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// The iterator class
|
||||
template <typename octet_iterator>
|
||||
class iterator : public std::iterator <std::bidirectional_iterator_tag, uint32_t> {
|
||||
octet_iterator it;
|
||||
octet_iterator range_start;
|
||||
octet_iterator range_end;
|
||||
public:
|
||||
iterator () {}
|
||||
explicit iterator (const octet_iterator& octet_it,
|
||||
const octet_iterator& rangestart,
|
||||
const octet_iterator& rangeend) :
|
||||
it(octet_it), range_start(rangestart), range_end(rangeend)
|
||||
{
|
||||
if (it < range_start || it > range_end)
|
||||
throw std::out_of_range("Invalid utf-8 iterator position");
|
||||
}
|
||||
// the default "big three" are OK
|
||||
octet_iterator base () const { return it; }
|
||||
uint32_t operator * () const
|
||||
{
|
||||
octet_iterator temp = it;
|
||||
return utf8::next(temp, range_end);
|
||||
}
|
||||
bool operator == (const iterator& rhs) const
|
||||
{
|
||||
if (range_start != rhs.range_start || range_end != rhs.range_end)
|
||||
throw std::logic_error("Comparing utf-8 iterators defined with different ranges");
|
||||
return (it == rhs.it);
|
||||
}
|
||||
bool operator != (const iterator& rhs) const
|
||||
{
|
||||
return !(operator == (rhs));
|
||||
}
|
||||
iterator& operator ++ ()
|
||||
{
|
||||
utf8::next(it, range_end);
|
||||
return *this;
|
||||
}
|
||||
iterator operator ++ (int)
|
||||
{
|
||||
iterator temp = *this;
|
||||
utf8::next(it, range_end);
|
||||
return temp;
|
||||
}
|
||||
iterator& operator -- ()
|
||||
{
|
||||
utf8::prior(it, range_start);
|
||||
return *this;
|
||||
}
|
||||
iterator operator -- (int)
|
||||
{
|
||||
iterator temp = *this;
|
||||
utf8::prior(it, range_start);
|
||||
return temp;
|
||||
}
|
||||
}; // class iterator
|
||||
|
||||
} // namespace utf8
|
||||
|
||||
#endif //header guard
|
||||
|
||||
|
||||
332
3rdparty/utf8-cpp/core.h
vendored
Normal file
332
3rdparty/utf8-cpp/core.h
vendored
Normal file
@@ -0,0 +1,332 @@
|
||||
// Copyright 2006 Nemanja Trifunovic
|
||||
|
||||
/*
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef UTF8_FOR_CPP_CORE_H_2675DCD0_9480_4c0c_B92A_CC14C027B731
|
||||
#define UTF8_FOR_CPP_CORE_H_2675DCD0_9480_4c0c_B92A_CC14C027B731
|
||||
|
||||
#include <iterator>
|
||||
|
||||
namespace utf8
|
||||
{
|
||||
// The typedefs for 8-bit, 16-bit and 32-bit unsigned integers
|
||||
// You may need to change them to match your system.
|
||||
// These typedefs have the same names as ones from cstdint, or boost/cstdint
|
||||
typedef unsigned char uint8_t;
|
||||
typedef unsigned short uint16_t;
|
||||
typedef unsigned int uint32_t;
|
||||
|
||||
// Helper code - not intended to be directly called by the library users. May be changed at any time
|
||||
namespace internal
|
||||
{
|
||||
// Unicode constants
|
||||
// Leading (high) surrogates: 0xd800 - 0xdbff
|
||||
// Trailing (low) surrogates: 0xdc00 - 0xdfff
|
||||
const uint16_t LEAD_SURROGATE_MIN = 0xd800u;
|
||||
const uint16_t LEAD_SURROGATE_MAX = 0xdbffu;
|
||||
const uint16_t TRAIL_SURROGATE_MIN = 0xdc00u;
|
||||
const uint16_t TRAIL_SURROGATE_MAX = 0xdfffu;
|
||||
const uint16_t LEAD_OFFSET = LEAD_SURROGATE_MIN - (0x10000 >> 10);
|
||||
const uint32_t SURROGATE_OFFSET = 0x10000u - (LEAD_SURROGATE_MIN << 10) - TRAIL_SURROGATE_MIN;
|
||||
|
||||
// Maximum valid value for a Unicode code point
|
||||
const uint32_t CODE_POINT_MAX = 0x0010ffffu;
|
||||
|
||||
template<typename octet_type>
|
||||
inline uint8_t mask8(octet_type oc)
|
||||
{
|
||||
return static_cast<uint8_t>(0xff & oc);
|
||||
}
|
||||
template<typename u16_type>
|
||||
inline uint16_t mask16(u16_type oc)
|
||||
{
|
||||
return static_cast<uint16_t>(0xffff & oc);
|
||||
}
|
||||
template<typename octet_type>
|
||||
inline bool is_trail(octet_type oc)
|
||||
{
|
||||
return ((utf8::internal::mask8(oc) >> 6) == 0x2);
|
||||
}
|
||||
|
||||
template <typename u16>
|
||||
inline bool is_lead_surrogate(u16 cp)
|
||||
{
|
||||
return (cp >= LEAD_SURROGATE_MIN && cp <= LEAD_SURROGATE_MAX);
|
||||
}
|
||||
|
||||
template <typename u16>
|
||||
inline bool is_trail_surrogate(u16 cp)
|
||||
{
|
||||
return (cp >= TRAIL_SURROGATE_MIN && cp <= TRAIL_SURROGATE_MAX);
|
||||
}
|
||||
|
||||
template <typename u16>
|
||||
inline bool is_surrogate(u16 cp)
|
||||
{
|
||||
return (cp >= LEAD_SURROGATE_MIN && cp <= TRAIL_SURROGATE_MAX);
|
||||
}
|
||||
|
||||
template <typename u32>
|
||||
inline bool is_code_point_valid(u32 cp)
|
||||
{
|
||||
return (cp <= CODE_POINT_MAX && !utf8::internal::is_surrogate(cp));
|
||||
}
|
||||
|
||||
template <typename octet_iterator>
|
||||
inline typename std::iterator_traits<octet_iterator>::difference_type
|
||||
sequence_length(octet_iterator lead_it)
|
||||
{
|
||||
uint8_t lead = utf8::internal::mask8(*lead_it);
|
||||
if (lead < 0x80)
|
||||
return 1;
|
||||
else if ((lead >> 5) == 0x6)
|
||||
return 2;
|
||||
else if ((lead >> 4) == 0xe)
|
||||
return 3;
|
||||
else if ((lead >> 3) == 0x1e)
|
||||
return 4;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
template <typename octet_difference_type>
|
||||
inline bool is_overlong_sequence(uint32_t cp, octet_difference_type length)
|
||||
{
|
||||
if (cp < 0x80) {
|
||||
if (length != 1)
|
||||
return true;
|
||||
}
|
||||
else if (cp < 0x800) {
|
||||
if (length != 2)
|
||||
return true;
|
||||
}
|
||||
else if (cp < 0x10000) {
|
||||
if (length != 3)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
enum utf_error {UTF8_OK, NOT_ENOUGH_ROOM, INVALID_LEAD, INCOMPLETE_SEQUENCE, OVERLONG_SEQUENCE, INVALID_CODE_POINT};
|
||||
|
||||
/// Helper for get_sequence_x
|
||||
template <typename octet_iterator>
|
||||
utf_error increase_safely(octet_iterator& it, octet_iterator end)
|
||||
{
|
||||
if (++it == end)
|
||||
return NOT_ENOUGH_ROOM;
|
||||
|
||||
if (!utf8::internal::is_trail(*it))
|
||||
return INCOMPLETE_SEQUENCE;
|
||||
|
||||
return UTF8_OK;
|
||||
}
|
||||
|
||||
#define UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(IT, END) {utf_error ret = increase_safely(IT, END); if (ret != UTF8_OK) return ret;}
|
||||
|
||||
/// get_sequence_x functions decode utf-8 sequences of the length x
|
||||
template <typename octet_iterator>
|
||||
utf_error get_sequence_1(octet_iterator& it, octet_iterator end, uint32_t& code_point)
|
||||
{
|
||||
if (it == end)
|
||||
return NOT_ENOUGH_ROOM;
|
||||
|
||||
code_point = utf8::internal::mask8(*it);
|
||||
|
||||
return UTF8_OK;
|
||||
}
|
||||
|
||||
template <typename octet_iterator>
|
||||
utf_error get_sequence_2(octet_iterator& it, octet_iterator end, uint32_t& code_point)
|
||||
{
|
||||
if (it == end)
|
||||
return NOT_ENOUGH_ROOM;
|
||||
|
||||
code_point = utf8::internal::mask8(*it);
|
||||
|
||||
UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(it, end)
|
||||
|
||||
code_point = ((code_point << 6) & 0x7ff) + ((*it) & 0x3f);
|
||||
|
||||
return UTF8_OK;
|
||||
}
|
||||
|
||||
template <typename octet_iterator>
|
||||
utf_error get_sequence_3(octet_iterator& it, octet_iterator end, uint32_t& code_point)
|
||||
{
|
||||
if (it == end)
|
||||
return NOT_ENOUGH_ROOM;
|
||||
|
||||
code_point = utf8::internal::mask8(*it);
|
||||
|
||||
UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(it, end)
|
||||
|
||||
code_point = ((code_point << 12) & 0xffff) + ((utf8::internal::mask8(*it) << 6) & 0xfff);
|
||||
|
||||
UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(it, end)
|
||||
|
||||
code_point += (*it) & 0x3f;
|
||||
|
||||
return UTF8_OK;
|
||||
}
|
||||
|
||||
template <typename octet_iterator>
|
||||
utf_error get_sequence_4(octet_iterator& it, octet_iterator end, uint32_t& code_point)
|
||||
{
|
||||
if (it == end)
|
||||
return NOT_ENOUGH_ROOM;
|
||||
|
||||
code_point = utf8::internal::mask8(*it);
|
||||
|
||||
UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(it, end)
|
||||
|
||||
code_point = ((code_point << 18) & 0x1fffff) + ((utf8::internal::mask8(*it) << 12) & 0x3ffff);
|
||||
|
||||
UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(it, end)
|
||||
|
||||
code_point += (utf8::internal::mask8(*it) << 6) & 0xfff;
|
||||
|
||||
UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(it, end)
|
||||
|
||||
code_point += (*it) & 0x3f;
|
||||
|
||||
return UTF8_OK;
|
||||
}
|
||||
|
||||
#undef UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR
|
||||
|
||||
template <typename octet_iterator>
|
||||
utf_error validate_next(octet_iterator& it, octet_iterator end, uint32_t& code_point)
|
||||
{
|
||||
if (it == end)
|
||||
return NOT_ENOUGH_ROOM;
|
||||
|
||||
// Save the original value of it so we can go back in case of failure
|
||||
// Of course, it does not make much sense with i.e. stream iterators
|
||||
octet_iterator original_it = it;
|
||||
|
||||
uint32_t cp = 0;
|
||||
// Determine the sequence length based on the lead octet
|
||||
typedef typename std::iterator_traits<octet_iterator>::difference_type octet_difference_type;
|
||||
const octet_difference_type length = utf8::internal::sequence_length(it);
|
||||
|
||||
// Get trail octets and calculate the code point
|
||||
utf_error err = UTF8_OK;
|
||||
switch (length) {
|
||||
case 0:
|
||||
return INVALID_LEAD;
|
||||
case 1:
|
||||
err = utf8::internal::get_sequence_1(it, end, cp);
|
||||
break;
|
||||
case 2:
|
||||
err = utf8::internal::get_sequence_2(it, end, cp);
|
||||
break;
|
||||
case 3:
|
||||
err = utf8::internal::get_sequence_3(it, end, cp);
|
||||
break;
|
||||
case 4:
|
||||
err = utf8::internal::get_sequence_4(it, end, cp);
|
||||
break;
|
||||
}
|
||||
|
||||
if (err == UTF8_OK) {
|
||||
// Decoding succeeded. Now, security checks...
|
||||
if (utf8::internal::is_code_point_valid(cp)) {
|
||||
if (!utf8::internal::is_overlong_sequence(cp, length)){
|
||||
// Passed! Return here.
|
||||
code_point = cp;
|
||||
++it;
|
||||
return UTF8_OK;
|
||||
}
|
||||
else
|
||||
err = OVERLONG_SEQUENCE;
|
||||
}
|
||||
else
|
||||
err = INVALID_CODE_POINT;
|
||||
}
|
||||
|
||||
// Failure branch - restore the original value of the iterator
|
||||
it = original_it;
|
||||
return err;
|
||||
}
|
||||
|
||||
template <typename octet_iterator>
|
||||
inline utf_error validate_next(octet_iterator& it, octet_iterator end) {
|
||||
uint32_t ignored;
|
||||
return utf8::internal::validate_next(it, end, ignored);
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
|
||||
/// The library API - functions intended to be called by the users
|
||||
|
||||
// Byte order mark
|
||||
const uint8_t bom[] = {0xef, 0xbb, 0xbf};
|
||||
|
||||
template <typename octet_iterator>
|
||||
octet_iterator find_invalid(octet_iterator start, octet_iterator end)
|
||||
{
|
||||
octet_iterator result = start;
|
||||
while (result != end) {
|
||||
utf8::internal::utf_error err_code = utf8::internal::validate_next(result, end);
|
||||
if (err_code != internal::UTF8_OK)
|
||||
return result;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename octet_iterator>
|
||||
inline bool is_valid(octet_iterator start, octet_iterator end)
|
||||
{
|
||||
return (utf8::find_invalid(start, end) == end);
|
||||
}
|
||||
|
||||
template <typename octet_iterator>
|
||||
inline bool starts_with_bom (octet_iterator it, octet_iterator end)
|
||||
{
|
||||
return (
|
||||
((it != end) && (utf8::internal::mask8(*it++)) == bom[0]) &&
|
||||
((it != end) && (utf8::internal::mask8(*it++)) == bom[1]) &&
|
||||
((it != end) && (utf8::internal::mask8(*it)) == bom[2])
|
||||
);
|
||||
}
|
||||
|
||||
//Deprecated in release 2.3
|
||||
template <typename octet_iterator>
|
||||
inline bool is_bom (octet_iterator it)
|
||||
{
|
||||
return (
|
||||
(utf8::internal::mask8(*it++)) == bom[0] &&
|
||||
(utf8::internal::mask8(*it++)) == bom[1] &&
|
||||
(utf8::internal::mask8(*it)) == bom[2]
|
||||
);
|
||||
}
|
||||
} // namespace utf8
|
||||
|
||||
#endif // header guard
|
||||
|
||||
|
||||
8
AUTHORS
8
AUTHORS
@@ -2,6 +2,10 @@ Scott Wheeler <wheeler@kde.org>
|
||||
Author, maintainer
|
||||
Lukas Lalinsky <lalinsky@gmail.com>
|
||||
Implementation of multiple new file formats, many bug fixes, maintainer
|
||||
Tsuda Kageyu <tsuda.kageyu@gmail.com>
|
||||
A lot of bug fixes and performance improvements, maintainer.
|
||||
Stephen F. Booth <me@sbooth.org>
|
||||
DSF metadata implementation, bug fixes, maintainer.
|
||||
Ismael Orenstein <orenstein@kde.org>
|
||||
Xing header implementation
|
||||
Allan Sandfeld Jensen <kde@carewolf.org>
|
||||
@@ -10,8 +14,8 @@ Teemu Tervo <teemu.tervo@gmx.net>
|
||||
Numerous bug reports and fixes
|
||||
Mathias Panzenböck <grosser.meister.morti@gmx.net>
|
||||
Mod, S3M, IT and XM metadata implementations
|
||||
Tsuda Kageyu <tsuda.kageyu@gmail.com>
|
||||
A lot of fixes and improvements, i.e. memory copy reduction etc.
|
||||
Damien Plisson <damien78@audirvana.com>
|
||||
DSDIFF metadata implementation
|
||||
|
||||
Please send all patches and questions to taglib-devel@kde.org rather than to
|
||||
individual developers!
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
project(taglib)
|
||||
|
||||
cmake_minimum_required(VERSION 2.8.0 FATAL_ERROR)
|
||||
|
||||
project(taglib)
|
||||
|
||||
if(NOT ${CMAKE_VERSION} VERSION_LESS 2.8.12)
|
||||
cmake_policy(SET CMP0022 OLD)
|
||||
endif()
|
||||
@@ -12,6 +12,15 @@ if(DEFINED ENABLE_STATIC)
|
||||
message(FATAL_ERROR "This option is no longer available, use BUILD_SHARED_LIBS instead")
|
||||
endif()
|
||||
|
||||
option(BUILD_SHARED_LIBS "Build shared libraries" OFF)
|
||||
if(APPLE)
|
||||
option(BUILD_FRAMEWORK "Build an OS X framework" OFF)
|
||||
if(BUILD_FRAMEWORK)
|
||||
set(BUILD_SHARED_LIBS ON)
|
||||
#set(CMAKE_MACOSX_RPATH 1)
|
||||
set(FRAMEWORK_INSTALL_DIR "/Library/Frameworks" CACHE STRING "Directory to install frameworks to.")
|
||||
endif()
|
||||
endif()
|
||||
if(NOT BUILD_SHARED_LIBS)
|
||||
add_definitions(-DTAGLIB_STATIC)
|
||||
endif()
|
||||
@@ -27,16 +36,17 @@ if(ENABLE_CCACHE)
|
||||
endif()
|
||||
|
||||
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)
|
||||
option(BUILD_BINDINGS "Build the bindings" ON)
|
||||
|
||||
option(NO_ITUNES_HACKS "Disable workarounds for iTunes bugs" OFF)
|
||||
|
||||
option(PLATFORM_WINRT "Enable WinRT support" OFF)
|
||||
if(PLATFORM_WINRT)
|
||||
add_definitions(-DPLATFORM_WINRT)
|
||||
endif()
|
||||
|
||||
add_definitions(-DHAVE_CONFIG_H)
|
||||
set(TESTS_DIR "${CMAKE_CURRENT_SOURCE_DIR}/tests/")
|
||||
|
||||
@@ -47,11 +57,6 @@ set(BIN_INSTALL_DIR "${EXEC_INSTALL_PREFIX}/bin" CACHE PATH "The subdirectory to
|
||||
set(LIB_INSTALL_DIR "${EXEC_INSTALL_PREFIX}/lib${LIB_SUFFIX}" CACHE PATH "The subdirectory relative to the install prefix where libraries will be installed (default is /lib${LIB_SUFFIX})")
|
||||
set(INCLUDE_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/include" CACHE PATH "The subdirectory to the header prefix")
|
||||
|
||||
if(APPLE)
|
||||
option(BUILD_FRAMEWORK "Build an OS X framework" OFF)
|
||||
set(FRAMEWORK_INSTALL_DIR "/Library/Frameworks" CACHE STRING "Directory to install frameworks to.")
|
||||
endif()
|
||||
|
||||
if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX)
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall")
|
||||
@@ -89,9 +94,9 @@ endif()
|
||||
# 2. If any interfaces have been added, removed, or changed since the last update, increment current, and set revision to 0.
|
||||
# 3. If any interfaces have been added since the last public release, then increment age.
|
||||
# 4. If any interfaces have been removed since the last public release, then set age to 0.
|
||||
set(TAGLIB_SOVERSION_CURRENT 17)
|
||||
set(TAGLIB_SOVERSION_CURRENT 18)
|
||||
set(TAGLIB_SOVERSION_REVISION 0)
|
||||
set(TAGLIB_SOVERSION_AGE 16)
|
||||
set(TAGLIB_SOVERSION_AGE 17)
|
||||
|
||||
math(EXPR TAGLIB_SOVERSION_MAJOR "${TAGLIB_SOVERSION_CURRENT} - ${TAGLIB_SOVERSION_AGE}")
|
||||
math(EXPR TAGLIB_SOVERSION_MINOR "${TAGLIB_SOVERSION_AGE}")
|
||||
@@ -122,20 +127,11 @@ endif()
|
||||
include_directories(${CMAKE_CURRENT_BINARY_DIR})
|
||||
configure_file(config.h.cmake "${CMAKE_CURRENT_BINARY_DIR}/config.h")
|
||||
|
||||
if(WITH_ASF)
|
||||
set(TAGLIB_WITH_ASF TRUE)
|
||||
endif()
|
||||
if(WITH_MP4)
|
||||
set(TAGLIB_WITH_MP4 TRUE)
|
||||
endif()
|
||||
|
||||
option(TRACE_IN_RELEASE "Output debug messages even in release mode" OFF)
|
||||
option(TRACE_IN_RELEASE "Output debug messages even in release mode" OFF)
|
||||
if(TRACE_IN_RELEASE)
|
||||
set(TRACE_IN_RELEASE TRUE)
|
||||
endif()
|
||||
|
||||
configure_file(taglib/taglib_config.h.cmake "${CMAKE_CURRENT_BINARY_DIR}/taglib_config.h")
|
||||
|
||||
add_subdirectory(taglib)
|
||||
|
||||
if(BUILD_BINDINGS)
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
include(CheckLibraryExists)
|
||||
include(CheckTypeSize)
|
||||
include(CheckCXXCompilerFlag)
|
||||
include(CheckCXXSourceCompiles)
|
||||
include(TestLargeFiles)
|
||||
|
||||
# Check if the size of numeric types are suitable.
|
||||
|
||||
@@ -34,6 +36,15 @@ if(NOT ${SIZEOF_DOUBLE} EQUAL 8)
|
||||
message(FATAL_ERROR "TagLib requires that double is 64-bit wide.")
|
||||
endif()
|
||||
|
||||
# Determine whether your compiler supports large files.
|
||||
|
||||
if(NOT WIN32)
|
||||
test_large_files(SUPPORT_LARGE_FILES)
|
||||
if(NOT SUPPORT_LARGE_FILES)
|
||||
MESSAGE(FATAL_ERROR "TagLib requires large files support.")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Enable check_cxx_source_compiles() to work with Boost "header-only" libraries.
|
||||
|
||||
find_package(Boost)
|
||||
@@ -46,135 +57,125 @@ endif()
|
||||
check_cxx_source_compiles("
|
||||
#include <atomic>
|
||||
int main() {
|
||||
std::atomic<unsigned int> x;
|
||||
x.fetch_add(1);
|
||||
x.fetch_sub(1);
|
||||
std::atomic_int x(1);
|
||||
++x;
|
||||
--x;
|
||||
return 0;
|
||||
}
|
||||
" HAVE_STD_ATOMIC)
|
||||
|
||||
if(NOT HAVE_STD_ATOMIC)
|
||||
find_package(Boost COMPONENTS atomic)
|
||||
if(Boost_ATOMIC_FOUND)
|
||||
set(HAVE_BOOST_ATOMIC 1)
|
||||
else()
|
||||
set(HAVE_BOOST_ATOMIC 0)
|
||||
endif()
|
||||
check_cxx_source_compiles("
|
||||
int main() {
|
||||
volatile int x;
|
||||
__sync_add_and_fetch(&x, 1);
|
||||
int y = __sync_sub_and_fetch(&x, 1);
|
||||
return 0;
|
||||
}
|
||||
" HAVE_GCC_ATOMIC)
|
||||
|
||||
if(NOT HAVE_BOOST_ATOMIC)
|
||||
if(NOT HAVE_GCC_ATOMIC)
|
||||
check_cxx_source_compiles("
|
||||
#include <libkern/OSAtomic.h>
|
||||
int main() {
|
||||
volatile int x;
|
||||
__sync_add_and_fetch(&x, 1);
|
||||
int y = __sync_sub_and_fetch(&x, 1);
|
||||
volatile int32_t x;
|
||||
OSAtomicIncrement32Barrier(&x);
|
||||
int32_t y = OSAtomicDecrement32Barrier(&x);
|
||||
return 0;
|
||||
}
|
||||
" HAVE_GCC_ATOMIC)
|
||||
" HAVE_MAC_ATOMIC)
|
||||
|
||||
if(NOT HAVE_GCC_ATOMIC)
|
||||
if(NOT HAVE_MAC_ATOMIC)
|
||||
check_cxx_source_compiles("
|
||||
#include <libkern/OSAtomic.h>
|
||||
#include <windows.h>
|
||||
int main() {
|
||||
volatile int32_t x;
|
||||
OSAtomicIncrement32Barrier(&x);
|
||||
int32_t y = OSAtomicDecrement32Barrier(&x);
|
||||
volatile LONG x;
|
||||
InterlockedIncrement(&x);
|
||||
LONG y = InterlockedDecrement(&x);
|
||||
return 0;
|
||||
}
|
||||
" HAVE_MAC_ATOMIC)
|
||||
" HAVE_WIN_ATOMIC)
|
||||
|
||||
if(NOT HAVE_MAC_ATOMIC)
|
||||
if(NOT HAVE_WIN_ATOMIC)
|
||||
check_cxx_source_compiles("
|
||||
#include <windows.h>
|
||||
#include <ia64intrin.h>
|
||||
int main() {
|
||||
volatile LONG x;
|
||||
InterlockedIncrement(&x);
|
||||
LONG y = InterlockedDecrement(&x);
|
||||
volatile int x;
|
||||
__sync_add_and_fetch(&x, 1);
|
||||
int y = __sync_sub_and_fetch(&x, 1);
|
||||
return 0;
|
||||
}
|
||||
" HAVE_WIN_ATOMIC)
|
||||
|
||||
if(NOT HAVE_WIN_ATOMIC)
|
||||
check_cxx_source_compiles("
|
||||
#include <ia64intrin.h>
|
||||
int main() {
|
||||
volatile int x;
|
||||
__sync_add_and_fetch(&x, 1);
|
||||
int y = __sync_sub_and_fetch(&x, 1);
|
||||
return 0;
|
||||
}
|
||||
" HAVE_IA64_ATOMIC)
|
||||
endif()
|
||||
" HAVE_IA64_ATOMIC)
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Determine which kind of smart pointers your compiler supports.
|
||||
|
||||
check_cxx_source_compiles("
|
||||
#include <memory>
|
||||
int main() {
|
||||
std::shared_ptr<int> x;
|
||||
std::unique_ptr<int> y;
|
||||
return 0;
|
||||
}
|
||||
" HAVE_STD_SMART_PTR)
|
||||
|
||||
# Determine which kind of byte swap functions your compiler supports.
|
||||
|
||||
check_cxx_source_compiles("
|
||||
#include <boost/endian/conversion.hpp>
|
||||
int main() {
|
||||
boost::endian::endian_reverse(static_cast<uint16_t>(1));
|
||||
boost::endian::endian_reverse(static_cast<uint32_t>(1));
|
||||
boost::endian::endian_reverse(static_cast<uint64_t>(1));
|
||||
__builtin_bswap16(0);
|
||||
__builtin_bswap32(0);
|
||||
__builtin_bswap64(0);
|
||||
return 0;
|
||||
}
|
||||
" HAVE_BOOST_BYTESWAP)
|
||||
" HAVE_GCC_BYTESWAP)
|
||||
|
||||
if(NOT HAVE_BOOST_BYTESWAP)
|
||||
if(NOT HAVE_GCC_BYTESWAP)
|
||||
check_cxx_source_compiles("
|
||||
#include <byteswap.h>
|
||||
int main() {
|
||||
__builtin_bswap16(0);
|
||||
__builtin_bswap32(0);
|
||||
__builtin_bswap64(0);
|
||||
__bswap_16(0);
|
||||
__bswap_32(0);
|
||||
__bswap_64(0);
|
||||
return 0;
|
||||
}
|
||||
" HAVE_GCC_BYTESWAP)
|
||||
" HAVE_GLIBC_BYTESWAP)
|
||||
|
||||
if(NOT HAVE_GCC_BYTESWAP)
|
||||
if(NOT HAVE_GLIBC_BYTESWAP)
|
||||
check_cxx_source_compiles("
|
||||
#include <byteswap.h>
|
||||
#include <stdlib.h>
|
||||
int main() {
|
||||
__bswap_16(0);
|
||||
__bswap_32(0);
|
||||
__bswap_64(0);
|
||||
_byteswap_ushort(0);
|
||||
_byteswap_ulong(0);
|
||||
_byteswap_uint64(0);
|
||||
return 0;
|
||||
}
|
||||
" HAVE_GLIBC_BYTESWAP)
|
||||
" HAVE_MSC_BYTESWAP)
|
||||
|
||||
if(NOT HAVE_GLIBC_BYTESWAP)
|
||||
if(NOT HAVE_MSC_BYTESWAP)
|
||||
check_cxx_source_compiles("
|
||||
#include <stdlib.h>
|
||||
#include <libkern/OSByteOrder.h>
|
||||
int main() {
|
||||
_byteswap_ushort(0);
|
||||
_byteswap_ulong(0);
|
||||
_byteswap_uint64(0);
|
||||
OSSwapInt16(0);
|
||||
OSSwapInt32(0);
|
||||
OSSwapInt64(0);
|
||||
return 0;
|
||||
}
|
||||
" HAVE_MSC_BYTESWAP)
|
||||
" HAVE_MAC_BYTESWAP)
|
||||
|
||||
if(NOT HAVE_MSC_BYTESWAP)
|
||||
if(NOT HAVE_MAC_BYTESWAP)
|
||||
check_cxx_source_compiles("
|
||||
#include <libkern/OSByteOrder.h>
|
||||
#include <sys/endian.h>
|
||||
int main() {
|
||||
OSSwapInt16(0);
|
||||
OSSwapInt32(0);
|
||||
OSSwapInt64(0);
|
||||
swap16(0);
|
||||
swap32(0);
|
||||
swap64(0);
|
||||
return 0;
|
||||
}
|
||||
" HAVE_MAC_BYTESWAP)
|
||||
|
||||
if(NOT HAVE_MAC_BYTESWAP)
|
||||
check_cxx_source_compiles("
|
||||
#include <sys/endian.h>
|
||||
int main() {
|
||||
swap16(0);
|
||||
swap32(0);
|
||||
swap64(0);
|
||||
return 0;
|
||||
}
|
||||
" HAVE_OPENBSD_BYTESWAP)
|
||||
endif()
|
||||
" HAVE_OPENBSD_BYTESWAP)
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
@@ -225,15 +226,6 @@ if(NOT ZLIB_SOURCE)
|
||||
else()
|
||||
set(HAVE_ZLIB 0)
|
||||
endif()
|
||||
|
||||
if(NOT HAVE_ZLIB)
|
||||
find_package(Boost COMPONENTS iostreams zlib)
|
||||
if(Boost_IOSTREAMS_FOUND AND Boost_ZLIB_FOUND)
|
||||
set(HAVE_BOOST_ZLIB 1)
|
||||
else()
|
||||
set(HAVE_BOOST_ZLIB 0)
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Determine whether CppUnit is installed.
|
||||
@@ -246,3 +238,7 @@ if(BUILD_TESTS AND NOT BUILD_SHARED_LIBS)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Detect WinRT mode
|
||||
if(CMAKE_SYSTEM_NAME STREQUAL "WindowsStore")
|
||||
set(PLATFORM_WINRT 1)
|
||||
endif()
|
||||
|
||||
159
INSTALL
159
INSTALL
@@ -1,159 +0,0 @@
|
||||
TagLib Installation
|
||||
===================
|
||||
|
||||
TagLib uses the CMake build system. As a user, you will most likely want to
|
||||
build TagLib in release mode and install it into a system-wide location.
|
||||
This can be done using the following commands:
|
||||
|
||||
cmake -DCMAKE_INSTALL_PREFIX=/usr/local -DCMAKE_BUILD_TYPE=Release .
|
||||
make
|
||||
sudo make install
|
||||
|
||||
In order to build the included examples, use the BUILD_EXAMPLES option:
|
||||
|
||||
cmake -DBUILD_EXAMPLES=ON [...]
|
||||
|
||||
See http://www.cmake.org/cmake/help/runningcmake.html for generic help on
|
||||
running CMake.
|
||||
|
||||
Mac OS X
|
||||
--------
|
||||
|
||||
On Mac OS X, you might want to build a framework that can be easily integrated
|
||||
into your application. If you set the BUILD_FRAMEWORK option on, it will compile
|
||||
TagLib as a framework. For example, the following command can be used to build
|
||||
an Universal Binary framework with Mac OS X 10.4 as the deployment target:
|
||||
|
||||
cmake -DCMAKE_BUILD_TYPE=Release \
|
||||
-DBUILD_FRAMEWORK=ON \
|
||||
-DCMAKE_C_COMPILER=/usr/bin/gcc-4.0 \
|
||||
-DCMAKE_CXX_COMPILER=/usr/bin/c++-4.0 \
|
||||
-DCMAKE_OSX_SYSROOT=/Developer/SDKs/MacOSX10.4u.sdk/ \
|
||||
-DCMAKE_OSX_DEPLOYMENT_TARGET=10.4 \
|
||||
-DCMAKE_OSX_ARCHITECTURES="ppc;i386;x86_64"
|
||||
|
||||
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" \
|
||||
-DBUILD_SHARED_LIBS=OFF \
|
||||
-DCMAKE_INSTALL_PREFIX="<folder you want to build to>"
|
||||
|
||||
After 'make', and 'make install', add libtag.a to your XCode project, and add
|
||||
the include folder to the project's User Header Search Paths.
|
||||
|
||||
Windows
|
||||
-------
|
||||
|
||||
It's Windows ... Systems vary!
|
||||
This means you need to adjust things to suit your system, especially paths.
|
||||
|
||||
Tested with:
|
||||
Microsoft Visual Studio 2010
|
||||
Gcc by mingw-w64.sf.net v4.6.3 (Strawberry Perl 32b)
|
||||
MinGW32-4.8.0
|
||||
|
||||
Requirements:
|
||||
1. Tool chain, Build Environment, Whatever ya want to call it ...
|
||||
Installed and working.
|
||||
2. CMake program. (Available at: www.cmake.org)
|
||||
Installed and working.
|
||||
|
||||
Optional:
|
||||
1. Zlib library.
|
||||
Available in some Tool Chains, Not all.
|
||||
Search the web, Take your choice.
|
||||
|
||||
Useful configuration options used with CMake (GUI and/or Command line):
|
||||
Any of the ZLIB_ variables may be used at the command line, ZLIB_ROOT is only
|
||||
available on the Command line.
|
||||
ZLIB_ROOT= Where to find ZLib's root directory.
|
||||
Assumes parent of: \include and \lib.
|
||||
ZLIB_INCLUDE_DIR= Where to find ZLib's Include directory.
|
||||
ZLIB_LIBRARY= Where to find ZLib's Library.
|
||||
ZLIB_SOURCE= Where to find ZLib's Source Code.
|
||||
Alternative to ZLIB_INCLUDE_DIR and ZLIB_LIBRARY.
|
||||
CMAKE_INSTALL_PREFIX= Where to install Taglib.
|
||||
CMAKE_BUILD_TYPE= Release, Debug, etc ... (Not available in MSVC)
|
||||
|
||||
The easiest way is at the Command Prompt.
|
||||
MSVS Command Prompt for MSVS Users.
|
||||
(Batch file and/or Shortcuts are your friends)
|
||||
|
||||
1. Build the Makefiles:
|
||||
Replace "GENERATOR" with your needs.
|
||||
For MSVS : "Visual Studio X" where X is the single or two digit version.
|
||||
For MinGW: "MinGW Makefiles"
|
||||
|
||||
C:\GitRoot\taglib> cmake -G "GENERATOR" -DCMAKE_INSTALL_PREFIX=C:\Libraries\taglib
|
||||
|
||||
Or use the CMake GUI:
|
||||
1. Open CMake GUI.
|
||||
2. Set Paths.
|
||||
"Where is the source code" and "Where to build the binaries"
|
||||
Example, Both would be: C:\GitRoot\taglib
|
||||
3. Tick: Advanced
|
||||
4. Select: Configure
|
||||
5. Select: Generator
|
||||
6. Tick: Use default native compilers
|
||||
7. Select: Finish
|
||||
Wait until done.
|
||||
5. If using ZLib, Scroll down.
|
||||
(to the bottom of the list of options ... should go over them all)
|
||||
1. Edit: ZLIB_INCLUDE_DIR
|
||||
2. Edit: ZLIB_LIBRARY
|
||||
6. Select: Generate
|
||||
|
||||
2. Build the project:
|
||||
MSVS:
|
||||
C:\GitRoot\taglib> msbuild all_build.vcxproj /p:Configuration=Release
|
||||
OR (Depending on MSVS version or personal choice)
|
||||
C:\GitRoot\taglib> devenv all_build.vcxproj /build Release
|
||||
MinGW:
|
||||
C:\GitRoot\taglib> gmake
|
||||
OR (Depending on MinGW install)
|
||||
C:\GitRoot\taglib> mingw32-make
|
||||
|
||||
Or in the MSVS GUI:
|
||||
1. Open MSVS.
|
||||
2. Open taglib solution.
|
||||
3. Set build type to: Release (look in the tool bars)
|
||||
2. Hit F7 to build the solution. (project)
|
||||
|
||||
3. Install the project:
|
||||
(Change 'install' to 'uninstall' to uninstall the project)
|
||||
MSVS:
|
||||
C:\GitRoot\taglib> msbuild install.vcxproj
|
||||
OR (Depending on MSVC version or personal choice)
|
||||
C:\GitRoot\taglib> devenv install.vcxproj
|
||||
MinGW:
|
||||
C:\GitRoot\taglib> gmake install
|
||||
OR (Depending on MinGW install)
|
||||
C:\GitRoot\taglib> mingw32-make install
|
||||
|
||||
Or in the MSVS GUI:
|
||||
1. Open project.
|
||||
2. Open Solution Explorer.
|
||||
3. Right Click: INSTALL
|
||||
4. Select: Project Only
|
||||
5. Select: Build Only INSTALL
|
||||
|
||||
To build a static library, set the following two options with CMake.
|
||||
-DBUILD_SHARED_LIBS=OFF -DENABLE_STATIC_RUNTIME=ON
|
||||
|
||||
Including ENABLE_STATIC_RUNTIME=ON indicates you want TagLib built using the
|
||||
static runtime library, rather than the DLL form of the runtime.
|
||||
|
||||
Unit Tests
|
||||
----------
|
||||
|
||||
If you want to run the test suite to make sure TagLib works properly on your
|
||||
system, you need to have cppunit installed. To build the tests, include
|
||||
the option -DBUILD_TESTS=on when running cmake.
|
||||
|
||||
The test suite has a custom target in the build system, so you can run
|
||||
the tests using make:
|
||||
|
||||
make check
|
||||
|
||||
175
INSTALL.md
Normal file
175
INSTALL.md
Normal file
@@ -0,0 +1,175 @@
|
||||
TagLib Installation
|
||||
===================
|
||||
|
||||
TagLib uses the CMake build system. As a user, you will most likely want to
|
||||
build TagLib in release mode and install it into a system-wide location.
|
||||
This can be done using the following commands:
|
||||
|
||||
cmake -DCMAKE_INSTALL_PREFIX=/usr/local -DCMAKE_BUILD_TYPE=Release .
|
||||
make
|
||||
sudo make install
|
||||
|
||||
In order to build the included examples, use the `BUILD_EXAMPLES` option:
|
||||
|
||||
cmake -DBUILD_EXAMPLES=ON [...]
|
||||
|
||||
See http://www.cmake.org/cmake/help/runningcmake.html for generic help on
|
||||
running CMake.
|
||||
|
||||
Mac OS X
|
||||
--------
|
||||
|
||||
On Mac OS X, you might want to build a framework that can be easily integrated
|
||||
into your application. If you set the BUILD_FRAMEWORK option on, it will compile
|
||||
TagLib as a framework. For example, the following command can be used to build
|
||||
an Universal Binary framework with Mac OS X 10.4 as the deployment target:
|
||||
|
||||
cmake -DCMAKE_BUILD_TYPE=Release \
|
||||
-DBUILD_FRAMEWORK=ON \
|
||||
-DCMAKE_C_COMPILER=/usr/bin/gcc-4.0 \
|
||||
-DCMAKE_CXX_COMPILER=/usr/bin/c++-4.0 \
|
||||
-DCMAKE_OSX_SYSROOT=/Developer/SDKs/MacOSX10.4u.sdk/ \
|
||||
-DCMAKE_OSX_DEPLOYMENT_TARGET=10.4 \
|
||||
-DCMAKE_OSX_ARCHITECTURES="ppc;i386;x86_64"
|
||||
|
||||
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" \
|
||||
-DBUILD_SHARED_LIBS=OFF \
|
||||
-DCMAKE_INSTALL_PREFIX="<folder you want to build to>"
|
||||
|
||||
After `make`, and `make install`, add `libtag.` to your XCode project, and add
|
||||
the include folder to the project's User Header Search Paths.
|
||||
|
||||
Windows
|
||||
-------
|
||||
|
||||
It's Windows ... Systems vary!
|
||||
This means you need to adjust things to suit your system, especially paths.
|
||||
|
||||
Tested with:
|
||||
* Microsoft Visual Studio 2010, 2015, 2017
|
||||
* Microsoft C++ Build Tools 2015, 2017 (standalone packages not requiring Visual Studio)
|
||||
* Gcc by mingw-w64.sf.net v4.6.3 (Strawberry Perl 32b)
|
||||
* MinGW32-4.8.0
|
||||
|
||||
Requirements:
|
||||
* Tool chain, build environment, whatever ya want to call it ...
|
||||
Installed and working.
|
||||
* CMake program. (Available at: www.cmake.org)
|
||||
Installed and working.
|
||||
|
||||
Optional:
|
||||
* Zlib library.
|
||||
Available in some tool chains, not all.
|
||||
Search the web, take your choice.
|
||||
|
||||
Useful configuration options used with CMake (GUI and/or command line):
|
||||
Any of the `ZLIB_` variables may be used at the command line, `ZLIB_ROOT` is only
|
||||
available on the command line.
|
||||
|
||||
| option | description |
|
||||
---------------------| ------------|
|
||||
`ZLIB_ROOT=` | Where to find ZLib's root directory. Assumes parent of: `\include` and `\lib.`|
|
||||
`ZLIB_INCLUDE_DIR=` | Where to find ZLib's Include directory.|
|
||||
`ZLIB_LIBRARY=` | Where to find ZLib's Library.
|
||||
`ZLIB_SOURCE=` | Where to find ZLib's Source Code. Alternative to `ZLIB_INCLUDE_DIR` and `ZLIB_LIBRARY`.
|
||||
`CMAKE_INSTALL_PREFIX=` | Where to install Taglib. |
|
||||
`CMAKE_BUILD_TYPE=` | Release, Debug, etc ... (Not available in MSVC) |
|
||||
|
||||
The easiest way is at the command prompt (Visual C++ command prompt for MSVS users – batch file and/or shortcuts are your friends).
|
||||
|
||||
1. **Build the Makefiles:**
|
||||
|
||||
Replace "GENERATOR" with your needs.
|
||||
* For MSVS: `Visual Studio XX YYYY`, e.g. `Visual Studio 14 2015`.
|
||||
|
||||
**Note**: As Visual Studio 2017 supports CMake, you can skip this step and open the taglib
|
||||
folder in VS instead.
|
||||
* For MinGW: `MinGW Makefiles`
|
||||
|
||||
C:\GitRoot\taglib> cmake -G "GENERATOR" -DCMAKE_INSTALL_PREFIX=C:\Libraries\taglib
|
||||
|
||||
Or use the CMake GUI:
|
||||
1. Open CMake GUI.
|
||||
2. Set paths: *Where is the source code* and *Where to build the binaries*.
|
||||
|
||||
In the example, both would be: `C:\GitRoot\taglib`
|
||||
3. Tick: Advanced
|
||||
4. Select: Configure
|
||||
5. Select: Generator
|
||||
6. Tick: Use default native compilers
|
||||
7. Select: Finish
|
||||
Wait until done.
|
||||
8. If using ZLib, Scroll down.
|
||||
(to the bottom of the list of options ... should go over them all)
|
||||
1. Edit: `ZLIB_INCLUDE_DIR`
|
||||
2. Edit: `ZLIB_LIBRARY`
|
||||
9. Select: Generate
|
||||
|
||||
2. **Build the project**
|
||||
* MSVS:
|
||||
|
||||
C:\GitRoot\taglib> msbuild all_build.vcxproj /p:Configuration=Release
|
||||
OR (Depending on MSVS version or personal choice)
|
||||
|
||||
C:\GitRoot\taglib> devenv all_build.vcxproj /build Release
|
||||
OR in the MSVS GUI:
|
||||
1. Open MSVS.
|
||||
2. Open taglib solution.
|
||||
3. Set build type to: Release (look in the tool bars)
|
||||
2. Hit F7 to build the solution. (project)
|
||||
* MinGW:
|
||||
|
||||
C:\GitRoot\taglib> gmake
|
||||
|
||||
OR (Depending on MinGW install)
|
||||
|
||||
C:\GitRoot\taglib> mingw32-make
|
||||
|
||||
|
||||
|
||||
3. **Install the project**
|
||||
|
||||
(Change `install` to `uninstall` to uninstall the project)
|
||||
* MSVS:
|
||||
|
||||
C:\GitRoot\taglib> msbuild install.vcxproj
|
||||
OR (Depending on MSVC version or personal choice)
|
||||
|
||||
C:\GitRoot\taglib> devenv install.vcxproj
|
||||
|
||||
Or in the MSVS GUI:
|
||||
1. Open project.
|
||||
2. Open Solution Explorer.
|
||||
3. Right Click: INSTALL
|
||||
4. Select: Project Only
|
||||
5. Select: Build Only INSTALL
|
||||
* MinGW:
|
||||
|
||||
C:\GitRoot\taglib> gmake install
|
||||
OR (Depending on MinGW install)
|
||||
|
||||
C:\GitRoot\taglib> mingw32-make install
|
||||
|
||||
|
||||
To build a static library, set the following two options with CMake:
|
||||
|
||||
-DBUILD_SHARED_LIBS=OFF -DENABLE_STATIC_RUNTIME=ON
|
||||
|
||||
Including `ENABLE_STATIC_RUNTIME=ON` indicates you want TagLib built using the
|
||||
static runtime library, rather than the DLL form of the runtime.
|
||||
|
||||
Unit Tests
|
||||
----------
|
||||
|
||||
If you want to run the test suite to make sure TagLib works properly on your
|
||||
system, you need to have cppunit installed. To build the tests, include
|
||||
the option `-DBUILD_TESTS=on` when running cmake.
|
||||
|
||||
The test suite has a custom target in the build system, so you can run
|
||||
the tests using make:
|
||||
|
||||
make check
|
||||
39
NEWS
39
NEWS
@@ -1,5 +1,40 @@
|
||||
TagLib 1.11 (Mar 4, 2016)
|
||||
=========================
|
||||
============================
|
||||
|
||||
* Added support for DSF and DSDIFF files.
|
||||
* Added support for WinRT.
|
||||
* Added support for classical music tags of iTunes 12.5.
|
||||
* Added support for file descriptor to FileStream.
|
||||
* Added support for 'cmID', 'purl', 'egid' MP4 atoms.
|
||||
* Enabled FileRef to detect file types based on the stream content.
|
||||
* Dropped support for Windows 9x and NT 4.0 or older.
|
||||
* Check for mandatory header objects in ASF files.
|
||||
* Fixed OOB read on invalid Ogg FLAC files (CVE-2018-11439).
|
||||
* Fixed handling of empty MPEG files.
|
||||
* Fixed reading MP4 atoms with zero length.
|
||||
* Fixed reading FLAC files with zero-sized seektables.
|
||||
* Fixed handling of lowercase field names in Vorbis Comments.
|
||||
* Fixed handling of 'rate' atoms in MP4 files.
|
||||
* Fixed handling of invalid UTF-8 sequences.
|
||||
* Fixed possible file corruptions when saving Ogg files.
|
||||
* TableOfContentsFrame::toString() improved.
|
||||
* UserTextIdentificationFrame::toString() improved.
|
||||
* Marked FileRef::create() deprecated.
|
||||
* Several smaller bug fixes and performance improvements.
|
||||
|
||||
TagLib 1.11.1 (Oct 24, 2016)
|
||||
============================
|
||||
|
||||
* Fixed binary incompatible change in TagLib::String.
|
||||
* Fixed reading ID3v2 CTOC frames with a lot of entries.
|
||||
* Fixed seeking ByteVectorStream from the end.
|
||||
|
||||
TagLib 1.11 (Apr 29, 2016)
|
||||
==========================
|
||||
|
||||
1.11:
|
||||
|
||||
* Fixed reading APE items with long keys.
|
||||
* Fixed reading ID3v2 SYLT frames when description is empty.
|
||||
|
||||
1.11 BETA 2:
|
||||
|
||||
|
||||
26
README.md
Normal file
26
README.md
Normal file
@@ -0,0 +1,26 @@
|
||||
# TagLib
|
||||
|
||||
[](https://travis-ci.org/taglib/taglib)
|
||||
|
||||
### TagLib Audio Metadata Library
|
||||
|
||||
http://taglib.org/
|
||||
|
||||
TagLib is a library for reading and editing the metadata of several
|
||||
popular audio formats. Currently it supports both ID3v1 and [ID3v2][]
|
||||
for MP3 files, [Ogg Vorbis][] comments and ID3 tags
|
||||
in [FLAC][], MPC, Speex, WavPack, TrueAudio, WAV, AIFF, MP4, APE,
|
||||
DSF, DFF, and ASF files.
|
||||
|
||||
TagLib is distributed under the [GNU Lesser General Public License][]
|
||||
(LGPL) and [Mozilla Public License][] (MPL). Essentially that means that
|
||||
it may be used in proprietary applications, but if changes are made to
|
||||
TagLib they must be contributed back to the project. Please review the
|
||||
licenses if you are considering using TagLib in your project.
|
||||
|
||||
[ID3v2]: http://www.id3.org
|
||||
[Ogg Vorbis]: http://vorbis.com/
|
||||
[FLAC]: https://xiph.org/flac/
|
||||
[GNU Lesser General Public License]: http://www.gnu.org/licenses/lgpl.html
|
||||
[Mozilla Public License]: http://www.mozilla.org/MPL/MPL-1.1.html
|
||||
|
||||
@@ -21,7 +21,14 @@ set(tag_c_HDRS tag_c.h)
|
||||
add_library(tag_c tag_c.cpp ${tag_c_HDRS})
|
||||
|
||||
target_link_libraries(tag_c tag)
|
||||
set_target_properties(tag_c PROPERTIES PUBLIC_HEADER "${tag_c_HDRS}")
|
||||
set_target_properties(tag_c PROPERTIES
|
||||
PUBLIC_HEADER "${tag_c_HDRS}"
|
||||
DEFINE_SYMBOL MAKE_TAGLIB_LIB
|
||||
)
|
||||
if(VISIBILITY_HIDDEN)
|
||||
set_target_properties(tag_c PROPERTIES C_VISIBILITY_PRESET hidden
|
||||
)
|
||||
endif()
|
||||
if(BUILD_FRAMEWORK)
|
||||
set_target_properties(tag_c PROPERTIES FRAMEWORK TRUE)
|
||||
endif()
|
||||
|
||||
@@ -92,32 +92,32 @@ void taglib_free(void* pointer)
|
||||
|
||||
TagLib_File *taglib_file_new(const char *filename)
|
||||
{
|
||||
return reinterpret_cast<TagLib_File *>(FileRef::create(filename));
|
||||
return reinterpret_cast<TagLib_File *>(new FileRef(filename));
|
||||
}
|
||||
|
||||
TagLib_File *taglib_file_new_type(const char *filename, TagLib_File_Type type)
|
||||
{
|
||||
switch(type) {
|
||||
case TagLib_File_MPEG:
|
||||
return reinterpret_cast<TagLib_File *>(new MPEG::File(filename));
|
||||
return reinterpret_cast<TagLib_File *>(new FileRef(new MPEG::File(filename)));
|
||||
case TagLib_File_OggVorbis:
|
||||
return reinterpret_cast<TagLib_File *>(new Ogg::Vorbis::File(filename));
|
||||
return reinterpret_cast<TagLib_File *>(new FileRef(new Ogg::Vorbis::File(filename)));
|
||||
case TagLib_File_FLAC:
|
||||
return reinterpret_cast<TagLib_File *>(new FLAC::File(filename));
|
||||
return reinterpret_cast<TagLib_File *>(new FileRef(new FLAC::File(filename)));
|
||||
case TagLib_File_MPC:
|
||||
return reinterpret_cast<TagLib_File *>(new MPC::File(filename));
|
||||
return reinterpret_cast<TagLib_File *>(new FileRef(new MPC::File(filename)));
|
||||
case TagLib_File_OggFlac:
|
||||
return reinterpret_cast<TagLib_File *>(new Ogg::FLAC::File(filename));
|
||||
return reinterpret_cast<TagLib_File *>(new FileRef(new Ogg::FLAC::File(filename)));
|
||||
case TagLib_File_WavPack:
|
||||
return reinterpret_cast<TagLib_File *>(new WavPack::File(filename));
|
||||
return reinterpret_cast<TagLib_File *>(new FileRef(new WavPack::File(filename)));
|
||||
case TagLib_File_Speex:
|
||||
return reinterpret_cast<TagLib_File *>(new Ogg::Speex::File(filename));
|
||||
return reinterpret_cast<TagLib_File *>(new FileRef(new Ogg::Speex::File(filename)));
|
||||
case TagLib_File_TrueAudio:
|
||||
return reinterpret_cast<TagLib_File *>(new TrueAudio::File(filename));
|
||||
return reinterpret_cast<TagLib_File *>(new FileRef(new TrueAudio::File(filename)));
|
||||
case TagLib_File_MP4:
|
||||
return reinterpret_cast<TagLib_File *>(new MP4::File(filename));
|
||||
return reinterpret_cast<TagLib_File *>(new FileRef(new MP4::File(filename)));
|
||||
case TagLib_File_ASF:
|
||||
return reinterpret_cast<TagLib_File *>(new ASF::File(filename));
|
||||
return reinterpret_cast<TagLib_File *>(new FileRef(new ASF::File(filename)));
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
@@ -125,29 +125,29 @@ TagLib_File *taglib_file_new_type(const char *filename, TagLib_File_Type type)
|
||||
|
||||
void taglib_file_free(TagLib_File *file)
|
||||
{
|
||||
delete reinterpret_cast<File *>(file);
|
||||
delete reinterpret_cast<FileRef *>(file);
|
||||
}
|
||||
|
||||
BOOL taglib_file_is_valid(const TagLib_File *file)
|
||||
{
|
||||
return reinterpret_cast<const File *>(file)->isValid();
|
||||
return reinterpret_cast<const FileRef *>(file)->isValid();
|
||||
}
|
||||
|
||||
TagLib_Tag *taglib_file_tag(const TagLib_File *file)
|
||||
{
|
||||
const File *f = reinterpret_cast<const File *>(file);
|
||||
const FileRef *f = reinterpret_cast<const FileRef *>(file);
|
||||
return reinterpret_cast<TagLib_Tag *>(f->tag());
|
||||
}
|
||||
|
||||
const TagLib_AudioProperties *taglib_file_audioproperties(const TagLib_File *file)
|
||||
{
|
||||
const File *f = reinterpret_cast<const File *>(file);
|
||||
const FileRef *f = reinterpret_cast<const FileRef *>(file);
|
||||
return reinterpret_cast<const TagLib_AudioProperties *>(f->audioProperties());
|
||||
}
|
||||
|
||||
BOOL taglib_file_save(TagLib_File *file)
|
||||
{
|
||||
return reinterpret_cast<File *>(file)->save();
|
||||
return reinterpret_cast<FileRef *>(file)->save();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -124,7 +124,7 @@ TAGLIB_C_EXPORT TagLib_File *taglib_file_new_type(const char *filename, TagLib_F
|
||||
TAGLIB_C_EXPORT void taglib_file_free(TagLib_File *file);
|
||||
|
||||
/*!
|
||||
* Returns true if the file is open and readble and valid information for
|
||||
* Returns true if the file is open and readable and valid information for
|
||||
* the Tag and / or AudioProperties was found.
|
||||
*/
|
||||
|
||||
@@ -137,7 +137,7 @@ TAGLIB_C_EXPORT BOOL taglib_file_is_valid(const TagLib_File *file);
|
||||
TAGLIB_C_EXPORT TagLib_Tag *taglib_file_tag(const TagLib_File *file);
|
||||
|
||||
/*!
|
||||
* Returns a pointer to the the audio properties associated with this file. This
|
||||
* Returns a pointer to the audio properties associated with this file. This
|
||||
* will be freed automatically when the file is freed.
|
||||
*/
|
||||
TAGLIB_C_EXPORT const TagLib_AudioProperties *taglib_file_audioproperties(const TagLib_File *file);
|
||||
|
||||
9
cmake/TestFileOffsetBits.c
Normal file
9
cmake/TestFileOffsetBits.c
Normal file
@@ -0,0 +1,9 @@
|
||||
#include <sys/types.h>
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
/* Cause a compile-time error if off_t is smaller than 64 bits */
|
||||
#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))
|
||||
int off_t_is_large[ (LARGE_OFF_T % 2147483629 == 721 && LARGE_OFF_T % 2147483647 == 1) ? 1 : -1 ];
|
||||
return 0;
|
||||
}
|
||||
23
cmake/TestLargeFiles.c.cmakein
Normal file
23
cmake/TestLargeFiles.c.cmakein
Normal file
@@ -0,0 +1,23 @@
|
||||
#cmakedefine _LARGEFILE_SOURCE
|
||||
#cmakedefine _LARGEFILE64_SOURCE
|
||||
#cmakedefine _LARGE_FILES
|
||||
#cmakedefine _FILE_OFFSET_BITS ${_FILE_OFFSET_BITS}
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
/* Cause a compile-time error if off_t is smaller than 64 bits,
|
||||
* and make sure we have ftello / fseeko.
|
||||
*/
|
||||
#define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62))
|
||||
int off_t_is_large[ (LARGE_OFF_T % 2147483629 == 721 && LARGE_OFF_T % 2147483647 == 1) ? 1 : -1 ];
|
||||
FILE *fp = fopen(argv[0],"r");
|
||||
off_t offset = ftello( fp );
|
||||
|
||||
fseeko( fp, offset, SEEK_CUR );
|
||||
fclose(fp);
|
||||
return 0;
|
||||
}
|
||||
104
cmake/modules/TestLargeFiles.cmake
Normal file
104
cmake/modules/TestLargeFiles.cmake
Normal file
@@ -0,0 +1,104 @@
|
||||
# - Define macro to check large file support
|
||||
#
|
||||
# TEST_LARGE_FILES(VARIABLE)
|
||||
#
|
||||
# VARIABLE will be set to true if off_t is 64 bits, and fseeko/ftello present.
|
||||
# This macro will also set defines necessary enable large file support, for instance
|
||||
# _LARGE_FILES
|
||||
# _LARGEFILE_SOURCE
|
||||
# _FILE_OFFSET_BITS 64
|
||||
# HAVE_FSEEKO
|
||||
#
|
||||
# However, it is YOUR job to make sure these defines are set in a cmakedefine so they
|
||||
# end up in a config.h file that is included in your source if necessary!
|
||||
|
||||
# This macro skips the Windows specific checks. Because TagLib uses Win32 API.
|
||||
|
||||
MACRO(TEST_LARGE_FILES VARIABLE)
|
||||
IF(NOT DEFINED ${VARIABLE})
|
||||
|
||||
# On most platforms it is probably overkill to first test the flags for 64-bit off_t,
|
||||
# and then separately fseeko. However, in the future we might have 128-bit filesystems
|
||||
# (ZFS), so it might be dangerous to indiscriminately set e.g. _FILE_OFFSET_BITS=64.
|
||||
|
||||
MESSAGE(STATUS "Checking for 64-bit off_t")
|
||||
|
||||
# First check without any special flags
|
||||
TRY_COMPILE(FILE64_OK "${PROJECT_BINARY_DIR}"
|
||||
"${CMAKE_SOURCE_DIR}/cmake/TestFileOffsetBits.c")
|
||||
if(FILE64_OK)
|
||||
MESSAGE(STATUS "Checking for 64-bit off_t - present")
|
||||
endif(FILE64_OK)
|
||||
|
||||
if(NOT FILE64_OK)
|
||||
# Test with _FILE_OFFSET_BITS=64
|
||||
TRY_COMPILE(FILE64_OK "${PROJECT_BINARY_DIR}"
|
||||
"${CMAKE_SOURCE_DIR}/cmake/TestFileOffsetBits.c"
|
||||
COMPILE_DEFINITIONS "-D_FILE_OFFSET_BITS=64" )
|
||||
if(FILE64_OK)
|
||||
MESSAGE(STATUS "Checking for 64-bit off_t - present with _FILE_OFFSET_BITS=64")
|
||||
set(_FILE_OFFSET_BITS 64)
|
||||
endif(FILE64_OK)
|
||||
endif(NOT FILE64_OK)
|
||||
|
||||
if(NOT FILE64_OK)
|
||||
# Test with _LARGE_FILES
|
||||
TRY_COMPILE(FILE64_OK "${PROJECT_BINARY_DIR}"
|
||||
"${CMAKE_SOURCE_DIR}/cmake/TestFileOffsetBits.c"
|
||||
COMPILE_DEFINITIONS "-D_LARGE_FILES" )
|
||||
if(FILE64_OK)
|
||||
MESSAGE(STATUS "Checking for 64-bit off_t - present with _LARGE_FILES")
|
||||
set(_LARGE_FILES 1)
|
||||
endif(FILE64_OK)
|
||||
endif(NOT FILE64_OK)
|
||||
|
||||
if(NOT FILE64_OK)
|
||||
# Test with _LARGEFILE_SOURCE
|
||||
TRY_COMPILE(FILE64_OK "${PROJECT_BINARY_DIR}"
|
||||
"${CMAKE_SOURCE_DIR}/cmake/TestFileOffsetBits.c"
|
||||
COMPILE_DEFINITIONS "-D_LARGEFILE_SOURCE" )
|
||||
if(FILE64_OK)
|
||||
MESSAGE(STATUS "Checking for 64-bit off_t - present with _LARGEFILE_SOURCE")
|
||||
set(_LARGEFILE_SOURCE 1)
|
||||
endif(FILE64_OK)
|
||||
endif(NOT FILE64_OK)
|
||||
|
||||
if(NOT FILE64_OK)
|
||||
MESSAGE(STATUS "Checking for 64-bit off_t - not present")
|
||||
else(NOT FILE64_OK)
|
||||
|
||||
# Set the flags we might have determined to be required above
|
||||
configure_file("${CMAKE_SOURCE_DIR}/cmake/TestLargeFiles.c.cmakein"
|
||||
"${PROJECT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/TestLargeFiles.c")
|
||||
|
||||
MESSAGE(STATUS "Checking for fseeko/ftello")
|
||||
# Test if ftello/fseeko are available
|
||||
TRY_COMPILE(FSEEKO_COMPILE_OK "${PROJECT_BINARY_DIR}"
|
||||
"${PROJECT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/TestLargeFiles.c")
|
||||
if(FSEEKO_COMPILE_OK)
|
||||
MESSAGE(STATUS "Checking for fseeko/ftello - present")
|
||||
endif(FSEEKO_COMPILE_OK)
|
||||
|
||||
if(NOT FSEEKO_COMPILE_OK)
|
||||
# glibc 2.2 neds _LARGEFILE_SOURCE for fseeko (but not 64-bit off_t...)
|
||||
TRY_COMPILE(FSEEKO_COMPILE_OK "${PROJECT_BINARY_DIR}"
|
||||
"${PROJECT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/TestLargeFiles.c"
|
||||
COMPILE_DEFINITIONS "-D_LARGEFILE_SOURCE" )
|
||||
if(FSEEKO_COMPILE_OK)
|
||||
MESSAGE(STATUS "Checking for fseeko/ftello - present with _LARGEFILE_SOURCE")
|
||||
set(_LARGEFILE_SOURCE 1)
|
||||
endif(FSEEKO_COMPILE_OK)
|
||||
endif(NOT FSEEKO_COMPILE_OK)
|
||||
|
||||
endif(NOT FILE64_OK)
|
||||
|
||||
if(FSEEKO_COMPILE_OK)
|
||||
SET(${VARIABLE} 1 CACHE INTERNAL "Result of test for large file support" FORCE)
|
||||
set(HAVE_FSEEKO 1)
|
||||
else(FSEEKO_COMPILE_OK)
|
||||
MESSAGE(STATUS "Checking for fseeko/ftello - not found")
|
||||
SET(${VARIABLE} 0 CACHE INTERNAL "Result of test for large file support" FORCE)
|
||||
endif(FSEEKO_COMPILE_OK)
|
||||
|
||||
ENDIF(NOT DEFINED ${VARIABLE})
|
||||
ENDMACRO(TEST_LARGE_FILES VARIABLE)
|
||||
@@ -1,7 +1,14 @@
|
||||
/* config.h. Generated by cmake from config.h.cmake */
|
||||
|
||||
#ifndef TAGLIB_CONFIG_H
|
||||
#define TAGLIB_CONFIG_H
|
||||
|
||||
/* Defined if required for large files support */
|
||||
#cmakedefine _LARGE_FILES ${_LARGE_FILES}
|
||||
#cmakedefine _LARGEFILE_SOURCE ${_LARGEFILE_SOURCE}
|
||||
#cmakedefine _FILE_OFFSET_BITS ${_FILE_OFFSET_BITS}
|
||||
|
||||
/* Defined if your compiler supports some byte swap functions */
|
||||
#cmakedefine HAVE_BOOST_BYTESWAP 1
|
||||
#cmakedefine HAVE_GCC_BYTESWAP 1
|
||||
#cmakedefine HAVE_GLIBC_BYTESWAP 1
|
||||
#cmakedefine HAVE_MSC_BYTESWAP 1
|
||||
@@ -10,12 +17,14 @@
|
||||
|
||||
/* Defined if your compiler supports some atomic operations */
|
||||
#cmakedefine HAVE_STD_ATOMIC 1
|
||||
#cmakedefine HAVE_BOOST_ATOMIC 1
|
||||
#cmakedefine HAVE_GCC_ATOMIC 1
|
||||
#cmakedefine HAVE_MAC_ATOMIC 1
|
||||
#cmakedefine HAVE_WIN_ATOMIC 1
|
||||
#cmakedefine HAVE_IA64_ATOMIC 1
|
||||
|
||||
/* Defined if your compiler supports shared_ptr */
|
||||
#cmakedefine HAVE_STD_SMART_PTR 1
|
||||
|
||||
/* Defined if your compiler supports some safer version of vsprintf */
|
||||
#cmakedefine HAVE_VSNPRINTF 1
|
||||
#cmakedefine HAVE_VSPRINTF_S 1
|
||||
@@ -25,9 +34,10 @@
|
||||
|
||||
/* Defined if zlib is installed */
|
||||
#cmakedefine HAVE_ZLIB 1
|
||||
#cmakedefine HAVE_BOOST_ZLIB 1
|
||||
|
||||
/* Indicates whether debug messages are shown even in release mode */
|
||||
#cmakedefine TRACE_IN_RELEASE 1
|
||||
|
||||
#cmakedefine TESTS_DIR "@TESTS_DIR@"
|
||||
|
||||
#endif
|
||||
|
||||
@@ -37,3 +37,7 @@ target_link_libraries(framelist tag)
|
||||
add_executable(strip-id3v1 strip-id3v1.cpp)
|
||||
target_link_libraries(strip-id3v1 tag)
|
||||
|
||||
########### next target ###############
|
||||
|
||||
add_executable(inspect inspect.cpp)
|
||||
target_link_libraries(inspect tag)
|
||||
|
||||
48
examples/inspect.cpp
Normal file
48
examples/inspect.cpp
Normal file
@@ -0,0 +1,48 @@
|
||||
/* Copyright (C) 2012 Lukas Lalinsky <lalinsky@gmail.com>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <iostream>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <fileref.h>
|
||||
#include <tfile.h>
|
||||
|
||||
using namespace std;
|
||||
using namespace TagLib;
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
// process the command line args
|
||||
|
||||
for(int i = 1; i < argc; i++) {
|
||||
|
||||
cout << "******************** \"" << argv[i] << "\"********************" << endl;
|
||||
|
||||
FileRef f(argv[i]);
|
||||
if(!f.isNull() && f.file()) {
|
||||
cout << f.file()->toString().to8Bit(true) << endl;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -28,6 +28,7 @@
|
||||
|
||||
#include <fileref.h>
|
||||
#include <tag.h>
|
||||
#include <tpicturemap.h>
|
||||
#include <tpropertymap.h>
|
||||
|
||||
using namespace std;
|
||||
@@ -45,13 +46,15 @@ int main(int argc, char *argv[])
|
||||
TagLib::Tag *tag = f.tag();
|
||||
|
||||
cout << "-- TAG (basic) --" << endl;
|
||||
cout << "title - \"" << tag->title() << "\"" << endl;
|
||||
cout << "artist - \"" << tag->artist() << "\"" << endl;
|
||||
cout << "album - \"" << tag->album() << "\"" << endl;
|
||||
cout << "year - \"" << tag->year() << "\"" << endl;
|
||||
cout << "comment - \"" << tag->comment() << "\"" << endl;
|
||||
cout << "track - \"" << tag->track() << "\"" << endl;
|
||||
cout << "genre - \"" << tag->genre() << "\"" << endl;
|
||||
cout << "title - \"" << tag->title() << "\"" << endl;
|
||||
cout << "artist - \"" << tag->artist() << "\"" << endl;
|
||||
cout << "album - \"" << tag->album() << "\"" << endl;
|
||||
cout << "year - \"" << tag->year() << "\"" << endl;
|
||||
cout << "comment - \"" << tag->comment() << "\"" << endl;
|
||||
cout << "track - \"" << tag->track() << "\"" << endl;
|
||||
cout << "genre - \"" << tag->genre() << "\"" << endl;
|
||||
if(!tag->pictures().isEmpty())
|
||||
cout << "pictures -" << tag->pictures() << endl;
|
||||
|
||||
TagLib::PropertyMap tags = f.file()->properties();
|
||||
|
||||
|
||||
@@ -22,8 +22,10 @@
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include <sstream>
|
||||
#include <string.h>
|
||||
|
||||
#include <stdio.h>
|
||||
@@ -34,6 +36,8 @@
|
||||
#include <tlist.h>
|
||||
#include <fileref.h>
|
||||
#include <tfile.h>
|
||||
#include <tpicturemap.h>
|
||||
|
||||
#include <tag.h>
|
||||
#include <tpropertymap.h>
|
||||
|
||||
@@ -70,6 +74,7 @@ void usage()
|
||||
cout << " -R <tagname> <tagvalue>" << endl;
|
||||
cout << " -I <tagname> <tagvalue>" << endl;
|
||||
cout << " -D <tagname>" << endl;
|
||||
cout << " -p <picture(jpg only, file between double quotes)>" << endl;
|
||||
cout << endl;
|
||||
|
||||
exit(1);
|
||||
@@ -110,14 +115,16 @@ int main(int argc, char *argv[])
|
||||
if(fileList.isEmpty())
|
||||
usage();
|
||||
|
||||
if(argv[argc-1][1] == 'p')
|
||||
argc++;
|
||||
|
||||
for(int i = 1; i < argc - 1; i += 2) {
|
||||
|
||||
if(isArgument(argv[i]) && i + 1 < argc && !isArgument(argv[i + 1])) {
|
||||
|
||||
char field = argv[i][1];
|
||||
TagLib::String value = argv[i + 1];
|
||||
|
||||
TagLib::List<TagLib::FileRef>::ConstIterator it;
|
||||
TagLib::List<TagLib::FileRef>::Iterator it;
|
||||
for(it = fileList.begin(); it != fileList.end(); ++it) {
|
||||
|
||||
TagLib::Tag *t = (*it).tag();
|
||||
@@ -167,6 +174,31 @@ int main(int argc, char *argv[])
|
||||
checkForRejectedProperties((*it).file()->setProperties(map));
|
||||
break;
|
||||
}
|
||||
case 'p':
|
||||
{
|
||||
if(!isFile(value.toCString())) {
|
||||
cout << value.toCString() << " not found." << endl;
|
||||
return 1;
|
||||
}
|
||||
ifstream picture;
|
||||
picture.open(value.toCString());
|
||||
stringstream buffer;
|
||||
buffer << picture.rdbuf();
|
||||
picture.close();
|
||||
TagLib::String buf(buffer.str());
|
||||
TagLib::ByteVector data(buf.data(TagLib::String::Latin1));
|
||||
if(!data.find("JFIF")) {
|
||||
cout << value.toCString() << " is not a JPEG." << endl;
|
||||
return 1;
|
||||
}
|
||||
TagLib::Picture pic(data,
|
||||
TagLib::Picture::FrontCover,
|
||||
"image/jpeg",
|
||||
"Added with taglib");
|
||||
TagLib::PictureMap picMap(pic);
|
||||
t->setPictures(picMap);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
usage();
|
||||
break;
|
||||
|
||||
@@ -1,36 +1,35 @@
|
||||
@echo off
|
||||
goto beginning
|
||||
*
|
||||
* It is what it is, you can do with it as you please.
|
||||
*
|
||||
* Just don't blame me if it teaches your computer to smoke!
|
||||
*
|
||||
* -Enjoy
|
||||
* fh :)_~
|
||||
*
|
||||
:beginning
|
||||
if /i "%1#" == "--libs#" goto doit
|
||||
if /i "%1#" == "--cflags#" goto doit
|
||||
if /i "%1#" == "--version#" goto doit
|
||||
if /i "%1#" == "--prefix#" goto doit
|
||||
|
||||
echo "usage: %0 [OPTIONS]"
|
||||
echo [--libs]
|
||||
echo [--cflags]
|
||||
echo [--version]
|
||||
echo [--prefix]
|
||||
goto theend
|
||||
|
||||
*
|
||||
* NOTE: Windows does not assume libraries are prefixed with 'lib'.
|
||||
* NOTE: If '-llibtag' is the last element, it is easily appended in the users installation/makefile process
|
||||
* to allow for static, shared or debug builds.
|
||||
* It would be preferable if the top level CMakeLists.txt provided the library name during config. ??
|
||||
:doit
|
||||
if /i "%1#" == "--libs#" echo -L${LIB_INSTALL_DIR} -llibtag
|
||||
if /i "%1#" == "--cflags#" echo -I${INCLUDE_INSTALL_DIR}/taglib
|
||||
if /i "%1#" == "--version#" echo ${TAGLIB_LIB_VERSION_STRING}
|
||||
if /i "%1#" == "--prefix#" echo ${CMAKE_INSTALL_PREFIX}
|
||||
|
||||
:theend
|
||||
|
||||
@echo off
|
||||
goto beginning
|
||||
*
|
||||
* It is what it is, you can do with it as you please.
|
||||
*
|
||||
* Just don't blame me if it teaches your computer to smoke!
|
||||
*
|
||||
* -Enjoy
|
||||
* fh :)_~
|
||||
*
|
||||
:beginning
|
||||
if /i "%1#" == "--libs#" goto doit
|
||||
if /i "%1#" == "--cflags#" goto doit
|
||||
if /i "%1#" == "--version#" goto doit
|
||||
if /i "%1#" == "--prefix#" goto doit
|
||||
|
||||
echo "usage: %0 [OPTIONS]"
|
||||
echo [--libs]
|
||||
echo [--cflags]
|
||||
echo [--version]
|
||||
echo [--prefix]
|
||||
goto theend
|
||||
|
||||
*
|
||||
* NOTE: Windows does not assume libraries are prefixed with 'lib'.
|
||||
* NOTE: If '-llibtag' is the last element, it is easily appended in the users installation/makefile process
|
||||
* to allow for static, shared or debug builds.
|
||||
* It would be preferable if the top level CMakeLists.txt provided the library name during config. ??
|
||||
:doit
|
||||
if /i "%1#" == "--libs#" echo -L${LIB_INSTALL_DIR} -llibtag
|
||||
if /i "%1#" == "--cflags#" echo -I${INCLUDE_INSTALL_DIR}/taglib
|
||||
if /i "%1#" == "--version#" echo ${TAGLIB_LIB_VERSION_STRING}
|
||||
if /i "%1#" == "--prefix#" echo ${CMAKE_INSTALL_PREFIX}
|
||||
|
||||
:theend
|
||||
|
||||
@@ -24,6 +24,11 @@ include_directories(
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/s3m
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/it
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/xm
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/ebml
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/ebml/matroska
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/dsf
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/dsdiff
|
||||
${CMAKE_SOURCE_DIR}/3rdparty
|
||||
)
|
||||
|
||||
if(ZLIB_FOUND)
|
||||
@@ -32,21 +37,17 @@ elseif(HAVE_ZLIB_SOURCE)
|
||||
include_directories(${ZLIB_SOURCE})
|
||||
endif()
|
||||
|
||||
if(HAVE_BOOST_BYTESWAP OR HAVE_BOOST_ATOMIC OR HAVE_BOOST_ZLIB)
|
||||
include_directories(${Boost_INCLUDE_DIR})
|
||||
endif()
|
||||
|
||||
set(tag_HDRS
|
||||
tag.h
|
||||
fileref.h
|
||||
audioproperties.h
|
||||
taglib_export.h
|
||||
${CMAKE_CURRENT_BINARY_DIR}/../taglib_config.h
|
||||
toolkit/taglib.h
|
||||
toolkit/tstring.h
|
||||
toolkit/tlist.h
|
||||
toolkit/tlist.tcc
|
||||
toolkit/tstringlist.h
|
||||
toolkit/tstringhandler.h
|
||||
toolkit/tbytevector.h
|
||||
toolkit/tbytevectorlist.h
|
||||
toolkit/tbytevectorstream.h
|
||||
@@ -55,6 +56,8 @@ set(tag_HDRS
|
||||
toolkit/tfilestream.h
|
||||
toolkit/tmap.h
|
||||
toolkit/tmap.tcc
|
||||
toolkit/tpicture.h
|
||||
toolkit/tpicturemap.h
|
||||
toolkit/tpropertymap.h
|
||||
toolkit/trefcounter.h
|
||||
toolkit/tdebuglistener.h
|
||||
@@ -141,6 +144,17 @@ set(tag_HDRS
|
||||
s3m/s3mproperties.h
|
||||
xm/xmfile.h
|
||||
xm/xmproperties.h
|
||||
ebml/ebmlfile.h
|
||||
ebml/ebmlelement.h
|
||||
ebml/ebmlconstants.h
|
||||
ebml/matroska/ebmlmatroskafile.h
|
||||
ebml/matroska/ebmlmatroskaconstants.h
|
||||
ebml/matroska/ebmlmatroskaaudio.h
|
||||
dsf/dsffile.h
|
||||
dsf/dsfproperties.h
|
||||
dsdiff/dsdifffile.h
|
||||
dsdiff/dsdiffproperties.h
|
||||
dsdiff/dsdiffdiintag.h
|
||||
)
|
||||
|
||||
set(mpeg_SRCS
|
||||
@@ -296,9 +310,32 @@ set(xm_SRCS
|
||||
xm/xmproperties.cpp
|
||||
)
|
||||
|
||||
set(dsf_SRCS
|
||||
dsf/dsffile.cpp
|
||||
dsf/dsfproperties.cpp
|
||||
)
|
||||
|
||||
set(ebml_SRCS
|
||||
ebml/ebmlfile.cpp
|
||||
ebml/ebmlelement.cpp
|
||||
)
|
||||
|
||||
set(matroska_SRCS
|
||||
ebml/matroska/ebmlmatroskafile.cpp
|
||||
ebml/matroska/ebmlmatroskaaudio.cpp
|
||||
)
|
||||
|
||||
set(dsdiff_SRCS
|
||||
dsdiff/dsdifffile.cpp
|
||||
dsdiff/dsdiffproperties.cpp
|
||||
dsdiff/dsdiffdiintag.cpp
|
||||
)
|
||||
|
||||
set(toolkit_SRCS
|
||||
toolkit/taglib.cpp
|
||||
toolkit/tstring.cpp
|
||||
toolkit/tstringlist.cpp
|
||||
toolkit/tstringhandler.cpp
|
||||
toolkit/tbytevector.cpp
|
||||
toolkit/tbytevectorlist.cpp
|
||||
toolkit/tbytevectorstream.cpp
|
||||
@@ -306,18 +343,14 @@ set(toolkit_SRCS
|
||||
toolkit/tfile.cpp
|
||||
toolkit/tfilestream.cpp
|
||||
toolkit/tdebug.cpp
|
||||
toolkit/tpicture.cpp
|
||||
toolkit/tpicturemap.cpp
|
||||
toolkit/tpropertymap.cpp
|
||||
toolkit/trefcounter.cpp
|
||||
toolkit/tdebuglistener.cpp
|
||||
toolkit/tzlib.cpp
|
||||
)
|
||||
|
||||
if(NOT WIN32)
|
||||
set(unicode_SRCS
|
||||
toolkit/unicode.cpp
|
||||
)
|
||||
endif()
|
||||
|
||||
if(HAVE_ZLIB_SOURCE)
|
||||
set(zlib_SRCS
|
||||
${ZLIB_SOURCE}/adler32.c
|
||||
@@ -334,7 +367,8 @@ set(tag_LIB_SRCS
|
||||
${vorbis_SRCS} ${oggflacs_SRCS} ${mpc_SRCS} ${ape_SRCS} ${toolkit_SRCS} ${flacs_SRCS}
|
||||
${wavpack_SRCS} ${speex_SRCS} ${trueaudio_SRCS} ${riff_SRCS} ${aiff_SRCS} ${wav_SRCS}
|
||||
${asf_SRCS} ${mp4_SRCS} ${mod_SRCS} ${s3m_SRCS} ${it_SRCS} ${xm_SRCS} ${opus_SRCS}
|
||||
${unicode_SRCS} ${zlib_SRCS}
|
||||
${ebml_SRCS} ${matroska_SRCS} ${dsf_SRCS} ${dsdiff_SRCS}
|
||||
${zlib_SRCS}
|
||||
tag.cpp
|
||||
tagunion.cpp
|
||||
fileref.cpp
|
||||
@@ -344,18 +378,10 @@ set(tag_LIB_SRCS
|
||||
|
||||
add_library(tag ${tag_LIB_SRCS} ${tag_HDRS})
|
||||
|
||||
if(ZLIB_FOUND)
|
||||
if(HAVE_ZLIB AND NOT HAVE_ZLIB_SOURCE)
|
||||
target_link_libraries(tag ${ZLIB_LIBRARIES})
|
||||
endif()
|
||||
|
||||
if(HAVE_BOOST_ATOMIC)
|
||||
target_link_libraries(tag ${Boost_ATOMIC_LIBRARY})
|
||||
endif()
|
||||
|
||||
if(HAVE_BOOST_ZLIB)
|
||||
target_link_libraries(tag ${Boost_IOSTREAMS_LIBRARY} ${Boost_ZLIB_LIBRARY})
|
||||
endif()
|
||||
|
||||
set_target_properties(tag PROPERTIES
|
||||
VERSION ${TAGLIB_SOVERSION_MAJOR}.${TAGLIB_SOVERSION_MINOR}.${TAGLIB_SOVERSION_PATCH}
|
||||
SOVERSION ${TAGLIB_SOVERSION_MAJOR}
|
||||
@@ -364,8 +390,19 @@ set_target_properties(tag PROPERTIES
|
||||
LINK_INTERFACE_LIBRARIES ""
|
||||
PUBLIC_HEADER "${tag_HDRS}"
|
||||
)
|
||||
if(VISIBILITY_HIDDEN)
|
||||
set_target_properties(tag PROPERTIES C_VISIBILITY_PRESET hidden
|
||||
)
|
||||
endif()
|
||||
|
||||
if(BUILD_FRAMEWORK)
|
||||
set_target_properties(tag PROPERTIES FRAMEWORK TRUE)
|
||||
unset(INSTALL_NAME_DIR)
|
||||
set_target_properties(tag PROPERTIES
|
||||
FRAMEWORK TRUE
|
||||
MACOSX_RPATH 1
|
||||
VERSION "A"
|
||||
SOVERSION "A"
|
||||
)
|
||||
endif()
|
||||
|
||||
install(TARGETS tag
|
||||
|
||||
@@ -39,6 +39,7 @@
|
||||
#include <id3v2header.h>
|
||||
#include <tpropertymap.h>
|
||||
#include <tagutils.h>
|
||||
#include <tsmartptr.h>
|
||||
|
||||
#include "apefile.h"
|
||||
#include "apetag.h"
|
||||
@@ -58,36 +59,40 @@ public:
|
||||
APELocation(-1),
|
||||
APESize(0),
|
||||
ID3v1Location(-1),
|
||||
ID3v2Header(0),
|
||||
ID3v2Location(-1),
|
||||
ID3v2Size(0),
|
||||
properties(0) {}
|
||||
ID3v2Size(0) {}
|
||||
|
||||
~FilePrivate()
|
||||
{
|
||||
delete ID3v2Header;
|
||||
delete properties;
|
||||
}
|
||||
long long APELocation;
|
||||
long long APESize;
|
||||
|
||||
long APELocation;
|
||||
long APESize;
|
||||
long long ID3v1Location;
|
||||
|
||||
long ID3v1Location;
|
||||
SCOPED_PTR<ID3v2::Header> ID3v2Header;
|
||||
long long ID3v2Location;
|
||||
long long ID3v2Size;
|
||||
|
||||
ID3v2::Header *ID3v2Header;
|
||||
long ID3v2Location;
|
||||
long ID3v2Size;
|
||||
DoubleTagUnion tag;
|
||||
|
||||
TagUnion tag;
|
||||
|
||||
Properties *properties;
|
||||
SCOPED_PTR<AudioProperties> properties;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// static members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
bool APE::File::isSupported(IOStream *stream)
|
||||
{
|
||||
// An APE file has an ID "MAC " somewhere. An ID3v2 tag may precede.
|
||||
|
||||
const ByteVector buffer = Utils::readHeader(stream, bufferSize(), true);
|
||||
return (buffer.find("MAC ") != ByteVector::npos());
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// public members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
APE::File::File(FileName file, bool readProperties, Properties::ReadStyle) :
|
||||
APE::File::File(FileName file, bool readProperties, AudioProperties::ReadStyle) :
|
||||
TagLib::File(file),
|
||||
d(new FilePrivate())
|
||||
{
|
||||
@@ -95,7 +100,7 @@ APE::File::File(FileName file, bool readProperties, Properties::ReadStyle) :
|
||||
read(readProperties);
|
||||
}
|
||||
|
||||
APE::File::File(IOStream *stream, bool readProperties, Properties::ReadStyle) :
|
||||
APE::File::File(IOStream *stream, bool readProperties, AudioProperties::ReadStyle) :
|
||||
TagLib::File(stream),
|
||||
d(new FilePrivate())
|
||||
{
|
||||
@@ -113,16 +118,6 @@ TagLib::Tag *APE::File::tag() const
|
||||
return &d->tag;
|
||||
}
|
||||
|
||||
PropertyMap APE::File::properties() const
|
||||
{
|
||||
return d->tag.properties();
|
||||
}
|
||||
|
||||
void APE::File::removeUnsupportedProperties(const StringList &properties)
|
||||
{
|
||||
d->tag.removeUnsupportedProperties(properties);
|
||||
}
|
||||
|
||||
PropertyMap APE::File::setProperties(const PropertyMap &properties)
|
||||
{
|
||||
if(ID3v1Tag())
|
||||
@@ -131,9 +126,9 @@ PropertyMap APE::File::setProperties(const PropertyMap &properties)
|
||||
return APETag(true)->setProperties(properties);
|
||||
}
|
||||
|
||||
APE::Properties *APE::File::audioProperties() const
|
||||
APE::AudioProperties *APE::File::audioProperties() const
|
||||
{
|
||||
return d->properties;
|
||||
return d->properties.get();
|
||||
}
|
||||
|
||||
bool APE::File::save()
|
||||
@@ -183,7 +178,7 @@ bool APE::File::save()
|
||||
}
|
||||
|
||||
const ByteVector data = APETag()->render();
|
||||
insert(data, d->APELocation, d->APESize);
|
||||
insert(data, d->APELocation, static_cast<size_t>(d->APESize));
|
||||
|
||||
if(d->ID3v1Location >= 0)
|
||||
d->ID3v1Location += (static_cast<long>(data.size()) - d->APESize);
|
||||
@@ -195,7 +190,7 @@ bool APE::File::save()
|
||||
// APE tag is empty. Remove the old one.
|
||||
|
||||
if(d->APELocation >= 0) {
|
||||
removeBlock(d->APELocation, d->APESize);
|
||||
removeBlock(d->APELocation, static_cast<size_t>(d->APESize));
|
||||
|
||||
if(d->ID3v1Location >= 0)
|
||||
d->ID3v1Location -= d->APESize;
|
||||
@@ -252,7 +247,7 @@ void APE::File::read(bool readProperties)
|
||||
|
||||
if(d->ID3v2Location >= 0) {
|
||||
seek(d->ID3v2Location);
|
||||
d->ID3v2Header = new ID3v2::Header(readBlock(ID3v2::Header::size()));
|
||||
d->ID3v2Header.reset(new ID3v2::Header(readBlock(ID3v2::Header::size())));
|
||||
d->ID3v2Size = d->ID3v2Header->completeTagSize();
|
||||
}
|
||||
|
||||
@@ -280,7 +275,7 @@ void APE::File::read(bool readProperties)
|
||||
|
||||
if(readProperties) {
|
||||
|
||||
long streamLength;
|
||||
long long streamLength;
|
||||
|
||||
if(d->APELocation >= 0)
|
||||
streamLength = d->APELocation;
|
||||
@@ -297,6 +292,6 @@ void APE::File::read(bool readProperties)
|
||||
seek(0);
|
||||
}
|
||||
|
||||
d->properties = new Properties(this, streamLength);
|
||||
d->properties.reset(new AudioProperties(this, streamLength));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -90,7 +90,7 @@ namespace TagLib {
|
||||
* \note In the current implementation, \a propertiesStyle is ignored.
|
||||
*/
|
||||
File(FileName file, bool readProperties = true,
|
||||
Properties::ReadStyle propertiesStyle = Properties::Average);
|
||||
AudioProperties::ReadStyle propertiesStyle = AudioProperties::Average);
|
||||
|
||||
/*!
|
||||
* Constructs an APE file from \a stream. If \a readProperties is true the
|
||||
@@ -102,7 +102,7 @@ namespace TagLib {
|
||||
* \note In the current implementation, \a propertiesStyle is ignored.
|
||||
*/
|
||||
File(IOStream *stream, bool readProperties = true,
|
||||
Properties::ReadStyle propertiesStyle = Properties::Average);
|
||||
AudioProperties::ReadStyle propertiesStyle = AudioProperties::Average);
|
||||
|
||||
/*!
|
||||
* Destroys this instance of the File.
|
||||
@@ -115,31 +115,18 @@ namespace TagLib {
|
||||
*/
|
||||
virtual TagLib::Tag *tag() const;
|
||||
|
||||
/*!
|
||||
* Implements the unified property interface -- export function.
|
||||
* If the file contains both an APE and an ID3v1 tag, only APE
|
||||
* will be converted to the PropertyMap.
|
||||
*/
|
||||
PropertyMap properties() const;
|
||||
|
||||
/*!
|
||||
* Removes unsupported properties. Forwards to the actual Tag's
|
||||
* removeUnsupportedProperties() function.
|
||||
*/
|
||||
void removeUnsupportedProperties(const StringList &properties);
|
||||
|
||||
/*!
|
||||
* Implements the unified property interface -- import function.
|
||||
* Creates an APEv2 tag if necessary. A potentially existing ID3v1
|
||||
* tag will be updated as well.
|
||||
*/
|
||||
PropertyMap setProperties(const PropertyMap &);
|
||||
virtual PropertyMap setProperties(const PropertyMap &);
|
||||
|
||||
/*!
|
||||
* Returns the APE::Properties for this file. If no audio properties
|
||||
* were read then this will return a null pointer.
|
||||
*/
|
||||
virtual Properties *audioProperties() const;
|
||||
virtual AudioProperties *audioProperties() const;
|
||||
|
||||
/*!
|
||||
* Saves the file.
|
||||
@@ -211,6 +198,15 @@ namespace TagLib {
|
||||
*/
|
||||
bool hasID3v1Tag() const;
|
||||
|
||||
/*!
|
||||
* Returns whether or not the given \a stream can be opened as an APE
|
||||
* file.
|
||||
*
|
||||
* \note This method is designed to do a quick check. The result may
|
||||
* not necessarily be correct.
|
||||
*/
|
||||
static bool isSupported(IOStream *stream);
|
||||
|
||||
private:
|
||||
File(const File &);
|
||||
File &operator=(const File &);
|
||||
|
||||
@@ -175,19 +175,19 @@ void APE::Footer::parse(const ByteVector &data)
|
||||
|
||||
// Read the version number
|
||||
|
||||
d->version = data.toUInt(8, false);
|
||||
d->version = data.toUInt32LE(8);
|
||||
|
||||
// Read the tag size
|
||||
|
||||
d->tagSize = data.toUInt(12, false);
|
||||
d->tagSize = data.toUInt32LE(12);
|
||||
|
||||
// Read the item count
|
||||
|
||||
d->itemCount = data.toUInt(16, false);
|
||||
d->itemCount = data.toUInt32LE(16);
|
||||
|
||||
// Read the flags
|
||||
|
||||
std::bitset<32> flags(TAGLIB_CONSTRUCT_BITSET(data.toUInt(20, false)));
|
||||
std::bitset<32> flags(TAGLIB_CONSTRUCT_BITSET(data.toUInt32LE(20)));
|
||||
|
||||
d->headerPresent = flags[31];
|
||||
d->footerPresent = !flags[30];
|
||||
@@ -206,15 +206,15 @@ ByteVector APE::Footer::render(bool isHeader) const
|
||||
// add the version number -- we always render a 2.000 tag regardless of what
|
||||
// the tag originally was.
|
||||
|
||||
v.append(ByteVector::fromUInt(2000, false));
|
||||
v.append(ByteVector::fromUInt32LE(2000));
|
||||
|
||||
// add the tag size
|
||||
|
||||
v.append(ByteVector::fromUInt(d->tagSize, false));
|
||||
v.append(ByteVector::fromUInt32LE(d->tagSize));
|
||||
|
||||
// add the item count
|
||||
|
||||
v.append(ByteVector::fromUInt(d->itemCount, false));
|
||||
v.append(ByteVector::fromUInt32LE(d->itemCount));
|
||||
|
||||
// render and add the flags
|
||||
|
||||
@@ -224,11 +224,11 @@ ByteVector APE::Footer::render(bool isHeader) const
|
||||
flags[30] = false; // footer is always present
|
||||
flags[29] = isHeader;
|
||||
|
||||
v.append(ByteVector::fromUInt(flags.to_ulong(), false));
|
||||
v.append(ByteVector::fromUInt32LE(flags.to_ulong()));
|
||||
|
||||
// add the reserved 64bit
|
||||
|
||||
v.append(ByteVector::fromLongLong(0));
|
||||
v.append(ByteVector::fromUInt64BE(0));
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@ namespace TagLib {
|
||||
* Constructs an APE footer based on \a data. parse() is called
|
||||
* immediately.
|
||||
*/
|
||||
Footer(const ByteVector &data);
|
||||
explicit Footer(const ByteVector &data);
|
||||
|
||||
/*!
|
||||
* Destroys the footer.
|
||||
|
||||
@@ -25,17 +25,17 @@
|
||||
|
||||
#include <tbytevectorlist.h>
|
||||
#include <tdebug.h>
|
||||
#include <tsmartptr.h>
|
||||
|
||||
#include "apeitem.h"
|
||||
|
||||
using namespace TagLib;
|
||||
using namespace APE;
|
||||
|
||||
class APE::Item::ItemPrivate
|
||||
struct ItemData
|
||||
{
|
||||
public:
|
||||
ItemPrivate() :
|
||||
type(Text),
|
||||
ItemData() :
|
||||
type(Item::Text),
|
||||
readOnly(false) {}
|
||||
|
||||
Item::ItemTypes type;
|
||||
@@ -45,6 +45,15 @@ public:
|
||||
bool readOnly;
|
||||
};
|
||||
|
||||
class APE::Item::ItemPrivate
|
||||
{
|
||||
public:
|
||||
ItemPrivate() :
|
||||
data(new ItemData()) {}
|
||||
|
||||
SHARED_PTR<ItemData> data;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// public members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -57,27 +66,27 @@ APE::Item::Item() :
|
||||
APE::Item::Item(const String &key, const String &value) :
|
||||
d(new ItemPrivate())
|
||||
{
|
||||
d->key = key;
|
||||
d->text.append(value);
|
||||
d->data->key = key;
|
||||
d->data->text.append(value);
|
||||
}
|
||||
|
||||
APE::Item::Item(const String &key, const StringList &values) :
|
||||
d(new ItemPrivate())
|
||||
{
|
||||
d->key = key;
|
||||
d->text = values;
|
||||
d->data->key = key;
|
||||
d->data->text = values;
|
||||
}
|
||||
|
||||
APE::Item::Item(const String &key, const ByteVector &value, bool binary) :
|
||||
d(new ItemPrivate())
|
||||
{
|
||||
d->key = key;
|
||||
d->data->key = key;
|
||||
if(binary) {
|
||||
d->type = Binary;
|
||||
d->value = value;
|
||||
d->data->type = Binary;
|
||||
d->data->value = value;
|
||||
}
|
||||
else {
|
||||
d->text.append(value);
|
||||
d->data->text.append(value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -106,135 +115,122 @@ void APE::Item::swap(Item &item)
|
||||
|
||||
void APE::Item::setReadOnly(bool readOnly)
|
||||
{
|
||||
d->readOnly = readOnly;
|
||||
d->data->readOnly = readOnly;
|
||||
}
|
||||
|
||||
bool APE::Item::isReadOnly() const
|
||||
{
|
||||
return d->readOnly;
|
||||
return d->data->readOnly;
|
||||
}
|
||||
|
||||
void APE::Item::setType(APE::Item::ItemTypes val)
|
||||
{
|
||||
d->type = val;
|
||||
d->data->type = val;
|
||||
}
|
||||
|
||||
APE::Item::ItemTypes APE::Item::type() const
|
||||
{
|
||||
return d->type;
|
||||
return d->data->type;
|
||||
}
|
||||
|
||||
String APE::Item::key() const
|
||||
{
|
||||
return d->key;
|
||||
return d->data->key;
|
||||
}
|
||||
|
||||
ByteVector APE::Item::binaryData() const
|
||||
{
|
||||
return d->value;
|
||||
return d->data->value;
|
||||
}
|
||||
|
||||
void APE::Item::setBinaryData(const ByteVector &value)
|
||||
{
|
||||
d->type = Binary;
|
||||
d->value = value;
|
||||
d->text.clear();
|
||||
}
|
||||
|
||||
ByteVector APE::Item::value() const
|
||||
{
|
||||
// This seems incorrect as it won't be actually rendering the value to keep it
|
||||
// up to date.
|
||||
|
||||
return d->value;
|
||||
d->data->type = Binary;
|
||||
d->data->value = value;
|
||||
d->data->text.clear();
|
||||
}
|
||||
|
||||
void APE::Item::setKey(const String &key)
|
||||
{
|
||||
d->key = key;
|
||||
d->data->key = key;
|
||||
}
|
||||
|
||||
void APE::Item::setValue(const String &value)
|
||||
{
|
||||
d->type = Text;
|
||||
d->text = value;
|
||||
d->value.clear();
|
||||
d->data->type = Text;
|
||||
d->data->text = value;
|
||||
d->data->value.clear();
|
||||
}
|
||||
|
||||
void APE::Item::setValues(const StringList &value)
|
||||
{
|
||||
d->type = Text;
|
||||
d->text = value;
|
||||
d->value.clear();
|
||||
d->data->type = Text;
|
||||
d->data->text = value;
|
||||
d->data->value.clear();
|
||||
}
|
||||
|
||||
void APE::Item::appendValue(const String &value)
|
||||
{
|
||||
d->type = Text;
|
||||
d->text.append(value);
|
||||
d->value.clear();
|
||||
d->data->type = Text;
|
||||
d->data->text.append(value);
|
||||
d->data->value.clear();
|
||||
}
|
||||
|
||||
void APE::Item::appendValues(const StringList &values)
|
||||
{
|
||||
d->type = Text;
|
||||
d->text.append(values);
|
||||
d->value.clear();
|
||||
d->data->type = Text;
|
||||
d->data->text.append(values);
|
||||
d->data->value.clear();
|
||||
}
|
||||
|
||||
int APE::Item::size() const
|
||||
{
|
||||
int result = 8 + d->key.size() + 1;
|
||||
switch(d->type) {
|
||||
size_t result = 8 + d->data->key.size() + 1;
|
||||
switch(d->data->type) {
|
||||
case Text:
|
||||
if(!d->text.isEmpty()) {
|
||||
StringList::ConstIterator it = d->text.begin();
|
||||
if(!d->data->text.isEmpty()) {
|
||||
StringList::ConstIterator it = d->data->text.begin();
|
||||
|
||||
result += it->data(String::UTF8).size();
|
||||
it++;
|
||||
for(; it != d->text.end(); ++it)
|
||||
for(; it != d->data->text.end(); ++it)
|
||||
result += 1 + it->data(String::UTF8).size();
|
||||
}
|
||||
break;
|
||||
|
||||
case Binary:
|
||||
case Locator:
|
||||
result += d->value.size();
|
||||
result += d->data->value.size();
|
||||
break;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
StringList APE::Item::toStringList() const
|
||||
{
|
||||
return d->text;
|
||||
return static_cast<int>(result);
|
||||
}
|
||||
|
||||
StringList APE::Item::values() const
|
||||
{
|
||||
return d->text;
|
||||
return d->data->text;
|
||||
}
|
||||
|
||||
String APE::Item::toString() const
|
||||
{
|
||||
if(d->type == Text && !isEmpty())
|
||||
return d->text.front();
|
||||
if(d->data->type == Text && !isEmpty())
|
||||
return d->data->text.front();
|
||||
else
|
||||
return String();
|
||||
}
|
||||
|
||||
bool APE::Item::isEmpty() const
|
||||
{
|
||||
switch(d->type) {
|
||||
switch(d->data->type) {
|
||||
case Text:
|
||||
if(d->text.isEmpty())
|
||||
if(d->data->text.isEmpty())
|
||||
return true;
|
||||
if(d->text.size() == 1 && d->text.front().isEmpty())
|
||||
if(d->data->text.size() == 1 && d->data->text.front().isEmpty())
|
||||
return true;
|
||||
return false;
|
||||
case Binary:
|
||||
case Locator:
|
||||
return d->value.isEmpty();
|
||||
return d->data->value.isEmpty();
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
@@ -249,51 +245,51 @@ void APE::Item::parse(const ByteVector &data)
|
||||
return;
|
||||
}
|
||||
|
||||
const unsigned int valueLength = data.toUInt(0, false);
|
||||
const unsigned int flags = data.toUInt(4, false);
|
||||
const unsigned int valueLength = data.toUInt32LE(0);
|
||||
const unsigned int flags = data.toUInt32LE(4);
|
||||
|
||||
// An item key can contain ASCII characters from 0x20 up to 0x7E, not UTF-8.
|
||||
// We assume that the validity of the given key has been checked.
|
||||
|
||||
d->key = String(&data[8], String::Latin1);
|
||||
d->data->key = String(&data[8], String::Latin1);
|
||||
|
||||
const ByteVector value = data.mid(8 + d->key.size() + 1, valueLength);
|
||||
const ByteVector value = data.mid(8 + d->data->key.size() + 1, valueLength);
|
||||
|
||||
setReadOnly(flags & 1);
|
||||
setType(ItemTypes((flags >> 1) & 3));
|
||||
|
||||
if(Text == d->type)
|
||||
d->text = StringList(ByteVectorList::split(value, '\0'), String::UTF8);
|
||||
if(Text == d->data->type)
|
||||
d->data->text = StringList(ByteVectorList::split(value, '\0'), String::UTF8);
|
||||
else
|
||||
d->value = value;
|
||||
d->data->value = value;
|
||||
}
|
||||
|
||||
ByteVector APE::Item::render() const
|
||||
{
|
||||
ByteVector data;
|
||||
unsigned int flags = ((d->readOnly) ? 1 : 0) | (d->type << 1);
|
||||
unsigned int flags = ((d->data->readOnly) ? 1 : 0) | (d->data->type << 1);
|
||||
ByteVector value;
|
||||
|
||||
if(isEmpty())
|
||||
return data;
|
||||
|
||||
if(d->type == Text) {
|
||||
StringList::ConstIterator it = d->text.begin();
|
||||
if(d->data->type == Text) {
|
||||
StringList::ConstIterator it = d->data->text.begin();
|
||||
|
||||
value.append(it->data(String::UTF8));
|
||||
it++;
|
||||
for(; it != d->text.end(); ++it) {
|
||||
for(; it != d->data->text.end(); ++it) {
|
||||
value.append('\0');
|
||||
value.append(it->data(String::UTF8));
|
||||
}
|
||||
d->value = value;
|
||||
d->data->value = value;
|
||||
}
|
||||
else
|
||||
value.append(d->value);
|
||||
value.append(d->data->value);
|
||||
|
||||
data.append(ByteVector::fromUInt(value.size(), false));
|
||||
data.append(ByteVector::fromUInt(flags, false));
|
||||
data.append(d->key.data(String::Latin1));
|
||||
data.append(ByteVector::fromUInt32LE(value.size()));
|
||||
data.append(ByteVector::fromUInt32LE(flags));
|
||||
data.append(d->data->key.data(String::Latin1));
|
||||
data.append(ByteVector('\0'));
|
||||
data.append(value);
|
||||
|
||||
|
||||
@@ -61,7 +61,6 @@ namespace TagLib {
|
||||
/*!
|
||||
* Constructs a text item with \a key and \a value.
|
||||
*/
|
||||
// BIC: Remove this, StringList has a constructor from a single string
|
||||
Item(const String &key, const String &value);
|
||||
|
||||
/*!
|
||||
@@ -112,11 +111,6 @@ namespace TagLib {
|
||||
*/
|
||||
void setBinaryData(const ByteVector &value);
|
||||
|
||||
#ifndef DO_NOT_DOCUMENT
|
||||
/* Remove in next binary incompatible release */
|
||||
ByteVector value() const;
|
||||
#endif
|
||||
|
||||
/*!
|
||||
* Sets the key for the item to \a key.
|
||||
*/
|
||||
@@ -163,11 +157,6 @@ namespace TagLib {
|
||||
*/
|
||||
String toString() const;
|
||||
|
||||
#ifndef DO_NOT_DOCUMENT
|
||||
/* Remove in next binary incompatible release */
|
||||
StringList toStringList() const;
|
||||
#endif
|
||||
|
||||
/*!
|
||||
* Returns the list of text values. If the data type is not \a Text, always
|
||||
* returns an empty StringList.
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
|
||||
#include <tstring.h>
|
||||
#include <tdebug.h>
|
||||
#include <bitset>
|
||||
|
||||
#include "id3v2tag.h"
|
||||
#include "apeproperties.h"
|
||||
#include "apefile.h"
|
||||
@@ -38,7 +38,7 @@
|
||||
|
||||
using namespace TagLib;
|
||||
|
||||
class APE::Properties::PropertiesPrivate
|
||||
class APE::AudioProperties::PropertiesPrivate
|
||||
{
|
||||
public:
|
||||
PropertiesPrivate() :
|
||||
@@ -63,66 +63,59 @@ public:
|
||||
// public members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
APE::Properties::Properties(File *, ReadStyle style) :
|
||||
AudioProperties(style),
|
||||
d(new PropertiesPrivate())
|
||||
{
|
||||
debug("APE::Properties::Properties() -- This constructor is no longer used.");
|
||||
}
|
||||
|
||||
APE::Properties::Properties(File *file, long streamLength, ReadStyle style) :
|
||||
AudioProperties(style),
|
||||
APE::AudioProperties::AudioProperties(File *file, long long streamLength, ReadStyle) :
|
||||
TagLib::AudioProperties(),
|
||||
d(new PropertiesPrivate())
|
||||
{
|
||||
read(file, streamLength);
|
||||
}
|
||||
|
||||
APE::Properties::~Properties()
|
||||
APE::AudioProperties::~AudioProperties()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
|
||||
int APE::Properties::length() const
|
||||
int APE::AudioProperties::length() const
|
||||
{
|
||||
return lengthInSeconds();
|
||||
}
|
||||
|
||||
int APE::Properties::lengthInSeconds() const
|
||||
int APE::AudioProperties::lengthInSeconds() const
|
||||
{
|
||||
return d->length / 1000;
|
||||
}
|
||||
|
||||
int APE::Properties::lengthInMilliseconds() const
|
||||
int APE::AudioProperties::lengthInMilliseconds() const
|
||||
{
|
||||
return d->length;
|
||||
}
|
||||
|
||||
int APE::Properties::bitrate() const
|
||||
int APE::AudioProperties::bitrate() const
|
||||
{
|
||||
return d->bitrate;
|
||||
}
|
||||
|
||||
int APE::Properties::sampleRate() const
|
||||
int APE::AudioProperties::sampleRate() const
|
||||
{
|
||||
return d->sampleRate;
|
||||
}
|
||||
|
||||
int APE::Properties::channels() const
|
||||
int APE::AudioProperties::channels() const
|
||||
{
|
||||
return d->channels;
|
||||
}
|
||||
|
||||
int APE::Properties::version() const
|
||||
int APE::AudioProperties::version() const
|
||||
{
|
||||
return d->version;
|
||||
}
|
||||
|
||||
int APE::Properties::bitsPerSample() const
|
||||
int APE::AudioProperties::bitsPerSample() const
|
||||
{
|
||||
return d->bitsPerSample;
|
||||
}
|
||||
|
||||
unsigned int APE::Properties::sampleFrames() const
|
||||
unsigned int APE::AudioProperties::sampleFrames() const
|
||||
{
|
||||
return d->sampleFrames;
|
||||
}
|
||||
@@ -138,14 +131,14 @@ namespace
|
||||
if(header.size() < 6 || !header.startsWith("MAC "))
|
||||
return -1;
|
||||
|
||||
return header.toUShort(4, false);
|
||||
return header.toUInt16LE(4);
|
||||
}
|
||||
}
|
||||
|
||||
void APE::Properties::read(File *file, long streamLength)
|
||||
void APE::AudioProperties::read(File *file, long long streamLength)
|
||||
{
|
||||
// First, we assume that the file pointer is set at the first descriptor.
|
||||
long offset = file->tell();
|
||||
long long offset = file->tell();
|
||||
int version = headerVersion(file->readBlock(6));
|
||||
|
||||
// Next, we look for the descriptor.
|
||||
@@ -156,7 +149,7 @@ void APE::Properties::read(File *file, long streamLength)
|
||||
}
|
||||
|
||||
if(version < 0) {
|
||||
debug("APE::Properties::read() -- APE descriptor not found");
|
||||
debug("APE::AudioProperties::read() -- APE descriptor not found");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -174,17 +167,17 @@ void APE::Properties::read(File *file, long streamLength)
|
||||
}
|
||||
}
|
||||
|
||||
void APE::Properties::analyzeCurrent(File *file)
|
||||
void APE::AudioProperties::analyzeCurrent(File *file)
|
||||
{
|
||||
// Read the descriptor
|
||||
file->seek(2, File::Current);
|
||||
const ByteVector descriptor = file->readBlock(44);
|
||||
if(descriptor.size() < 44) {
|
||||
debug("APE::Properties::analyzeCurrent() -- descriptor is too short.");
|
||||
debug("APE::AudioProperties::analyzeCurrent() -- descriptor is too short.");
|
||||
return;
|
||||
}
|
||||
|
||||
const unsigned int descriptorBytes = descriptor.toUInt(0, false);
|
||||
const unsigned int descriptorBytes = descriptor.toUInt32LE(0);
|
||||
|
||||
if((descriptorBytes - 52) > 0)
|
||||
file->seek(descriptorBytes - 52, File::Current);
|
||||
@@ -192,39 +185,39 @@ void APE::Properties::analyzeCurrent(File *file)
|
||||
// Read the header
|
||||
const ByteVector header = file->readBlock(24);
|
||||
if(header.size() < 24) {
|
||||
debug("APE::Properties::analyzeCurrent() -- MAC header is too short.");
|
||||
debug("APE::AudioProperties::analyzeCurrent() -- MAC header is too short.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the APE info
|
||||
d->channels = header.toShort(18, false);
|
||||
d->sampleRate = header.toUInt(20, false);
|
||||
d->bitsPerSample = header.toShort(16, false);
|
||||
d->channels = header.toUInt16LE(18);
|
||||
d->sampleRate = header.toUInt32LE(20);
|
||||
d->bitsPerSample = header.toUInt16LE(16);
|
||||
|
||||
const unsigned int totalFrames = header.toUInt(12, false);
|
||||
const unsigned int totalFrames = header.toUInt32LE(12);
|
||||
if(totalFrames == 0)
|
||||
return;
|
||||
|
||||
const unsigned int blocksPerFrame = header.toUInt(4, false);
|
||||
const unsigned int finalFrameBlocks = header.toUInt(8, false);
|
||||
const unsigned int blocksPerFrame = header.toUInt32LE(4);
|
||||
const unsigned int finalFrameBlocks = header.toUInt32LE(8);
|
||||
d->sampleFrames = (totalFrames - 1) * blocksPerFrame + finalFrameBlocks;
|
||||
}
|
||||
|
||||
void APE::Properties::analyzeOld(File *file)
|
||||
void APE::AudioProperties::analyzeOld(File *file)
|
||||
{
|
||||
const ByteVector header = file->readBlock(26);
|
||||
if(header.size() < 26) {
|
||||
debug("APE::Properties::analyzeOld() -- MAC header is too short.");
|
||||
debug("APE::AudioProperties::analyzeOld() -- MAC header is too short.");
|
||||
return;
|
||||
}
|
||||
|
||||
const unsigned int totalFrames = header.toUInt(18, false);
|
||||
const unsigned int totalFrames = header.toUInt32LE(18);
|
||||
|
||||
// Fail on 0 length APE files (catches non-finalized APE files)
|
||||
if(totalFrames == 0)
|
||||
return;
|
||||
|
||||
const short compressionLevel = header.toShort(0, false);
|
||||
const short compressionLevel = header.toUInt32LE(0);
|
||||
unsigned int blocksPerFrame;
|
||||
if(d->version >= 3950)
|
||||
blocksPerFrame = 73728 * 4;
|
||||
@@ -234,19 +227,19 @@ void APE::Properties::analyzeOld(File *file)
|
||||
blocksPerFrame = 9216;
|
||||
|
||||
// Get the APE info
|
||||
d->channels = header.toShort(4, false);
|
||||
d->sampleRate = header.toUInt(6, false);
|
||||
d->channels = header.toUInt16LE(4);
|
||||
d->sampleRate = header.toUInt32LE(6);
|
||||
|
||||
const unsigned int finalFrameBlocks = header.toUInt(22, false);
|
||||
const unsigned int finalFrameBlocks = header.toUInt32LE(22);
|
||||
d->sampleFrames = (totalFrames - 1) * blocksPerFrame + finalFrameBlocks;
|
||||
|
||||
// Get the bit depth from the RIFF-fmt chunk.
|
||||
file->seek(16, File::Current);
|
||||
const ByteVector fmt = file->readBlock(28);
|
||||
if(fmt.size() < 28 || !fmt.startsWith("WAVEfmt ")) {
|
||||
debug("APE::Properties::analyzeOld() -- fmt header is too short.");
|
||||
debug("APE::AudioProperties::analyzeOld() -- fmt header is too short.");
|
||||
return;
|
||||
}
|
||||
|
||||
d->bitsPerSample = fmt.toShort(26, false);
|
||||
d->bitsPerSample = fmt.toUInt16LE(26);
|
||||
}
|
||||
|
||||
@@ -46,27 +46,19 @@ namespace TagLib {
|
||||
* API.
|
||||
*/
|
||||
|
||||
class TAGLIB_EXPORT Properties : public AudioProperties
|
||||
class TAGLIB_EXPORT AudioProperties : public TagLib::AudioProperties
|
||||
{
|
||||
public:
|
||||
/*!
|
||||
* Create an instance of APE::Properties with the data read from the
|
||||
* APE::File \a file.
|
||||
*
|
||||
* \deprecated
|
||||
*/
|
||||
Properties(File *file, ReadStyle style = Average);
|
||||
AudioProperties(File *file, long long streamLength, ReadStyle style = Average);
|
||||
|
||||
/*!
|
||||
* Create an instance of APE::Properties with the data read from the
|
||||
* APE::File \a file.
|
||||
* Destroys this APE::AudioProperties instance.
|
||||
*/
|
||||
Properties(File *file, long streamLength, ReadStyle style = Average);
|
||||
|
||||
/*!
|
||||
* Destroys this APE::Properties instance.
|
||||
*/
|
||||
virtual ~Properties();
|
||||
virtual ~AudioProperties();
|
||||
|
||||
/*!
|
||||
* Returns the length of the file in seconds. The length is rounded down to
|
||||
@@ -84,16 +76,14 @@ namespace TagLib {
|
||||
*
|
||||
* \see lengthInMilliseconds()
|
||||
*/
|
||||
// BIC: make virtual
|
||||
int lengthInSeconds() const;
|
||||
virtual int lengthInSeconds() const;
|
||||
|
||||
/*!
|
||||
* Returns the length of the file in milliseconds.
|
||||
*
|
||||
* \see lengthInSeconds()
|
||||
*/
|
||||
// BIC: make virtual
|
||||
int lengthInMilliseconds() const;
|
||||
virtual int lengthInMilliseconds() const;
|
||||
|
||||
/*!
|
||||
* Returns the average bit rate of the file in kb/s.
|
||||
@@ -126,11 +116,7 @@ namespace TagLib {
|
||||
int version() const;
|
||||
|
||||
private:
|
||||
Properties(const Properties &);
|
||||
Properties &operator=(const Properties &);
|
||||
|
||||
void read(File *file, long streamLength);
|
||||
|
||||
void read(File *file, long long streamLength);
|
||||
void analyzeCurrent(File *file);
|
||||
void analyzeOld(File *file);
|
||||
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
#include <tfile.h>
|
||||
#include <tstring.h>
|
||||
#include <tmap.h>
|
||||
#include <tpicturemap.h>
|
||||
#include <tpropertymap.h>
|
||||
#include <tdebug.h>
|
||||
#include <tutils.h>
|
||||
@@ -47,23 +48,24 @@ using namespace APE;
|
||||
|
||||
namespace
|
||||
{
|
||||
bool isKeyValid(const char *key, size_t length)
|
||||
const unsigned int MinKeyLength = 2;
|
||||
const unsigned int MaxKeyLength = 255;
|
||||
|
||||
bool isKeyValid(const ByteVector &key)
|
||||
{
|
||||
const char *invalidKeys[] = { "ID3", "TAG", "OGGS", "MP+", 0 };
|
||||
|
||||
if(length < 2 || length > 16)
|
||||
return false;
|
||||
|
||||
// only allow printable ASCII including space (32..126)
|
||||
|
||||
for(const char *p = key; p < key + length; ++p) {
|
||||
const int c = static_cast<unsigned char>(*p);
|
||||
for(ByteVector::ConstIterator it = key.begin(); it != key.end(); ++it) {
|
||||
const int c = static_cast<unsigned char>(*it);
|
||||
if(c < 32 || c > 126)
|
||||
return false;
|
||||
}
|
||||
|
||||
const String upperKey = String(key).upper();
|
||||
for(size_t i = 0; invalidKeys[i] != 0; ++i) {
|
||||
if(Utils::equalsIgnoreCase(key, invalidKeys[i]))
|
||||
if(upperKey == invalidKeys[i])
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -79,7 +81,7 @@ public:
|
||||
footerLocation(0) {}
|
||||
|
||||
File *file;
|
||||
long footerLocation;
|
||||
long long footerLocation;
|
||||
|
||||
Footer footer;
|
||||
ItemListMap itemListMap;
|
||||
@@ -95,7 +97,7 @@ APE::Tag::Tag() :
|
||||
{
|
||||
}
|
||||
|
||||
APE::Tag::Tag(TagLib::File *file, long footerLocation) :
|
||||
APE::Tag::Tag(TagLib::File *file, long long footerLocation) :
|
||||
TagLib::Tag(),
|
||||
d(new TagPrivate())
|
||||
{
|
||||
@@ -164,6 +166,42 @@ unsigned int APE::Tag::track() const
|
||||
return d->itemListMap["TRACK"].toString().toInt();
|
||||
}
|
||||
|
||||
TagLib::PictureMap APE::Tag::pictures() const
|
||||
{
|
||||
PictureMap map;
|
||||
if(d->itemListMap.contains(FRONT_COVER)) {
|
||||
Item front = d->itemListMap[FRONT_COVER];
|
||||
if(Item::Binary == front.type()) {
|
||||
ByteVector picture = front.binaryData();
|
||||
const size_t index = picture.find('\0');
|
||||
if(index < picture.size()) {
|
||||
ByteVector desc = picture.mid(0, index + 1);
|
||||
String mime = "image/jpeg";
|
||||
ByteVector data = picture.mid(index + 1);
|
||||
Picture p(data, Picture::FrontCover, mime, desc);
|
||||
map.insert(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(d->itemListMap.contains(BACK_COVER)) {
|
||||
Item back = d->itemListMap[BACK_COVER];
|
||||
if(Item::Binary == back.type()) {
|
||||
ByteVector picture = back.binaryData();
|
||||
const size_t index = picture.find('\0');
|
||||
if(index < picture.size()) {
|
||||
ByteVector desc = picture.mid(0, index + 1);
|
||||
String mime = "image/jpeg";
|
||||
ByteVector data = picture.mid(index + 1);
|
||||
Picture p(data, Picture::BackCover, mime, desc);
|
||||
map.insert(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return PictureMap(map);
|
||||
}
|
||||
|
||||
void APE::Tag::setTitle(const String &s)
|
||||
{
|
||||
addValue("TITLE", s, true);
|
||||
@@ -191,7 +229,7 @@ void APE::Tag::setGenre(const String &s)
|
||||
|
||||
void APE::Tag::setYear(unsigned int i)
|
||||
{
|
||||
if(i <= 0)
|
||||
if(i == 0)
|
||||
removeItem("YEAR");
|
||||
else
|
||||
addValue("YEAR", String::number(i), true);
|
||||
@@ -199,12 +237,69 @@ void APE::Tag::setYear(unsigned int i)
|
||||
|
||||
void APE::Tag::setTrack(unsigned int i)
|
||||
{
|
||||
if(i <= 0)
|
||||
if(i == 0)
|
||||
removeItem("TRACK");
|
||||
else
|
||||
addValue("TRACK", String::number(i), true);
|
||||
}
|
||||
|
||||
void APE::Tag::setPictures(const PictureMap &l)
|
||||
{
|
||||
removeItem(FRONT_COVER);
|
||||
removeItem(BACK_COVER);
|
||||
for(PictureMap::ConstIterator pictureMapIt = l.begin();
|
||||
pictureMapIt != l.end();
|
||||
++pictureMapIt) {
|
||||
Picture::Type type = pictureMapIt->first;
|
||||
if(Picture::FrontCover != type && Picture::BackCover != type) {
|
||||
std::cout << "APE: Trying to add a picture with wrong type"
|
||||
<< std::endl;
|
||||
continue;
|
||||
}
|
||||
|
||||
const char *id;
|
||||
switch(type) {
|
||||
case Picture::FrontCover:
|
||||
id = FRONT_COVER;
|
||||
break;
|
||||
case Picture::BackCover:
|
||||
id = BACK_COVER;
|
||||
break;
|
||||
default:
|
||||
id = FRONT_COVER;
|
||||
break;
|
||||
}
|
||||
|
||||
PictureList list = pictureMapIt->second;
|
||||
for(PictureList::ConstIterator pictureListIt = list.begin();
|
||||
pictureListIt != list.end();
|
||||
++pictureListIt) {
|
||||
Picture picture = *pictureListIt;
|
||||
if(d->itemListMap.contains(id)) {
|
||||
std::cout << "APE: Already added a picture of type "
|
||||
<< id
|
||||
<< " '"
|
||||
<< picture.description()
|
||||
<< "' "
|
||||
<< "and next are being ignored"
|
||||
<< std::endl;
|
||||
break;
|
||||
}
|
||||
|
||||
ByteVector data = picture.description().data(String::Latin1)
|
||||
.append('\0')
|
||||
.append(picture.data());
|
||||
|
||||
Item item;
|
||||
item.setKey(id);
|
||||
item.setType(Item::Binary);
|
||||
item.setBinaryData(data);
|
||||
setItem(item.key(), item);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
// conversions of tag keys between what we use in PropertyMap and what's usual
|
||||
@@ -235,7 +330,7 @@ PropertyMap APE::Tag::properties() const
|
||||
if(tagName == keyConversions[i][1])
|
||||
tagName = keyConversions[i][0];
|
||||
}
|
||||
properties[tagName].append(it->second.toStringList());
|
||||
properties[tagName].append(it->second.values());
|
||||
}
|
||||
}
|
||||
return properties;
|
||||
@@ -296,11 +391,10 @@ PropertyMap APE::Tag::setProperties(const PropertyMap &origProps)
|
||||
|
||||
bool APE::Tag::checkKey(const String &key)
|
||||
{
|
||||
if(!key.isLatin1())
|
||||
if(key.size() < MinKeyLength || key.size() > MaxKeyLength)
|
||||
return false;
|
||||
|
||||
const std::string data = key.to8Bit(false);
|
||||
return isKeyValid(data.c_str(), data.size());
|
||||
return isKeyValid(key.data(String::UTF8));
|
||||
}
|
||||
|
||||
APE::Footer *APE::Tag::footer() const
|
||||
@@ -393,7 +487,7 @@ ByteVector APE::Tag::render() const
|
||||
}
|
||||
|
||||
d->footer.setItemCount(itemCount);
|
||||
d->footer.setTagSize(data.size() + Footer::size());
|
||||
d->footer.setTagSize(static_cast<unsigned int>(data.size() + Footer::size()));
|
||||
d->footer.setHeaderPresent(true);
|
||||
|
||||
return d->footer.renderHeader() + data + d->footer.renderFooter();
|
||||
@@ -406,20 +500,23 @@ void APE::Tag::parse(const ByteVector &data)
|
||||
if(data.size() < 11)
|
||||
return;
|
||||
|
||||
unsigned int pos = 0;
|
||||
size_t pos = 0;
|
||||
|
||||
for(unsigned int i = 0; i < d->footer.itemCount() && pos <= data.size() - 11; i++) {
|
||||
|
||||
const int nullPos = data.find('\0', pos + 8);
|
||||
if(nullPos < 0) {
|
||||
const size_t nullPos = data.find('\0', pos + 8);
|
||||
if(nullPos == ByteVector::npos()) {
|
||||
debug("APE::Tag::parse() - Couldn't find a key/value separator. Stopped parsing.");
|
||||
return;
|
||||
}
|
||||
|
||||
const unsigned int keyLength = nullPos - pos - 8;
|
||||
const unsigned int valLegnth = data.toUInt(pos, false);
|
||||
const size_t keyLength = nullPos - pos - 8;
|
||||
const size_t valLegnth = data.toUInt32LE(pos);
|
||||
|
||||
if(isKeyValid(&data[pos + 8], keyLength)){
|
||||
if(keyLength >= MinKeyLength
|
||||
&& keyLength <= MaxKeyLength
|
||||
&& isKeyValid(data.mid(pos + 8, keyLength)))
|
||||
{
|
||||
APE::Item item;
|
||||
item.parse(data.mid(pos));
|
||||
|
||||
|
||||
@@ -34,6 +34,9 @@
|
||||
|
||||
#include "apeitem.h"
|
||||
|
||||
#define FRONT_COVER "COVER ART (FRONT)"
|
||||
#define BACK_COVER "COVER ART (BACK)"
|
||||
|
||||
namespace TagLib {
|
||||
|
||||
class File;
|
||||
@@ -49,7 +52,7 @@ namespace TagLib {
|
||||
*
|
||||
* \see APE::Tag::itemListMap()
|
||||
*/
|
||||
typedef Map<const String, Item> ItemListMap;
|
||||
typedef Map<String, Item> ItemListMap;
|
||||
|
||||
|
||||
//! An APE tag implementation
|
||||
@@ -66,7 +69,7 @@ namespace TagLib {
|
||||
* Create an APE tag and parse the data in \a file with APE footer at
|
||||
* \a tagOffset.
|
||||
*/
|
||||
Tag(TagLib::File *file, long footerLocation);
|
||||
Tag(TagLib::File *file, long long footerLocation);
|
||||
|
||||
/*!
|
||||
* Destroys this Tag instance.
|
||||
@@ -95,6 +98,15 @@ namespace TagLib {
|
||||
virtual unsigned int year() const;
|
||||
virtual unsigned int track() const;
|
||||
|
||||
/**
|
||||
* @brief pictures
|
||||
* According to :
|
||||
* http://www.hydrogenaud.io/forums/index.php?showtopic=40603&st=50&p=504669&#entry504669
|
||||
* http://git.videolan.org/?p=vlc.git;a=blob;f=modules/meta_engine/taglib.cpp
|
||||
* @return
|
||||
*/
|
||||
virtual PictureMap pictures() const;
|
||||
|
||||
virtual void setTitle(const String &s);
|
||||
virtual void setArtist(const String &s);
|
||||
virtual void setAlbum(const String &s);
|
||||
@@ -102,6 +114,7 @@ namespace TagLib {
|
||||
virtual void setGenre(const String &s);
|
||||
virtual void setYear(unsigned int i);
|
||||
virtual void setTrack(unsigned int i);
|
||||
virtual void setPictures(const PictureMap &l);
|
||||
|
||||
/*!
|
||||
* Implements the unified tag dictionary interface -- export function.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/**************************************************************************
|
||||
/**************************************************************************
|
||||
copyright : (C) 2005-2007 by Lukáš Lalinský
|
||||
email : lalinsky@gmail.com
|
||||
**************************************************************************/
|
||||
@@ -25,7 +25,7 @@
|
||||
|
||||
#include <taglib.h>
|
||||
#include <tdebug.h>
|
||||
#include <trefcounter.h>
|
||||
#include <tsmartptr.h>
|
||||
|
||||
#include "asfattribute.h"
|
||||
#include "asffile.h"
|
||||
@@ -33,25 +33,35 @@
|
||||
|
||||
using namespace TagLib;
|
||||
|
||||
class ASF::Attribute::AttributePrivate : public RefCounter
|
||||
namespace
|
||||
{
|
||||
public:
|
||||
AttributePrivate()
|
||||
: pictureValue(ASF::Picture::fromInvalid()),
|
||||
struct AttributeData
|
||||
{
|
||||
AttributeData() :
|
||||
numericValue(0),
|
||||
stream(0),
|
||||
language(0) {}
|
||||
AttributeTypes type;
|
||||
String stringValue;
|
||||
ByteVector byteVectorValue;
|
||||
ASF::Picture pictureValue;
|
||||
union {
|
||||
unsigned int intValue;
|
||||
unsigned short shortValue;
|
||||
unsigned long long longLongValue;
|
||||
bool boolValue;
|
||||
|
||||
ASF::Attribute::AttributeTypes type;
|
||||
String stringValue;
|
||||
ByteVector byteVectorValue;
|
||||
ASF::Picture pictureValue;
|
||||
unsigned long long numericValue;
|
||||
int stream;
|
||||
int language;
|
||||
};
|
||||
int stream;
|
||||
int language;
|
||||
}
|
||||
|
||||
class ASF::Attribute::AttributePrivate
|
||||
{
|
||||
public:
|
||||
AttributePrivate() :
|
||||
data(new AttributeData())
|
||||
{
|
||||
data->pictureValue = ASF::Picture::fromInvalid();
|
||||
}
|
||||
|
||||
SHARED_PTR<AttributeData> data;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -61,62 +71,61 @@ public:
|
||||
ASF::Attribute::Attribute() :
|
||||
d(new AttributePrivate())
|
||||
{
|
||||
d->type = UnicodeType;
|
||||
d->data->type = UnicodeType;
|
||||
}
|
||||
|
||||
ASF::Attribute::Attribute(const ASF::Attribute &other) :
|
||||
d(other.d)
|
||||
d(new AttributePrivate(*other.d))
|
||||
{
|
||||
d->ref();
|
||||
}
|
||||
|
||||
ASF::Attribute::Attribute(const String &value) :
|
||||
d(new AttributePrivate())
|
||||
{
|
||||
d->type = UnicodeType;
|
||||
d->stringValue = value;
|
||||
d->data->type = UnicodeType;
|
||||
d->data->stringValue = value;
|
||||
}
|
||||
|
||||
ASF::Attribute::Attribute(const ByteVector &value) :
|
||||
d(new AttributePrivate())
|
||||
{
|
||||
d->type = BytesType;
|
||||
d->byteVectorValue = value;
|
||||
d->data->type = BytesType;
|
||||
d->data->byteVectorValue = value;
|
||||
}
|
||||
|
||||
ASF::Attribute::Attribute(const ASF::Picture &value) :
|
||||
d(new AttributePrivate())
|
||||
{
|
||||
d->type = BytesType;
|
||||
d->pictureValue = value;
|
||||
d->data->type = BytesType;
|
||||
d->data->pictureValue = value;
|
||||
}
|
||||
|
||||
ASF::Attribute::Attribute(unsigned int value) :
|
||||
d(new AttributePrivate())
|
||||
{
|
||||
d->type = DWordType;
|
||||
d->intValue = value;
|
||||
d->data->type = DWordType;
|
||||
d->data->numericValue = value;
|
||||
}
|
||||
|
||||
ASF::Attribute::Attribute(unsigned long long value) :
|
||||
d(new AttributePrivate())
|
||||
{
|
||||
d->type = QWordType;
|
||||
d->longLongValue = value;
|
||||
d->data->type = QWordType;
|
||||
d->data->numericValue = value;
|
||||
}
|
||||
|
||||
ASF::Attribute::Attribute(unsigned short value) :
|
||||
d(new AttributePrivate())
|
||||
{
|
||||
d->type = WordType;
|
||||
d->shortValue = value;
|
||||
d->data->type = WordType;
|
||||
d->data->numericValue = value;
|
||||
}
|
||||
|
||||
ASF::Attribute::Attribute(bool value) :
|
||||
d(new AttributePrivate())
|
||||
{
|
||||
d->type = BoolType;
|
||||
d->boolValue = value;
|
||||
d->data->type = BoolType;
|
||||
d->data->numericValue = value;
|
||||
}
|
||||
|
||||
ASF::Attribute &ASF::Attribute::operator=(const ASF::Attribute &other)
|
||||
@@ -134,62 +143,62 @@ void ASF::Attribute::swap(Attribute &other)
|
||||
|
||||
ASF::Attribute::~Attribute()
|
||||
{
|
||||
if(d->deref())
|
||||
delete d;
|
||||
delete d;
|
||||
}
|
||||
|
||||
ASF::Attribute::AttributeTypes ASF::Attribute::type() const
|
||||
{
|
||||
return d->type;
|
||||
return d->data->type;
|
||||
}
|
||||
|
||||
String ASF::Attribute::toString() const
|
||||
{
|
||||
return d->stringValue;
|
||||
return d->data->stringValue;
|
||||
}
|
||||
|
||||
ByteVector ASF::Attribute::toByteVector() const
|
||||
{
|
||||
if(d->pictureValue.isValid())
|
||||
return d->pictureValue.render();
|
||||
return d->byteVectorValue;
|
||||
if(d->data->pictureValue.isValid())
|
||||
return d->data->pictureValue.render();
|
||||
|
||||
return d->data->byteVectorValue;
|
||||
}
|
||||
|
||||
unsigned short ASF::Attribute::toBool() const
|
||||
{
|
||||
return d->shortValue;
|
||||
return d->data->numericValue ? 1 : 0;
|
||||
}
|
||||
|
||||
unsigned short ASF::Attribute::toUShort() const
|
||||
{
|
||||
return d->shortValue;
|
||||
return static_cast<unsigned short>(d->data->numericValue);
|
||||
}
|
||||
|
||||
unsigned int ASF::Attribute::toUInt() const
|
||||
{
|
||||
return d->intValue;
|
||||
return static_cast<unsigned int>(d->data->numericValue);
|
||||
}
|
||||
|
||||
unsigned long long ASF::Attribute::toULongLong() const
|
||||
{
|
||||
return d->longLongValue;
|
||||
return static_cast<unsigned long long>(d->data->numericValue);
|
||||
}
|
||||
|
||||
ASF::Picture ASF::Attribute::toPicture() const
|
||||
{
|
||||
return d->pictureValue;
|
||||
return d->data->pictureValue;
|
||||
}
|
||||
|
||||
String ASF::Attribute::parse(ASF::File &f, int kind)
|
||||
{
|
||||
unsigned int size, nameLength;
|
||||
String name;
|
||||
d->pictureValue = Picture::fromInvalid();
|
||||
d->data->pictureValue = Picture::fromInvalid();
|
||||
// extended content descriptor
|
||||
if(kind == 0) {
|
||||
nameLength = readWORD(&f);
|
||||
name = readString(&f, nameLength);
|
||||
d->type = ASF::Attribute::AttributeTypes(readWORD(&f));
|
||||
d->data->type = ASF::Attribute::AttributeTypes(readWORD(&f));
|
||||
size = readWORD(&f);
|
||||
}
|
||||
// metadata & metadata library
|
||||
@@ -197,11 +206,11 @@ String ASF::Attribute::parse(ASF::File &f, int kind)
|
||||
int temp = readWORD(&f);
|
||||
// metadata library
|
||||
if(kind == 2) {
|
||||
d->language = temp;
|
||||
d->data->language = temp;
|
||||
}
|
||||
d->stream = readWORD(&f);
|
||||
d->data->stream = readWORD(&f);
|
||||
nameLength = readWORD(&f);
|
||||
d->type = ASF::Attribute::AttributeTypes(readWORD(&f));
|
||||
d->data->type = ASF::Attribute::AttributeTypes(readWORD(&f));
|
||||
size = readDWORD(&f);
|
||||
name = readString(&f, nameLength);
|
||||
}
|
||||
@@ -210,42 +219,42 @@ String ASF::Attribute::parse(ASF::File &f, int kind)
|
||||
debug("ASF::Attribute::parse() -- Value larger than 64kB");
|
||||
}
|
||||
|
||||
switch(d->type) {
|
||||
switch(d->data->type) {
|
||||
case WordType:
|
||||
d->shortValue = readWORD(&f);
|
||||
d->data->numericValue = readWORD(&f);
|
||||
break;
|
||||
|
||||
case BoolType:
|
||||
if(kind == 0) {
|
||||
d->boolValue = (readDWORD(&f) == 1);
|
||||
d->data->numericValue = (readDWORD(&f) != 0);
|
||||
}
|
||||
else {
|
||||
d->boolValue = (readWORD(&f) == 1);
|
||||
d->data->numericValue = (readWORD(&f) != 0);
|
||||
}
|
||||
break;
|
||||
|
||||
case DWordType:
|
||||
d->intValue = readDWORD(&f);
|
||||
d->data->numericValue = readDWORD(&f);
|
||||
break;
|
||||
|
||||
case QWordType:
|
||||
d->longLongValue = readQWORD(&f);
|
||||
d->data->numericValue = readQWORD(&f);
|
||||
break;
|
||||
|
||||
case UnicodeType:
|
||||
d->stringValue = readString(&f, size);
|
||||
d->data->stringValue = readString(&f, size);
|
||||
break;
|
||||
|
||||
case BytesType:
|
||||
case GuidType:
|
||||
d->byteVectorValue = f.readBlock(size);
|
||||
d->data->byteVectorValue = f.readBlock(size);
|
||||
break;
|
||||
}
|
||||
|
||||
if(d->type == BytesType && name == "WM/Picture") {
|
||||
d->pictureValue.parse(d->byteVectorValue);
|
||||
if(d->pictureValue.isValid()) {
|
||||
d->byteVectorValue.clear();
|
||||
if(d->data->type == BytesType && name == "WM/Picture") {
|
||||
d->data->pictureValue.parse(d->data->byteVectorValue);
|
||||
if(d->data->pictureValue.isValid()) {
|
||||
d->data->byteVectorValue.clear();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -254,7 +263,7 @@ String ASF::Attribute::parse(ASF::File &f, int kind)
|
||||
|
||||
int ASF::Attribute::dataSize() const
|
||||
{
|
||||
switch (d->type) {
|
||||
switch (d->data->type) {
|
||||
case WordType:
|
||||
return 2;
|
||||
case BoolType:
|
||||
@@ -264,12 +273,12 @@ int ASF::Attribute::dataSize() const
|
||||
case QWordType:
|
||||
return 5;
|
||||
case UnicodeType:
|
||||
return d->stringValue.size() * 2 + 2;
|
||||
return static_cast<int>(d->data->stringValue.size() * 2 + 2);
|
||||
case BytesType:
|
||||
if(d->pictureValue.isValid())
|
||||
return d->pictureValue.dataSize();
|
||||
if(d->data->pictureValue.isValid())
|
||||
return d->data->pictureValue.dataSize();
|
||||
case GuidType:
|
||||
return d->byteVectorValue.size();
|
||||
return static_cast<int>(d->data->byteVectorValue.size());
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -278,55 +287,55 @@ ByteVector ASF::Attribute::render(const String &name, int kind) const
|
||||
{
|
||||
ByteVector data;
|
||||
|
||||
switch (d->type) {
|
||||
switch (d->data->type) {
|
||||
case WordType:
|
||||
data.append(ByteVector::fromShort(d->shortValue, false));
|
||||
data.append(ByteVector::fromUInt16LE(toUShort()));
|
||||
break;
|
||||
|
||||
case BoolType:
|
||||
if(kind == 0) {
|
||||
data.append(ByteVector::fromUInt(d->boolValue ? 1 : 0, false));
|
||||
data.append(ByteVector::fromUInt32LE(toBool()));
|
||||
}
|
||||
else {
|
||||
data.append(ByteVector::fromShort(d->boolValue ? 1 : 0, false));
|
||||
data.append(ByteVector::fromUInt16LE(toBool()));
|
||||
}
|
||||
break;
|
||||
|
||||
case DWordType:
|
||||
data.append(ByteVector::fromUInt(d->intValue, false));
|
||||
data.append(ByteVector::fromUInt32LE(toUInt()));
|
||||
break;
|
||||
|
||||
case QWordType:
|
||||
data.append(ByteVector::fromLongLong(d->longLongValue, false));
|
||||
data.append(ByteVector::fromUInt64LE(toULongLong()));
|
||||
break;
|
||||
|
||||
case UnicodeType:
|
||||
data.append(renderString(d->stringValue));
|
||||
data.append(renderString(d->data->stringValue));
|
||||
break;
|
||||
|
||||
case BytesType:
|
||||
if(d->pictureValue.isValid()) {
|
||||
data.append(d->pictureValue.render());
|
||||
if(d->data->pictureValue.isValid()) {
|
||||
data.append(d->data->pictureValue.render());
|
||||
break;
|
||||
}
|
||||
case GuidType:
|
||||
data.append(d->byteVectorValue);
|
||||
data.append(d->data->byteVectorValue);
|
||||
break;
|
||||
}
|
||||
|
||||
if(kind == 0) {
|
||||
data = renderString(name, true) +
|
||||
ByteVector::fromShort((int)d->type, false) +
|
||||
ByteVector::fromShort(data.size(), false) +
|
||||
ByteVector::fromUInt16LE((int)d->data->type) +
|
||||
ByteVector::fromUInt16LE(data.size()) +
|
||||
data;
|
||||
}
|
||||
else {
|
||||
ByteVector nameData = renderString(name);
|
||||
data = ByteVector::fromShort(kind == 2 ? d->language : 0, false) +
|
||||
ByteVector::fromShort(d->stream, false) +
|
||||
ByteVector::fromShort(nameData.size(), false) +
|
||||
ByteVector::fromShort((int)d->type, false) +
|
||||
ByteVector::fromUInt(data.size(), false) +
|
||||
data = ByteVector::fromUInt16LE(kind == 2 ? d->data->language : 0) +
|
||||
ByteVector::fromUInt16LE(d->data->stream) +
|
||||
ByteVector::fromUInt16LE(nameData.size()) +
|
||||
ByteVector::fromUInt16LE((int)d->data->type) +
|
||||
ByteVector::fromUInt32LE(data.size()) +
|
||||
nameData +
|
||||
data;
|
||||
}
|
||||
@@ -336,20 +345,20 @@ ByteVector ASF::Attribute::render(const String &name, int kind) const
|
||||
|
||||
int ASF::Attribute::language() const
|
||||
{
|
||||
return d->language;
|
||||
return d->data->language;
|
||||
}
|
||||
|
||||
void ASF::Attribute::setLanguage(int value)
|
||||
{
|
||||
d->language = value;
|
||||
d->data->language = value;
|
||||
}
|
||||
|
||||
int ASF::Attribute::stream() const
|
||||
{
|
||||
return d->stream;
|
||||
return d->data->stream;
|
||||
}
|
||||
|
||||
void ASF::Attribute::setStream(int value)
|
||||
{
|
||||
d->stream = value;
|
||||
d->data->stream = value;
|
||||
}
|
||||
|
||||
@@ -36,7 +36,6 @@ namespace TagLib
|
||||
|
||||
namespace ASF
|
||||
{
|
||||
|
||||
class File;
|
||||
class Picture;
|
||||
|
||||
@@ -113,7 +112,7 @@ namespace TagLib
|
||||
/*!
|
||||
* Copies the contents of \a other into this item.
|
||||
*/
|
||||
ASF::Attribute &operator=(const Attribute &other);
|
||||
Attribute &operator=(const Attribute &other);
|
||||
|
||||
/*!
|
||||
* Exchanges the content of the Attribute by the content of \a other.
|
||||
@@ -185,17 +184,13 @@ namespace TagLib
|
||||
*/
|
||||
void setStream(int value);
|
||||
|
||||
#ifndef DO_NOT_DOCUMENT
|
||||
/* THIS IS PRIVATE, DON'T TOUCH IT! */
|
||||
String parse(ASF::File &file, int kind = 0);
|
||||
#endif
|
||||
|
||||
//! Returns the size of the stored data
|
||||
int dataSize() const;
|
||||
|
||||
private:
|
||||
friend class File;
|
||||
|
||||
String parse(ASF::File &file, int kind = 0);
|
||||
ByteVector render(const String &name, int kind = 0) const;
|
||||
|
||||
class AttributePrivate;
|
||||
|
||||
@@ -27,6 +27,8 @@
|
||||
#include <tbytevectorlist.h>
|
||||
#include <tpropertymap.h>
|
||||
#include <tstring.h>
|
||||
#include <tagutils.h>
|
||||
#include <tsmartptr.h>
|
||||
|
||||
#include "asffile.h"
|
||||
#include "asftag.h"
|
||||
@@ -49,37 +51,24 @@ public:
|
||||
class MetadataObject;
|
||||
class MetadataLibraryObject;
|
||||
|
||||
FilePrivate():
|
||||
headerSize(0),
|
||||
tag(0),
|
||||
properties(0),
|
||||
contentDescriptionObject(0),
|
||||
extendedContentDescriptionObject(0),
|
||||
headerExtensionObject(0),
|
||||
metadataObject(0),
|
||||
metadataLibraryObject(0)
|
||||
{
|
||||
objects.setAutoDelete(true);
|
||||
}
|
||||
typedef List<SHARED_PTR<BaseObject> > ObjectList;
|
||||
typedef ObjectList::ConstIterator ObjectConstIterator;
|
||||
|
||||
~FilePrivate()
|
||||
{
|
||||
delete tag;
|
||||
delete properties;
|
||||
}
|
||||
FilePrivate():
|
||||
headerSize(0) {}
|
||||
|
||||
unsigned long long headerSize;
|
||||
|
||||
ASF::Tag *tag;
|
||||
ASF::Properties *properties;
|
||||
SCOPED_PTR<ASF::Tag> tag;
|
||||
SCOPED_PTR<ASF::AudioProperties> properties;
|
||||
|
||||
List<BaseObject *> objects;
|
||||
ObjectList objects;
|
||||
|
||||
ContentDescriptionObject *contentDescriptionObject;
|
||||
ExtendedContentDescriptionObject *extendedContentDescriptionObject;
|
||||
HeaderExtensionObject *headerExtensionObject;
|
||||
MetadataObject *metadataObject;
|
||||
MetadataLibraryObject *metadataLibraryObject;
|
||||
SHARED_PTR<ContentDescriptionObject> contentDescriptionObject;
|
||||
SHARED_PTR<ExtendedContentDescriptionObject> extendedContentDescriptionObject;
|
||||
SHARED_PTR<HeaderExtensionObject> headerExtensionObject;
|
||||
SHARED_PTR<MetadataObject> metadataObject;
|
||||
SHARED_PTR<MetadataLibraryObject> metadataLibraryObject;
|
||||
};
|
||||
|
||||
namespace
|
||||
@@ -112,7 +101,7 @@ class ASF::File::FilePrivate::UnknownObject : public ASF::File::FilePrivate::Bas
|
||||
{
|
||||
ByteVector myGuid;
|
||||
public:
|
||||
UnknownObject(const ByteVector &guid);
|
||||
explicit UnknownObject(const ByteVector &guid);
|
||||
ByteVector guid() const;
|
||||
};
|
||||
|
||||
@@ -168,7 +157,7 @@ public:
|
||||
class ASF::File::FilePrivate::HeaderExtensionObject : public ASF::File::FilePrivate::BaseObject
|
||||
{
|
||||
public:
|
||||
List<ASF::File::FilePrivate::BaseObject *> objects;
|
||||
ObjectList objects;
|
||||
HeaderExtensionObject();
|
||||
ByteVector guid() const;
|
||||
void parse(ASF::File *file, unsigned int size);
|
||||
@@ -193,7 +182,7 @@ private:
|
||||
void ASF::File::FilePrivate::BaseObject::parse(ASF::File *file, unsigned int size)
|
||||
{
|
||||
data.clear();
|
||||
if(size > 24 && size <= (unsigned int)(file->length()))
|
||||
if(size > 24 && static_cast<long long>(size) <= file->length())
|
||||
data = file->readBlock(size - 24);
|
||||
else
|
||||
data = ByteVector();
|
||||
@@ -201,7 +190,7 @@ void ASF::File::FilePrivate::BaseObject::parse(ASF::File *file, unsigned int siz
|
||||
|
||||
ByteVector ASF::File::FilePrivate::BaseObject::render(ASF::File * /*file*/)
|
||||
{
|
||||
return guid() + ByteVector::fromLongLong(data.size() + 24, false) + data;
|
||||
return guid() + ByteVector::fromUInt64LE(data.size() + 24) + data;
|
||||
}
|
||||
|
||||
ASF::File::FilePrivate::UnknownObject::UnknownObject(const ByteVector &guid) : myGuid(guid)
|
||||
@@ -226,8 +215,8 @@ void ASF::File::FilePrivate::FilePropertiesObject::parse(ASF::File *file, unsign
|
||||
return;
|
||||
}
|
||||
|
||||
const long long duration = data.toLongLong(40, false);
|
||||
const long long preroll = data.toLongLong(56, false);
|
||||
const long long duration = data.toInt64LE(40);
|
||||
const long long preroll = data.toInt64LE(56);
|
||||
file->d->properties->setLengthInMilliseconds(static_cast<int>(duration / 10000.0 - preroll + 0.5));
|
||||
}
|
||||
|
||||
@@ -244,11 +233,11 @@ void ASF::File::FilePrivate::StreamPropertiesObject::parse(ASF::File *file, unsi
|
||||
return;
|
||||
}
|
||||
|
||||
file->d->properties->setCodec(data.toUShort(54, false));
|
||||
file->d->properties->setChannels(data.toUShort(56, false));
|
||||
file->d->properties->setSampleRate(data.toUInt(58, false));
|
||||
file->d->properties->setBitrate(static_cast<int>(data.toUInt(62, false) * 8.0 / 1000.0 + 0.5));
|
||||
file->d->properties->setBitsPerSample(data.toUShort(68, false));
|
||||
file->d->properties->setCodec(data.toUInt16LE(54));
|
||||
file->d->properties->setChannels(data.toUInt16LE(56));
|
||||
file->d->properties->setSampleRate(data.toUInt32LE(58));
|
||||
file->d->properties->setBitrate(static_cast<int>(data.toUInt32LE(62) * 8.0 / 1000.0 + 0.5));
|
||||
file->d->properties->setBitsPerSample(data.toUInt16LE(68));
|
||||
}
|
||||
|
||||
ByteVector ASF::File::FilePrivate::ContentDescriptionObject::guid() const
|
||||
@@ -258,7 +247,6 @@ ByteVector ASF::File::FilePrivate::ContentDescriptionObject::guid() const
|
||||
|
||||
void ASF::File::FilePrivate::ContentDescriptionObject::parse(ASF::File *file, unsigned int /*size*/)
|
||||
{
|
||||
file->d->contentDescriptionObject = this;
|
||||
const int titleLength = readWORD(file);
|
||||
const int artistLength = readWORD(file);
|
||||
const int copyrightLength = readWORD(file);
|
||||
@@ -279,11 +267,11 @@ ByteVector ASF::File::FilePrivate::ContentDescriptionObject::render(ASF::File *f
|
||||
const ByteVector v4 = renderString(file->d->tag->comment());
|
||||
const ByteVector v5 = renderString(file->d->tag->rating());
|
||||
data.clear();
|
||||
data.append(ByteVector::fromShort(v1.size(), false));
|
||||
data.append(ByteVector::fromShort(v2.size(), false));
|
||||
data.append(ByteVector::fromShort(v3.size(), false));
|
||||
data.append(ByteVector::fromShort(v4.size(), false));
|
||||
data.append(ByteVector::fromShort(v5.size(), false));
|
||||
data.append(ByteVector::fromUInt16LE(v1.size()));
|
||||
data.append(ByteVector::fromUInt16LE(v2.size()));
|
||||
data.append(ByteVector::fromUInt16LE(v3.size()));
|
||||
data.append(ByteVector::fromUInt16LE(v4.size()));
|
||||
data.append(ByteVector::fromUInt16LE(v5.size()));
|
||||
data.append(v1);
|
||||
data.append(v2);
|
||||
data.append(v3);
|
||||
@@ -299,7 +287,6 @@ ByteVector ASF::File::FilePrivate::ExtendedContentDescriptionObject::guid() cons
|
||||
|
||||
void ASF::File::FilePrivate::ExtendedContentDescriptionObject::parse(ASF::File *file, unsigned int /*size*/)
|
||||
{
|
||||
file->d->extendedContentDescriptionObject = this;
|
||||
int count = readWORD(file);
|
||||
while(count--) {
|
||||
ASF::Attribute attribute;
|
||||
@@ -311,7 +298,7 @@ void ASF::File::FilePrivate::ExtendedContentDescriptionObject::parse(ASF::File *
|
||||
ByteVector ASF::File::FilePrivate::ExtendedContentDescriptionObject::render(ASF::File *file)
|
||||
{
|
||||
data.clear();
|
||||
data.append(ByteVector::fromShort(attributeData.size(), false));
|
||||
data.append(ByteVector::fromUInt16LE(attributeData.size()));
|
||||
data.append(attributeData.toByteVector(""));
|
||||
return BaseObject::render(file);
|
||||
}
|
||||
@@ -323,7 +310,6 @@ ByteVector ASF::File::FilePrivate::MetadataObject::guid() const
|
||||
|
||||
void ASF::File::FilePrivate::MetadataObject::parse(ASF::File *file, unsigned int /*size*/)
|
||||
{
|
||||
file->d->metadataObject = this;
|
||||
int count = readWORD(file);
|
||||
while(count--) {
|
||||
ASF::Attribute attribute;
|
||||
@@ -335,7 +321,7 @@ void ASF::File::FilePrivate::MetadataObject::parse(ASF::File *file, unsigned int
|
||||
ByteVector ASF::File::FilePrivate::MetadataObject::render(ASF::File *file)
|
||||
{
|
||||
data.clear();
|
||||
data.append(ByteVector::fromShort(attributeData.size(), false));
|
||||
data.append(ByteVector::fromUInt16LE(attributeData.size()));
|
||||
data.append(attributeData.toByteVector(""));
|
||||
return BaseObject::render(file);
|
||||
}
|
||||
@@ -347,7 +333,6 @@ ByteVector ASF::File::FilePrivate::MetadataLibraryObject::guid() const
|
||||
|
||||
void ASF::File::FilePrivate::MetadataLibraryObject::parse(ASF::File *file, unsigned int /*size*/)
|
||||
{
|
||||
file->d->metadataLibraryObject = this;
|
||||
int count = readWORD(file);
|
||||
while(count--) {
|
||||
ASF::Attribute attribute;
|
||||
@@ -359,14 +344,13 @@ void ASF::File::FilePrivate::MetadataLibraryObject::parse(ASF::File *file, unsig
|
||||
ByteVector ASF::File::FilePrivate::MetadataLibraryObject::render(ASF::File *file)
|
||||
{
|
||||
data.clear();
|
||||
data.append(ByteVector::fromShort(attributeData.size(), false));
|
||||
data.append(ByteVector::fromUInt16LE(attributeData.size()));
|
||||
data.append(attributeData.toByteVector(""));
|
||||
return BaseObject::render(file);
|
||||
}
|
||||
|
||||
ASF::File::FilePrivate::HeaderExtensionObject::HeaderExtensionObject()
|
||||
{
|
||||
objects.setAutoDelete(true);
|
||||
}
|
||||
|
||||
ByteVector ASF::File::FilePrivate::HeaderExtensionObject::guid() const
|
||||
@@ -376,7 +360,6 @@ ByteVector ASF::File::FilePrivate::HeaderExtensionObject::guid() const
|
||||
|
||||
void ASF::File::FilePrivate::HeaderExtensionObject::parse(ASF::File *file, unsigned int /*size*/)
|
||||
{
|
||||
file->d->headerExtensionObject = this;
|
||||
file->seek(18, File::Current);
|
||||
long long dataSize = readDWORD(file);
|
||||
long long dataPos = 0;
|
||||
@@ -392,15 +375,17 @@ void ASF::File::FilePrivate::HeaderExtensionObject::parse(ASF::File *file, unsig
|
||||
file->setValid(false);
|
||||
break;
|
||||
}
|
||||
BaseObject *obj;
|
||||
SHARED_PTR<BaseObject> obj;
|
||||
if(guid == metadataGuid) {
|
||||
obj = new MetadataObject();
|
||||
file->d->metadataObject.reset(new MetadataObject());
|
||||
obj = file->d->metadataObject;
|
||||
}
|
||||
else if(guid == metadataLibraryGuid) {
|
||||
obj = new MetadataLibraryObject();
|
||||
file->d->metadataLibraryObject.reset(new MetadataLibraryObject());
|
||||
obj = file->d->metadataLibraryObject;
|
||||
}
|
||||
else {
|
||||
obj = new UnknownObject(guid);
|
||||
obj.reset(new UnknownObject(guid));
|
||||
}
|
||||
obj->parse(file, (unsigned int)size);
|
||||
objects.append(obj);
|
||||
@@ -411,10 +396,10 @@ void ASF::File::FilePrivate::HeaderExtensionObject::parse(ASF::File *file, unsig
|
||||
ByteVector ASF::File::FilePrivate::HeaderExtensionObject::render(ASF::File *file)
|
||||
{
|
||||
data.clear();
|
||||
for(List<BaseObject *>::ConstIterator it = objects.begin(); it != objects.end(); ++it) {
|
||||
for(ObjectConstIterator it = objects.begin(); it != objects.end(); ++it) {
|
||||
data.append((*it)->render(file));
|
||||
}
|
||||
data = ByteVector("\x11\xD2\xD3\xAB\xBA\xA9\xcf\x11\x8E\xE6\x00\xC0\x0C\x20\x53\x65\x06\x00", 18) + ByteVector::fromUInt(data.size(), false) + data;
|
||||
data = ByteVector("\x11\xD2\xD3\xAB\xBA\xA9\xcf\x11\x8E\xE6\x00\xC0\x0C\x20\x53\x65\x06\x00", 18) + ByteVector::fromUInt32LE(data.size()) + data;
|
||||
return BaseObject::render(file);
|
||||
}
|
||||
|
||||
@@ -433,7 +418,7 @@ void ASF::File::FilePrivate::CodecListObject::parse(ASF::File *file, unsigned in
|
||||
|
||||
unsigned int pos = 16;
|
||||
|
||||
const int count = data.toUInt(pos, false);
|
||||
const int count = data.toUInt32LE(pos);
|
||||
pos += 4;
|
||||
|
||||
for(int i = 0; i < count; ++i) {
|
||||
@@ -441,22 +426,22 @@ void ASF::File::FilePrivate::CodecListObject::parse(ASF::File *file, unsigned in
|
||||
if(pos >= data.size())
|
||||
break;
|
||||
|
||||
const CodecType type = static_cast<CodecType>(data.toUShort(pos, false));
|
||||
const CodecType type = static_cast<CodecType>(data.toUInt16LE(pos));
|
||||
pos += 2;
|
||||
|
||||
int nameLength = data.toUShort(pos, false);
|
||||
int nameLength = data.toUInt16LE(pos);
|
||||
pos += 2;
|
||||
|
||||
const unsigned int namePos = pos;
|
||||
pos += nameLength * 2;
|
||||
|
||||
const int descLength = data.toUShort(pos, false);
|
||||
const int descLength = data.toUInt16LE(pos);
|
||||
pos += 2;
|
||||
|
||||
const unsigned int descPos = pos;
|
||||
pos += descLength * 2;
|
||||
|
||||
const int infoLength = data.toUShort(pos, false);
|
||||
const int infoLength = data.toUInt16LE(pos);
|
||||
pos += 2 + infoLength * 2;
|
||||
|
||||
if(type == CodecListObject::Audio) {
|
||||
@@ -473,11 +458,23 @@ void ASF::File::FilePrivate::CodecListObject::parse(ASF::File *file, unsigned in
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// static members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
bool ASF::File::isSupported(IOStream *stream)
|
||||
{
|
||||
// An ASF file has to start with the designated GUID.
|
||||
|
||||
const ByteVector id = Utils::readHeader(stream, 16, false);
|
||||
return (id == headerGuid);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// public members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
ASF::File::File(FileName file, bool, Properties::ReadStyle) :
|
||||
ASF::File::File(FileName file, bool, AudioProperties::ReadStyle) :
|
||||
TagLib::File(file),
|
||||
d(new FilePrivate())
|
||||
{
|
||||
@@ -485,7 +482,7 @@ ASF::File::File(FileName file, bool, Properties::ReadStyle) :
|
||||
read();
|
||||
}
|
||||
|
||||
ASF::File::File(IOStream *stream, bool, Properties::ReadStyle) :
|
||||
ASF::File::File(IOStream *stream, bool, AudioProperties::ReadStyle) :
|
||||
TagLib::File(stream),
|
||||
d(new FilePrivate())
|
||||
{
|
||||
@@ -500,27 +497,12 @@ ASF::File::~File()
|
||||
|
||||
ASF::Tag *ASF::File::tag() const
|
||||
{
|
||||
return d->tag;
|
||||
return d->tag.get();
|
||||
}
|
||||
|
||||
PropertyMap ASF::File::properties() const
|
||||
ASF::AudioProperties *ASF::File::audioProperties() const
|
||||
{
|
||||
return d->tag->properties();
|
||||
}
|
||||
|
||||
void ASF::File::removeUnsupportedProperties(const StringList &properties)
|
||||
{
|
||||
d->tag->removeUnsupportedProperties(properties);
|
||||
}
|
||||
|
||||
PropertyMap ASF::File::setProperties(const PropertyMap &properties)
|
||||
{
|
||||
return d->tag->setProperties(properties);
|
||||
}
|
||||
|
||||
ASF::Properties *ASF::File::audioProperties() const
|
||||
{
|
||||
return d->properties;
|
||||
return d->properties.get();
|
||||
}
|
||||
|
||||
bool ASF::File::save()
|
||||
@@ -536,23 +518,23 @@ bool ASF::File::save()
|
||||
}
|
||||
|
||||
if(!d->contentDescriptionObject) {
|
||||
d->contentDescriptionObject = new FilePrivate::ContentDescriptionObject();
|
||||
d->contentDescriptionObject.reset(new FilePrivate::ContentDescriptionObject());
|
||||
d->objects.append(d->contentDescriptionObject);
|
||||
}
|
||||
if(!d->extendedContentDescriptionObject) {
|
||||
d->extendedContentDescriptionObject = new FilePrivate::ExtendedContentDescriptionObject();
|
||||
d->extendedContentDescriptionObject.reset(new FilePrivate::ExtendedContentDescriptionObject());
|
||||
d->objects.append(d->extendedContentDescriptionObject);
|
||||
}
|
||||
if(!d->headerExtensionObject) {
|
||||
d->headerExtensionObject = new FilePrivate::HeaderExtensionObject();
|
||||
d->headerExtensionObject.reset(new FilePrivate::HeaderExtensionObject());
|
||||
d->objects.append(d->headerExtensionObject);
|
||||
}
|
||||
if(!d->metadataObject) {
|
||||
d->metadataObject = new FilePrivate::MetadataObject();
|
||||
d->metadataObject.reset(new FilePrivate::MetadataObject());
|
||||
d->headerExtensionObject->objects.append(d->metadataObject);
|
||||
}
|
||||
if(!d->metadataLibraryObject) {
|
||||
d->metadataLibraryObject = new FilePrivate::MetadataLibraryObject();
|
||||
d->metadataLibraryObject.reset(new FilePrivate::MetadataLibraryObject());
|
||||
d->headerExtensionObject->objects.append(d->metadataLibraryObject);
|
||||
}
|
||||
|
||||
@@ -591,16 +573,16 @@ bool ASF::File::save()
|
||||
}
|
||||
|
||||
ByteVector data;
|
||||
for(List<FilePrivate::BaseObject *>::ConstIterator it = d->objects.begin(); it != d->objects.end(); ++it) {
|
||||
for(FilePrivate::ObjectConstIterator it = d->objects.begin(); it != d->objects.end(); ++it) {
|
||||
data.append((*it)->render(this));
|
||||
}
|
||||
|
||||
seek(16);
|
||||
writeBlock(ByteVector::fromLongLong(data.size() + 30, false));
|
||||
writeBlock(ByteVector::fromUInt(d->objects.size(), false));
|
||||
writeBlock(ByteVector::fromUInt64LE(data.size() + 30));
|
||||
writeBlock(ByteVector::fromUInt32LE(d->objects.size()));
|
||||
writeBlock(ByteVector("\x01\x02", 2));
|
||||
|
||||
insert(data, 30, static_cast<unsigned long>(d->headerSize - 30));
|
||||
insert(data, 30, static_cast<size_t>(d->headerSize - 30));
|
||||
|
||||
d->headerSize = data.size() + 30;
|
||||
|
||||
@@ -616,15 +598,14 @@ void ASF::File::read()
|
||||
if(!isValid())
|
||||
return;
|
||||
|
||||
ByteVector guid = readBlock(16);
|
||||
if(guid != headerGuid) {
|
||||
debug("ASF: Not an ASF file.");
|
||||
if(readBlock(16) != headerGuid) {
|
||||
debug("ASF::File::read(): Not an ASF file.");
|
||||
setValid(false);
|
||||
return;
|
||||
}
|
||||
|
||||
d->tag = new ASF::Tag();
|
||||
d->properties = new ASF::Properties();
|
||||
d->tag.reset(new ASF::Tag());
|
||||
d->properties.reset(new ASF::AudioProperties());
|
||||
|
||||
bool ok;
|
||||
d->headerSize = readQWORD(this, &ok);
|
||||
@@ -639,8 +620,10 @@ void ASF::File::read()
|
||||
}
|
||||
seek(2, Current);
|
||||
|
||||
SHARED_PTR<FilePrivate::FilePropertiesObject> filePropertiesObject;
|
||||
SHARED_PTR<FilePrivate::StreamPropertiesObject> streamPropertiesObject;
|
||||
for(int i = 0; i < numObjects; i++) {
|
||||
guid = readBlock(16);
|
||||
const ByteVector guid = readBlock(16);
|
||||
if(guid.size() != 16) {
|
||||
setValid(false);
|
||||
break;
|
||||
@@ -650,24 +633,29 @@ void ASF::File::read()
|
||||
setValid(false);
|
||||
break;
|
||||
}
|
||||
FilePrivate::BaseObject *obj;
|
||||
SHARED_PTR<FilePrivate::BaseObject> obj;
|
||||
if(guid == filePropertiesGuid) {
|
||||
obj = new FilePrivate::FilePropertiesObject();
|
||||
filePropertiesObject.reset(new FilePrivate::FilePropertiesObject());
|
||||
obj = filePropertiesObject;
|
||||
}
|
||||
else if(guid == streamPropertiesGuid) {
|
||||
obj = new FilePrivate::StreamPropertiesObject();
|
||||
streamPropertiesObject.reset(new FilePrivate::StreamPropertiesObject());
|
||||
obj = streamPropertiesObject;
|
||||
}
|
||||
else if(guid == contentDescriptionGuid) {
|
||||
obj = new FilePrivate::ContentDescriptionObject();
|
||||
d->contentDescriptionObject.reset(new FilePrivate::ContentDescriptionObject());
|
||||
obj = d->contentDescriptionObject;
|
||||
}
|
||||
else if(guid == extendedContentDescriptionGuid) {
|
||||
obj = new FilePrivate::ExtendedContentDescriptionObject();
|
||||
d->extendedContentDescriptionObject.reset(new FilePrivate::ExtendedContentDescriptionObject());
|
||||
obj = d->extendedContentDescriptionObject;
|
||||
}
|
||||
else if(guid == headerExtensionGuid) {
|
||||
obj = new FilePrivate::HeaderExtensionObject();
|
||||
d->headerExtensionObject.reset(new FilePrivate::HeaderExtensionObject());
|
||||
obj = d->headerExtensionObject;
|
||||
}
|
||||
else if(guid == codecListGuid) {
|
||||
obj = new FilePrivate::CodecListObject();
|
||||
obj.reset(new FilePrivate::CodecListObject());
|
||||
}
|
||||
else {
|
||||
if(guid == contentEncryptionGuid ||
|
||||
@@ -675,9 +663,15 @@ void ASF::File::read()
|
||||
guid == advancedContentEncryptionGuid) {
|
||||
d->properties->setEncrypted(true);
|
||||
}
|
||||
obj = new FilePrivate::UnknownObject(guid);
|
||||
obj.reset(new FilePrivate::UnknownObject(guid));
|
||||
}
|
||||
obj->parse(this, size);
|
||||
d->objects.append(obj);
|
||||
}
|
||||
|
||||
if(!filePropertiesObject || !streamPropertiesObject) {
|
||||
debug("ASF::File::read(): Missing mandatory header objects.");
|
||||
setValid(false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,17 +48,21 @@ namespace TagLib {
|
||||
public:
|
||||
|
||||
/*!
|
||||
* Constructs an ASF file from \a file.
|
||||
* Contructs an ASF file from \a file. If \a readProperties is true the
|
||||
* file's audio properties will also be read using \a propertiesStyle. If
|
||||
* false, \a propertiesStyle is ignored.
|
||||
*
|
||||
* \note In the current implementation, both \a readProperties and
|
||||
* \a propertiesStyle are ignored. The audio properties are always
|
||||
* read.
|
||||
*/
|
||||
File(FileName file, bool readProperties = true,
|
||||
Properties::ReadStyle propertiesStyle = Properties::Average);
|
||||
AudioProperties::ReadStyle propertiesStyle = AudioProperties::Average);
|
||||
|
||||
/*!
|
||||
* Constructs an ASF file from \a stream.
|
||||
* Contructs an ASF file from \a file. If \a readProperties is true the
|
||||
* file's audio properties will also be read using \a propertiesStyle. If
|
||||
* false, \a propertiesStyle is ignored.
|
||||
*
|
||||
* \note In the current implementation, both \a readProperties and
|
||||
* \a propertiesStyle are ignored. The audio properties are always
|
||||
@@ -68,7 +72,7 @@ namespace TagLib {
|
||||
* responsible for deleting it after the File object.
|
||||
*/
|
||||
File(IOStream *stream, bool readProperties = true,
|
||||
Properties::ReadStyle propertiesStyle = Properties::Average);
|
||||
AudioProperties::ReadStyle propertiesStyle = AudioProperties::Average);
|
||||
|
||||
/*!
|
||||
* Destroys this instance of the File.
|
||||
@@ -87,26 +91,10 @@ namespace TagLib {
|
||||
*/
|
||||
virtual Tag *tag() const;
|
||||
|
||||
/*!
|
||||
* Implements the unified property interface -- export function.
|
||||
*/
|
||||
PropertyMap properties() const;
|
||||
|
||||
/*!
|
||||
* Removes unsupported properties. Forwards to the actual Tag's
|
||||
* removeUnsupportedProperties() function.
|
||||
*/
|
||||
void removeUnsupportedProperties(const StringList &properties);
|
||||
|
||||
/*!
|
||||
* Implements the unified property interface -- import function.
|
||||
*/
|
||||
PropertyMap setProperties(const PropertyMap &);
|
||||
|
||||
/*!
|
||||
* Returns the ASF audio properties for this file.
|
||||
*/
|
||||
virtual Properties *audioProperties() const;
|
||||
virtual AudioProperties *audioProperties() const;
|
||||
|
||||
/*!
|
||||
* Save the file.
|
||||
@@ -115,6 +103,15 @@ namespace TagLib {
|
||||
*/
|
||||
virtual bool save();
|
||||
|
||||
/*!
|
||||
* Returns whether or not the given \a stream can be opened as an ASF
|
||||
* file.
|
||||
*
|
||||
* \note This method is designed to do a quick check. The result may
|
||||
* not necessarily be correct.
|
||||
*/
|
||||
static bool isSupported(IOStream *stream);
|
||||
|
||||
private:
|
||||
void read();
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
|
||||
#include <taglib.h>
|
||||
#include <tdebug.h>
|
||||
#include <trefcounter.h>
|
||||
#include <tsmartptr.h>
|
||||
|
||||
#include "asfattribute.h"
|
||||
#include "asffile.h"
|
||||
@@ -34,14 +34,25 @@
|
||||
|
||||
using namespace TagLib;
|
||||
|
||||
class ASF::Picture::PicturePrivate : public RefCounter
|
||||
namespace
|
||||
{
|
||||
struct PictureData
|
||||
{
|
||||
bool valid;
|
||||
ASF::Picture::Type type;
|
||||
String mimeType;
|
||||
String description;
|
||||
ByteVector picture;
|
||||
};
|
||||
}
|
||||
|
||||
class ASF::Picture::PicturePrivate
|
||||
{
|
||||
public:
|
||||
bool valid;
|
||||
Type type;
|
||||
String mimeType;
|
||||
String description;
|
||||
ByteVector picture;
|
||||
PicturePrivate() :
|
||||
data(new PictureData()) {}
|
||||
|
||||
SHARED_PTR<PictureData> data;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -51,71 +62,69 @@ public:
|
||||
ASF::Picture::Picture() :
|
||||
d(new PicturePrivate())
|
||||
{
|
||||
d->valid = true;
|
||||
d->data->valid = true;
|
||||
}
|
||||
|
||||
ASF::Picture::Picture(const Picture& other) :
|
||||
d(other.d)
|
||||
d(new PicturePrivate(*other.d))
|
||||
{
|
||||
d->ref();
|
||||
}
|
||||
|
||||
ASF::Picture::~Picture()
|
||||
{
|
||||
if(d->deref())
|
||||
delete d;
|
||||
delete d;
|
||||
}
|
||||
|
||||
bool ASF::Picture::isValid() const
|
||||
{
|
||||
return d->valid;
|
||||
return d->data->valid;
|
||||
}
|
||||
|
||||
String ASF::Picture::mimeType() const
|
||||
{
|
||||
return d->mimeType;
|
||||
return d->data->mimeType;
|
||||
}
|
||||
|
||||
void ASF::Picture::setMimeType(const String &value)
|
||||
{
|
||||
d->mimeType = value;
|
||||
d->data->mimeType = value;
|
||||
}
|
||||
|
||||
ASF::Picture::Type ASF::Picture::type() const
|
||||
{
|
||||
return d->type;
|
||||
return d->data->type;
|
||||
}
|
||||
|
||||
void ASF::Picture::setType(const ASF::Picture::Type& t)
|
||||
{
|
||||
d->type = t;
|
||||
d->data->type = t;
|
||||
}
|
||||
|
||||
String ASF::Picture::description() const
|
||||
{
|
||||
return d->description;
|
||||
return d->data->description;
|
||||
}
|
||||
|
||||
void ASF::Picture::setDescription(const String &desc)
|
||||
{
|
||||
d->description = desc;
|
||||
d->data->description = desc;
|
||||
}
|
||||
|
||||
ByteVector ASF::Picture::picture() const
|
||||
{
|
||||
return d->picture;
|
||||
return d->data->picture;
|
||||
}
|
||||
|
||||
void ASF::Picture::setPicture(const ByteVector &p)
|
||||
{
|
||||
d->picture = p;
|
||||
d->data->picture = p;
|
||||
}
|
||||
|
||||
int ASF::Picture::dataSize() const
|
||||
{
|
||||
return
|
||||
9 + (d->mimeType.length() + d->description.length()) * 2 +
|
||||
d->picture.size();
|
||||
return static_cast<int>(
|
||||
9 + (d->data->mimeType.length() + d->data->description.length()) * 2 +
|
||||
d->data->picture.size());
|
||||
}
|
||||
|
||||
ASF::Picture& ASF::Picture::operator=(const ASF::Picture& other)
|
||||
@@ -137,47 +146,47 @@ ByteVector ASF::Picture::render() const
|
||||
return ByteVector();
|
||||
|
||||
return
|
||||
ByteVector((char)d->type) +
|
||||
ByteVector::fromUInt(d->picture.size(), false) +
|
||||
renderString(d->mimeType) +
|
||||
renderString(d->description) +
|
||||
d->picture;
|
||||
ByteVector((char)d->data->type) +
|
||||
ByteVector::fromUInt32LE(d->data->picture.size()) +
|
||||
renderString(d->data->mimeType) +
|
||||
renderString(d->data->description) +
|
||||
d->data->picture;
|
||||
}
|
||||
|
||||
void ASF::Picture::parse(const ByteVector& bytes)
|
||||
{
|
||||
d->valid = false;
|
||||
d->data->valid = false;
|
||||
if(bytes.size() < 9)
|
||||
return;
|
||||
int pos = 0;
|
||||
d->type = (Type)bytes[0]; ++pos;
|
||||
const unsigned int dataLen = bytes.toUInt(pos, false); pos+=4;
|
||||
size_t pos = 0;
|
||||
d->data->type = (Type)bytes[0]; ++pos;
|
||||
const unsigned int dataLen = bytes.toUInt32LE(pos); pos+=4;
|
||||
|
||||
const ByteVector nullStringTerminator(2, 0);
|
||||
|
||||
int endPos = bytes.find(nullStringTerminator, pos, 2);
|
||||
if(endPos < 0)
|
||||
size_t endPos = bytes.find(nullStringTerminator, pos, 2);
|
||||
if(endPos == ByteVector::npos())
|
||||
return;
|
||||
d->mimeType = String(bytes.mid(pos, endPos - pos), String::UTF16LE);
|
||||
d->data->mimeType = String(bytes.mid(pos, endPos - pos), String::UTF16LE);
|
||||
pos = endPos+2;
|
||||
|
||||
endPos = bytes.find(nullStringTerminator, pos, 2);
|
||||
if(endPos < 0)
|
||||
if(endPos == ByteVector::npos())
|
||||
return;
|
||||
d->description = String(bytes.mid(pos, endPos - pos), String::UTF16LE);
|
||||
d->data->description = String(bytes.mid(pos, endPos - pos), String::UTF16LE);
|
||||
pos = endPos+2;
|
||||
|
||||
if(dataLen + pos != bytes.size())
|
||||
return;
|
||||
|
||||
d->picture = bytes.mid(pos, dataLen);
|
||||
d->valid = true;
|
||||
d->data->picture = bytes.mid(pos, dataLen);
|
||||
d->data->valid = true;
|
||||
return;
|
||||
}
|
||||
|
||||
ASF::Picture ASF::Picture::fromInvalid()
|
||||
{
|
||||
Picture ret;
|
||||
ret.d->valid = false;
|
||||
ret.d->data->valid = false;
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -35,6 +35,7 @@ namespace TagLib
|
||||
{
|
||||
namespace ASF
|
||||
{
|
||||
class Attribute;
|
||||
|
||||
//! An ASF attached picture interface implementation
|
||||
|
||||
@@ -46,7 +47,8 @@ namespace TagLib
|
||||
* \see Attribute::toPicture()
|
||||
* \see Attribute::Attribute(const Picture& picture)
|
||||
*/
|
||||
class TAGLIB_EXPORT Picture {
|
||||
class TAGLIB_EXPORT Picture
|
||||
{
|
||||
public:
|
||||
|
||||
/*!
|
||||
@@ -103,7 +105,7 @@ namespace TagLib
|
||||
Picture();
|
||||
|
||||
/*!
|
||||
* Construct an picture as a copy of \a other.
|
||||
* Constructs an picture as a copy of \a other.
|
||||
*/
|
||||
Picture(const Picture& other);
|
||||
|
||||
@@ -206,16 +208,15 @@ namespace TagLib
|
||||
*/
|
||||
int dataSize() const;
|
||||
|
||||
#ifndef DO_NOT_DOCUMENT
|
||||
/* THIS IS PRIVATE, DON'T TOUCH IT! */
|
||||
void parse(const ByteVector& );
|
||||
static Picture fromInvalid();
|
||||
#endif
|
||||
private:
|
||||
friend class Attribute;
|
||||
|
||||
private:
|
||||
class PicturePrivate;
|
||||
PicturePrivate *d;
|
||||
};
|
||||
void parse(const ByteVector &);
|
||||
static Picture fromInvalid();
|
||||
|
||||
class PicturePrivate;
|
||||
PicturePrivate *d;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
|
||||
using namespace TagLib;
|
||||
|
||||
class ASF::Properties::PropertiesPrivate
|
||||
class ASF::AudioProperties::PropertiesPrivate
|
||||
{
|
||||
public:
|
||||
PropertiesPrivate() :
|
||||
@@ -38,7 +38,7 @@ public:
|
||||
sampleRate(0),
|
||||
channels(0),
|
||||
bitsPerSample(0),
|
||||
codec(ASF::Properties::Unknown),
|
||||
codec(ASF::AudioProperties::Unknown),
|
||||
encrypted(false) {}
|
||||
|
||||
int length;
|
||||
@@ -46,7 +46,7 @@ public:
|
||||
int sampleRate;
|
||||
int channels;
|
||||
int bitsPerSample;
|
||||
ASF::Properties::Codec codec;
|
||||
ASF::AudioProperties::Codec codec;
|
||||
String codecName;
|
||||
String codecDescription;
|
||||
bool encrypted;
|
||||
@@ -56,68 +56,68 @@ public:
|
||||
// public members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
ASF::Properties::Properties() :
|
||||
AudioProperties(AudioProperties::Average),
|
||||
ASF::AudioProperties::AudioProperties() :
|
||||
TagLib::AudioProperties(),
|
||||
d(new PropertiesPrivate())
|
||||
{
|
||||
}
|
||||
|
||||
ASF::Properties::~Properties()
|
||||
ASF::AudioProperties::~AudioProperties()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
|
||||
int ASF::Properties::length() const
|
||||
int ASF::AudioProperties::length() const
|
||||
{
|
||||
return lengthInSeconds();
|
||||
}
|
||||
|
||||
int ASF::Properties::lengthInSeconds() const
|
||||
int ASF::AudioProperties::lengthInSeconds() const
|
||||
{
|
||||
return d->length / 1000;
|
||||
}
|
||||
|
||||
int ASF::Properties::lengthInMilliseconds() const
|
||||
int ASF::AudioProperties::lengthInMilliseconds() const
|
||||
{
|
||||
return d->length;
|
||||
}
|
||||
|
||||
int ASF::Properties::bitrate() const
|
||||
int ASF::AudioProperties::bitrate() const
|
||||
{
|
||||
return d->bitrate;
|
||||
}
|
||||
|
||||
int ASF::Properties::sampleRate() const
|
||||
int ASF::AudioProperties::sampleRate() const
|
||||
{
|
||||
return d->sampleRate;
|
||||
}
|
||||
|
||||
int ASF::Properties::channels() const
|
||||
int ASF::AudioProperties::channels() const
|
||||
{
|
||||
return d->channels;
|
||||
}
|
||||
|
||||
int ASF::Properties::bitsPerSample() const
|
||||
int ASF::AudioProperties::bitsPerSample() const
|
||||
{
|
||||
return d->bitsPerSample;
|
||||
}
|
||||
|
||||
ASF::Properties::Codec ASF::Properties::codec() const
|
||||
ASF::AudioProperties::Codec ASF::AudioProperties::codec() const
|
||||
{
|
||||
return d->codec;
|
||||
}
|
||||
|
||||
String ASF::Properties::codecName() const
|
||||
String ASF::AudioProperties::codecName() const
|
||||
{
|
||||
return d->codecName;
|
||||
}
|
||||
|
||||
String ASF::Properties::codecDescription() const
|
||||
String ASF::AudioProperties::codecDescription() const
|
||||
{
|
||||
return d->codecDescription;
|
||||
}
|
||||
|
||||
bool ASF::Properties::isEncrypted() const
|
||||
bool ASF::AudioProperties::isEncrypted() const
|
||||
{
|
||||
return d->encrypted;
|
||||
}
|
||||
@@ -126,37 +126,32 @@ bool ASF::Properties::isEncrypted() const
|
||||
// private members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void ASF::Properties::setLength(int /*length*/)
|
||||
{
|
||||
debug("ASF::Properties::setLength() -- This method is deprecated. Do not use.");
|
||||
}
|
||||
|
||||
void ASF::Properties::setLengthInMilliseconds(int value)
|
||||
void ASF::AudioProperties::setLengthInMilliseconds(int value)
|
||||
{
|
||||
d->length = value;
|
||||
}
|
||||
|
||||
void ASF::Properties::setBitrate(int value)
|
||||
void ASF::AudioProperties::setBitrate(int value)
|
||||
{
|
||||
d->bitrate = value;
|
||||
}
|
||||
|
||||
void ASF::Properties::setSampleRate(int value)
|
||||
void ASF::AudioProperties::setSampleRate(int value)
|
||||
{
|
||||
d->sampleRate = value;
|
||||
}
|
||||
|
||||
void ASF::Properties::setChannels(int value)
|
||||
void ASF::AudioProperties::setChannels(int value)
|
||||
{
|
||||
d->channels = value;
|
||||
}
|
||||
|
||||
void ASF::Properties::setBitsPerSample(int value)
|
||||
void ASF::AudioProperties::setBitsPerSample(int value)
|
||||
{
|
||||
d->bitsPerSample = value;
|
||||
}
|
||||
|
||||
void ASF::Properties::setCodec(int value)
|
||||
void ASF::AudioProperties::setCodec(int value)
|
||||
{
|
||||
switch(value)
|
||||
{
|
||||
@@ -178,17 +173,17 @@ void ASF::Properties::setCodec(int value)
|
||||
}
|
||||
}
|
||||
|
||||
void ASF::Properties::setCodecName(const String &value)
|
||||
void ASF::AudioProperties::setCodecName(const String &value)
|
||||
{
|
||||
d->codecName = value;
|
||||
}
|
||||
|
||||
void ASF::Properties::setCodecDescription(const String &value)
|
||||
void ASF::AudioProperties::setCodecDescription(const String &value)
|
||||
{
|
||||
d->codecDescription = value;
|
||||
}
|
||||
|
||||
void ASF::Properties::setEncrypted(bool value)
|
||||
void ASF::AudioProperties::setEncrypted(bool value)
|
||||
{
|
||||
d->encrypted = value;
|
||||
}
|
||||
|
||||
@@ -34,11 +34,14 @@ namespace TagLib {
|
||||
|
||||
namespace ASF {
|
||||
|
||||
//! An implementation of ASF audio properties
|
||||
class TAGLIB_EXPORT Properties : public AudioProperties
|
||||
{
|
||||
public:
|
||||
class File;
|
||||
|
||||
//! An implementation of ASF audio properties
|
||||
class TAGLIB_EXPORT AudioProperties : public TagLib::AudioProperties
|
||||
{
|
||||
friend class File;
|
||||
|
||||
public:
|
||||
/*!
|
||||
* Audio codec types can be used in ASF file.
|
||||
*/
|
||||
@@ -71,14 +74,14 @@ namespace TagLib {
|
||||
};
|
||||
|
||||
/*!
|
||||
* Creates an instance of ASF::Properties.
|
||||
* Creates an instance of ASF::AudioProperties.
|
||||
*/
|
||||
Properties();
|
||||
AudioProperties();
|
||||
|
||||
/*!
|
||||
* Destroys this ASF::Properties instance.
|
||||
* Destroys this ASF::AudioProperties instance.
|
||||
*/
|
||||
virtual ~Properties();
|
||||
virtual ~AudioProperties();
|
||||
|
||||
/*!
|
||||
* Returns the length of the file in seconds. The length is rounded down to
|
||||
@@ -96,16 +99,14 @@ namespace TagLib {
|
||||
*
|
||||
* \see lengthInMilliseconds()
|
||||
*/
|
||||
// BIC: make virtual
|
||||
int lengthInSeconds() const;
|
||||
virtual int lengthInSeconds() const;
|
||||
|
||||
/*!
|
||||
* Returns the length of the file in milliseconds.
|
||||
*
|
||||
* \see lengthInSeconds()
|
||||
*/
|
||||
// BIC: make virtual
|
||||
int lengthInMilliseconds() const;
|
||||
virtual int lengthInMilliseconds() const;
|
||||
|
||||
/*!
|
||||
* Returns the average bit rate of the file in kb/s.
|
||||
@@ -159,10 +160,7 @@ namespace TagLib {
|
||||
*/
|
||||
bool isEncrypted() const;
|
||||
|
||||
#ifndef DO_NOT_DOCUMENT
|
||||
// deprecated
|
||||
void setLength(int value);
|
||||
|
||||
private:
|
||||
void setLengthInMilliseconds(int value);
|
||||
void setBitrate(int value);
|
||||
void setSampleRate(int value);
|
||||
@@ -172,9 +170,7 @@ namespace TagLib {
|
||||
void setCodecName(const String &value);
|
||||
void setCodecDescription(const String &value);
|
||||
void setEncrypted(bool value);
|
||||
#endif
|
||||
|
||||
private:
|
||||
class PropertiesPrivate;
|
||||
PropertiesPrivate *d;
|
||||
};
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#include <tpicturemap.h>
|
||||
#include <tpropertymap.h>
|
||||
#include "asftag.h"
|
||||
|
||||
@@ -39,10 +40,10 @@ public:
|
||||
AttributeListMap attributeListMap;
|
||||
};
|
||||
|
||||
ASF::Tag::Tag()
|
||||
: TagLib::Tag()
|
||||
ASF::Tag::Tag() :
|
||||
TagLib::Tag(),
|
||||
d(new TagPrivate())
|
||||
{
|
||||
d = new TagPrivate;
|
||||
}
|
||||
|
||||
ASF::Tag::~Tag()
|
||||
@@ -110,6 +111,91 @@ String ASF::Tag::genre() const
|
||||
return String();
|
||||
}
|
||||
|
||||
PictureMap ASF::Tag::pictures() const
|
||||
{
|
||||
PictureMap map;
|
||||
if(d->attributeListMap.contains("WM/Picture")) {
|
||||
AttributeList list = d->attributeListMap["WM/Picture"];
|
||||
for(AttributeList::ConstIterator it = list.begin();
|
||||
it != list.end();
|
||||
++it) {
|
||||
ASF::Picture asfPicture = (*it).toPicture();
|
||||
TagLib::Picture::Type type;
|
||||
switch(asfPicture.type()) {
|
||||
case ASF::Picture::FileIcon:
|
||||
type = TagLib::Picture::FileIcon;
|
||||
break;
|
||||
case ASF::Picture::OtherFileIcon:
|
||||
type = TagLib::Picture::OtherFileIcon;
|
||||
break;
|
||||
case ASF::Picture::FrontCover:
|
||||
type = TagLib::Picture::FrontCover;
|
||||
break;
|
||||
case ASF::Picture::BackCover:
|
||||
type = TagLib::Picture::BackCover;
|
||||
break;
|
||||
case ASF::Picture::LeafletPage:
|
||||
type = TagLib::Picture::LeafletPage;
|
||||
break;
|
||||
case ASF::Picture::Media:
|
||||
type = TagLib::Picture::Media;
|
||||
break;
|
||||
case ASF::Picture::LeadArtist:
|
||||
type = TagLib::Picture::LeadArtist;
|
||||
break;
|
||||
case ASF::Picture::Artist:
|
||||
type = TagLib::Picture::Artist;
|
||||
break;
|
||||
case ASF::Picture::Conductor:
|
||||
type = TagLib::Picture::Conductor;
|
||||
break;
|
||||
case ASF::Picture::Band:
|
||||
type = TagLib::Picture::Band;
|
||||
break;
|
||||
case ASF::Picture::Composer:
|
||||
type = TagLib::Picture::Composer;
|
||||
break;
|
||||
case ASF::Picture::Lyricist:
|
||||
type = TagLib::Picture::Lyricist;
|
||||
break;
|
||||
case ASF::Picture::RecordingLocation:
|
||||
type = TagLib::Picture::RecordingLocation;
|
||||
break;
|
||||
case ASF::Picture::DuringRecording:
|
||||
type = TagLib::Picture::DuringRecording;
|
||||
break;
|
||||
case ASF::Picture::DuringPerformance:
|
||||
type = TagLib::Picture::DuringPerformance;
|
||||
break;
|
||||
case ASF::Picture::MovieScreenCapture:
|
||||
type = TagLib::Picture::MovieScreenCapture;
|
||||
break;
|
||||
case ASF::Picture::ColouredFish:
|
||||
type = TagLib::Picture::ColouredFish;
|
||||
break;
|
||||
case ASF::Picture::Illustration:
|
||||
type = TagLib::Picture::Illustration;
|
||||
break;
|
||||
case ASF::Picture::BandLogo:
|
||||
type = TagLib::Picture::BandLogo;
|
||||
break;
|
||||
case ASF::Picture::PublisherLogo:
|
||||
type = TagLib::Picture::PublisherLogo;
|
||||
break;
|
||||
default:
|
||||
type = TagLib::Picture::Other;
|
||||
break;
|
||||
}
|
||||
TagLib::Picture picture(asfPicture.picture(),
|
||||
type,
|
||||
asfPicture.mimeType(),
|
||||
asfPicture.description());
|
||||
map.insert(picture);
|
||||
}
|
||||
}
|
||||
return PictureMap(map);
|
||||
}
|
||||
|
||||
void ASF::Tag::setTitle(const String &value)
|
||||
{
|
||||
d->title = value;
|
||||
@@ -155,6 +241,94 @@ void ASF::Tag::setTrack(unsigned int value)
|
||||
setAttribute("WM/TrackNumber", String::number(value));
|
||||
}
|
||||
|
||||
void ASF::Tag::setPictures(const PictureMap &l)
|
||||
{
|
||||
removeItem("WM/Picture");
|
||||
for(PictureMap::ConstIterator pictureMapIt = l.begin();
|
||||
pictureMapIt != l.end();
|
||||
++pictureMapIt)
|
||||
{
|
||||
PictureList list = pictureMapIt->second;
|
||||
for( PictureList::ConstIterator pictureListIt = list.begin();
|
||||
pictureListIt != list.end();
|
||||
++pictureListIt)
|
||||
{
|
||||
const TagLib::Picture picture = (*pictureListIt);
|
||||
ASF::Picture asfPicture;
|
||||
asfPicture.setPicture(picture.data());
|
||||
asfPicture.setMimeType(picture.mime());
|
||||
asfPicture.setDescription(picture.description());
|
||||
switch (picture.type()) {
|
||||
case TagLib::Picture::Other:
|
||||
asfPicture.setType(ASF::Picture::Other);
|
||||
break;
|
||||
case TagLib::Picture::FileIcon:
|
||||
asfPicture.setType(ASF::Picture::FileIcon);
|
||||
break;
|
||||
case TagLib::Picture::OtherFileIcon:
|
||||
asfPicture.setType(ASF::Picture::OtherFileIcon);
|
||||
break;
|
||||
case TagLib::Picture::FrontCover:
|
||||
asfPicture.setType(ASF::Picture::FrontCover);
|
||||
break;
|
||||
case TagLib::Picture::BackCover:
|
||||
asfPicture.setType(ASF::Picture::BackCover);
|
||||
break;
|
||||
case TagLib::Picture::LeafletPage:
|
||||
asfPicture.setType(ASF::Picture::LeafletPage);
|
||||
break;
|
||||
case TagLib::Picture::Media:
|
||||
asfPicture.setType(ASF::Picture::Media);
|
||||
break;
|
||||
case TagLib::Picture::LeadArtist:
|
||||
asfPicture.setType(ASF::Picture::LeadArtist);
|
||||
break;
|
||||
case TagLib::Picture::Artist:
|
||||
asfPicture.setType(ASF::Picture::Artist);
|
||||
break;
|
||||
case TagLib::Picture::Conductor:
|
||||
asfPicture.setType(ASF::Picture::Conductor);
|
||||
break;
|
||||
case TagLib::Picture::Band:
|
||||
asfPicture.setType(ASF::Picture::Band);
|
||||
break;
|
||||
case TagLib::Picture::Composer:
|
||||
asfPicture.setType(ASF::Picture::Composer);
|
||||
break;
|
||||
case TagLib::Picture::Lyricist:
|
||||
asfPicture.setType(ASF::Picture::Lyricist);
|
||||
break;
|
||||
case TagLib::Picture::RecordingLocation:
|
||||
asfPicture.setType(ASF::Picture::RecordingLocation);
|
||||
break;
|
||||
case TagLib::Picture::DuringRecording:
|
||||
asfPicture.setType(ASF::Picture::DuringRecording);
|
||||
break;
|
||||
case TagLib::Picture::DuringPerformance:
|
||||
asfPicture.setType(ASF::Picture::DuringPerformance);
|
||||
break;
|
||||
case TagLib::Picture::MovieScreenCapture:
|
||||
asfPicture.setType(ASF::Picture::MovieScreenCapture);
|
||||
break;
|
||||
case TagLib::Picture::ColouredFish:
|
||||
asfPicture.setType(ASF::Picture::ColouredFish);
|
||||
break;
|
||||
case TagLib::Picture::Illustration:
|
||||
asfPicture.setType(ASF::Picture::Illustration);
|
||||
break;
|
||||
case TagLib::Picture::BandLogo:
|
||||
asfPicture.setType(ASF::Picture::BandLogo);
|
||||
break;
|
||||
case TagLib::Picture::PublisherLogo:
|
||||
asfPicture.setType(ASF::Picture::PublisherLogo);
|
||||
break;
|
||||
}
|
||||
addAttribute("WM/Picture", Attribute(asfPicture));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
ASF::AttributeListMap& ASF::Tag::attributeListMap()
|
||||
{
|
||||
return d->attributeListMap;
|
||||
|
||||
@@ -98,6 +98,8 @@ namespace TagLib {
|
||||
*/
|
||||
virtual unsigned int track() const;
|
||||
|
||||
virtual PictureMap pictures() const;
|
||||
|
||||
/*!
|
||||
* Sets the title to \a s.
|
||||
*/
|
||||
@@ -144,6 +146,8 @@ namespace TagLib {
|
||||
*/
|
||||
virtual void setTrack(unsigned int i);
|
||||
|
||||
virtual void setPictures(const PictureMap &l);
|
||||
|
||||
/*!
|
||||
* Returns true if the tag does not contain any data. This should be
|
||||
* reimplemented in subclasses that provide more than the basic tagging
|
||||
|
||||
@@ -45,7 +45,7 @@ namespace TagLib
|
||||
return 0;
|
||||
}
|
||||
if(ok) *ok = true;
|
||||
return v.toUShort(false);
|
||||
return v.toUInt16LE(0);
|
||||
}
|
||||
|
||||
inline unsigned int readDWORD(File *file, bool *ok = 0)
|
||||
@@ -56,7 +56,7 @@ namespace TagLib
|
||||
return 0;
|
||||
}
|
||||
if(ok) *ok = true;
|
||||
return v.toUInt(false);
|
||||
return v.toUInt32LE(0);
|
||||
}
|
||||
|
||||
inline long long readQWORD(File *file, bool *ok = 0)
|
||||
@@ -67,13 +67,13 @@ namespace TagLib
|
||||
return 0;
|
||||
}
|
||||
if(ok) *ok = true;
|
||||
return v.toLongLong(false);
|
||||
return v.toInt64LE(0);
|
||||
}
|
||||
|
||||
inline String readString(File *file, int length)
|
||||
{
|
||||
ByteVector data = file->readBlock(length);
|
||||
unsigned int size = data.size();
|
||||
size_t size = data.size();
|
||||
while (size >= 2) {
|
||||
if(data[size - 1] != '\0' || data[size - 2] != '\0') {
|
||||
break;
|
||||
@@ -88,9 +88,9 @@ namespace TagLib
|
||||
|
||||
inline ByteVector renderString(const String &str, bool includeLength = false)
|
||||
{
|
||||
ByteVector data = str.data(String::UTF16LE) + ByteVector::fromShort(0, false);
|
||||
ByteVector data = str.data(String::UTF16LE) + ByteVector::fromUInt16LE(0);
|
||||
if(includeLength) {
|
||||
data = ByteVector::fromShort(data.size(), false) + data;
|
||||
data = ByteVector::fromUInt16LE(data.size()) + data;
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
@@ -23,142 +23,34 @@
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#include <tbytevector.h>
|
||||
|
||||
#include "aiffproperties.h"
|
||||
#include "apeproperties.h"
|
||||
#include "asfproperties.h"
|
||||
#include "flacproperties.h"
|
||||
#include "mp4properties.h"
|
||||
#include "mpcproperties.h"
|
||||
#include "mpegproperties.h"
|
||||
#include "opusproperties.h"
|
||||
#include "speexproperties.h"
|
||||
#include "trueaudioproperties.h"
|
||||
#include "vorbisproperties.h"
|
||||
#include "wavproperties.h"
|
||||
#include "wavpackproperties.h"
|
||||
#include <tstringlist.h>
|
||||
|
||||
#include "audioproperties.h"
|
||||
|
||||
using namespace TagLib;
|
||||
|
||||
class AudioProperties::AudioPropertiesPrivate
|
||||
{
|
||||
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// public methods
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
AudioProperties::~AudioProperties()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
int AudioProperties::lengthInSeconds() const
|
||||
String AudioProperties::toString() const
|
||||
{
|
||||
// This is an ugly workaround but we can't add a virtual function.
|
||||
// Should be virtual in taglib2.
|
||||
|
||||
if(dynamic_cast<const APE::Properties*>(this))
|
||||
return dynamic_cast<const APE::Properties*>(this)->lengthInSeconds();
|
||||
|
||||
else if(dynamic_cast<const ASF::Properties*>(this))
|
||||
return dynamic_cast<const ASF::Properties*>(this)->lengthInSeconds();
|
||||
|
||||
else if(dynamic_cast<const FLAC::Properties*>(this))
|
||||
return dynamic_cast<const FLAC::Properties*>(this)->lengthInSeconds();
|
||||
|
||||
else if(dynamic_cast<const MP4::Properties*>(this))
|
||||
return dynamic_cast<const MP4::Properties*>(this)->lengthInSeconds();
|
||||
|
||||
else if(dynamic_cast<const MPC::Properties*>(this))
|
||||
return dynamic_cast<const MPC::Properties*>(this)->lengthInSeconds();
|
||||
|
||||
else if(dynamic_cast<const MPEG::Properties*>(this))
|
||||
return dynamic_cast<const MPEG::Properties*>(this)->lengthInSeconds();
|
||||
|
||||
else if(dynamic_cast<const Ogg::Opus::Properties*>(this))
|
||||
return dynamic_cast<const Ogg::Opus::Properties*>(this)->lengthInSeconds();
|
||||
|
||||
else if(dynamic_cast<const Ogg::Speex::Properties*>(this))
|
||||
return dynamic_cast<const Ogg::Speex::Properties*>(this)->lengthInSeconds();
|
||||
|
||||
else if(dynamic_cast<const TrueAudio::Properties*>(this))
|
||||
return dynamic_cast<const TrueAudio::Properties*>(this)->lengthInSeconds();
|
||||
|
||||
else if (dynamic_cast<const RIFF::AIFF::Properties*>(this))
|
||||
return dynamic_cast<const RIFF::AIFF::Properties*>(this)->lengthInSeconds();
|
||||
|
||||
else if(dynamic_cast<const RIFF::WAV::Properties*>(this))
|
||||
return dynamic_cast<const RIFF::WAV::Properties*>(this)->lengthInSeconds();
|
||||
|
||||
else if(dynamic_cast<const Vorbis::Properties*>(this))
|
||||
return dynamic_cast<const Vorbis::Properties*>(this)->lengthInSeconds();
|
||||
|
||||
else if(dynamic_cast<const WavPack::Properties*>(this))
|
||||
return dynamic_cast<const WavPack::Properties*>(this)->lengthInSeconds();
|
||||
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
int AudioProperties::lengthInMilliseconds() const
|
||||
{
|
||||
// This is an ugly workaround but we can't add a virtual function.
|
||||
// Should be virtual in taglib2.
|
||||
|
||||
if(dynamic_cast<const APE::Properties*>(this))
|
||||
return dynamic_cast<const APE::Properties*>(this)->lengthInMilliseconds();
|
||||
|
||||
else if(dynamic_cast<const ASF::Properties*>(this))
|
||||
return dynamic_cast<const ASF::Properties*>(this)->lengthInMilliseconds();
|
||||
|
||||
else if(dynamic_cast<const FLAC::Properties*>(this))
|
||||
return dynamic_cast<const FLAC::Properties*>(this)->lengthInMilliseconds();
|
||||
|
||||
else if(dynamic_cast<const MP4::Properties*>(this))
|
||||
return dynamic_cast<const MP4::Properties*>(this)->lengthInMilliseconds();
|
||||
|
||||
else if(dynamic_cast<const MPC::Properties*>(this))
|
||||
return dynamic_cast<const MPC::Properties*>(this)->lengthInMilliseconds();
|
||||
|
||||
else if(dynamic_cast<const MPEG::Properties*>(this))
|
||||
return dynamic_cast<const MPEG::Properties*>(this)->lengthInMilliseconds();
|
||||
|
||||
else if(dynamic_cast<const Ogg::Opus::Properties*>(this))
|
||||
return dynamic_cast<const Ogg::Opus::Properties*>(this)->lengthInMilliseconds();
|
||||
|
||||
else if(dynamic_cast<const Ogg::Speex::Properties*>(this))
|
||||
return dynamic_cast<const Ogg::Speex::Properties*>(this)->lengthInMilliseconds();
|
||||
|
||||
else if(dynamic_cast<const TrueAudio::Properties*>(this))
|
||||
return dynamic_cast<const TrueAudio::Properties*>(this)->lengthInMilliseconds();
|
||||
|
||||
else if(dynamic_cast<const RIFF::AIFF::Properties*>(this))
|
||||
return dynamic_cast<const RIFF::AIFF::Properties*>(this)->lengthInMilliseconds();
|
||||
|
||||
else if(dynamic_cast<const RIFF::WAV::Properties*>(this))
|
||||
return dynamic_cast<const RIFF::WAV::Properties*>(this)->lengthInMilliseconds();
|
||||
|
||||
else if(dynamic_cast<const Vorbis::Properties*>(this))
|
||||
return dynamic_cast<const Vorbis::Properties*>(this)->lengthInMilliseconds();
|
||||
|
||||
else if(dynamic_cast<const WavPack::Properties*>(this))
|
||||
return dynamic_cast<const WavPack::Properties*>(this)->lengthInMilliseconds();
|
||||
|
||||
else
|
||||
return 0;
|
||||
StringList desc;
|
||||
desc.append("Audio");
|
||||
desc.append(String::number(length()) + " seconds");
|
||||
desc.append(String::number(bitrate()) + " kbps");
|
||||
return desc.toString(", ");
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// protected methods
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
AudioProperties::AudioProperties(ReadStyle) :
|
||||
AudioProperties::AudioProperties() :
|
||||
d(0)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
#define TAGLIB_AUDIOPROPERTIES_H
|
||||
|
||||
#include "taglib_export.h"
|
||||
#include "tstring.h"
|
||||
|
||||
namespace TagLib {
|
||||
|
||||
@@ -75,16 +76,14 @@ namespace TagLib {
|
||||
*
|
||||
* \see lengthInMilliseconds()
|
||||
*/
|
||||
// BIC: make virtual
|
||||
int lengthInSeconds() const;
|
||||
virtual int lengthInSeconds() const = 0;
|
||||
|
||||
/*!
|
||||
* Returns the length of the file in milliseconds.
|
||||
*
|
||||
* \see lengthInSeconds()
|
||||
*/
|
||||
// BIC: make virtual
|
||||
int lengthInMilliseconds() const;
|
||||
virtual int lengthInMilliseconds() const = 0;
|
||||
|
||||
/*!
|
||||
* Returns the most appropriate bit rate for the file in kb/s. For constant
|
||||
@@ -103,23 +102,27 @@ namespace TagLib {
|
||||
*/
|
||||
virtual int channels() const = 0;
|
||||
|
||||
/*!
|
||||
* Returns description of the audio file.
|
||||
*/
|
||||
virtual String toString() const;
|
||||
|
||||
protected:
|
||||
|
||||
/*!
|
||||
* Construct an audio properties instance. This is protected as this class
|
||||
* Constructs an audio properties instance. This is protected as this class
|
||||
* should not be instantiated directly, but should be instantiated via its
|
||||
* subclasses and can be fetched from the FileRef or File APIs.
|
||||
*
|
||||
* \see ReadStyle
|
||||
*/
|
||||
AudioProperties(ReadStyle style);
|
||||
AudioProperties();
|
||||
|
||||
private:
|
||||
// Noncopyable. Derived classes as well.
|
||||
AudioProperties(const AudioProperties &);
|
||||
AudioProperties &operator=(const AudioProperties &);
|
||||
|
||||
class AudioPropertiesPrivate;
|
||||
AudioPropertiesPrivate *d;
|
||||
class PropertiesPrivate;
|
||||
PropertiesPrivate *d;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
166
taglib/dsdiff/dsdiffdiintag.cpp
Normal file
166
taglib/dsdiff/dsdiffdiintag.cpp
Normal file
@@ -0,0 +1,166 @@
|
||||
/***************************************************************************
|
||||
copyright : (C) 2016 by Damien Plisson, Audirvana
|
||||
email : damien78@audirvana.com
|
||||
***************************************************************************/
|
||||
|
||||
/***************************************************************************
|
||||
* This library is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU Lesser General Public License version *
|
||||
* 2.1 as published by the Free Software Foundation. *
|
||||
* *
|
||||
* This library is distributed in the hope that it will be useful, but *
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
||||
* Lesser General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Lesser General Public *
|
||||
* License along with this library; if not, write to the Free Software *
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
|
||||
* 02110-1301 USA *
|
||||
* *
|
||||
* Alternatively, this file is available under the Mozilla Public *
|
||||
* License Version 1.1. You may obtain a copy of the License at *
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#include "dsdiffdiintag.h"
|
||||
#include "tstringlist.h"
|
||||
#include "tpropertymap.h"
|
||||
#include "tpicturemap.h"
|
||||
|
||||
using namespace TagLib;
|
||||
using namespace DSDIFF::DIIN;
|
||||
|
||||
class DSDIFF::DIIN::Tag::TagPrivate
|
||||
{
|
||||
public:
|
||||
TagPrivate()
|
||||
{
|
||||
}
|
||||
|
||||
String title;
|
||||
String artist;
|
||||
};
|
||||
|
||||
DSDIFF::DIIN::Tag::Tag() : TagLib::Tag()
|
||||
{
|
||||
d = new TagPrivate;
|
||||
}
|
||||
|
||||
DSDIFF::DIIN::Tag::~Tag()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
|
||||
String DSDIFF::DIIN::Tag::title() const
|
||||
{
|
||||
return d->title;
|
||||
}
|
||||
|
||||
String DSDIFF::DIIN::Tag::artist() const
|
||||
{
|
||||
return d->artist;
|
||||
}
|
||||
|
||||
String DSDIFF::DIIN::Tag::album() const
|
||||
{
|
||||
return String();
|
||||
}
|
||||
|
||||
String DSDIFF::DIIN::Tag::comment() const
|
||||
{
|
||||
return String();
|
||||
}
|
||||
|
||||
String DSDIFF::DIIN::Tag::genre() const
|
||||
{
|
||||
return String();
|
||||
}
|
||||
|
||||
unsigned int DSDIFF::DIIN::Tag::year() const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned int DSDIFF::DIIN::Tag::track() const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
PictureMap DSDIFF::DIIN::Tag::pictures() const
|
||||
{
|
||||
return PictureMap();
|
||||
}
|
||||
|
||||
void DSDIFF::DIIN::Tag::setTitle(const String &title)
|
||||
{
|
||||
d->title = title;
|
||||
}
|
||||
|
||||
void DSDIFF::DIIN::Tag::setArtist(const String &artist)
|
||||
{
|
||||
d->artist = artist;
|
||||
}
|
||||
|
||||
void DSDIFF::DIIN::Tag::setAlbum(const String &)
|
||||
{
|
||||
}
|
||||
|
||||
void DSDIFF::DIIN::Tag::setComment(const String &)
|
||||
{
|
||||
}
|
||||
|
||||
void DSDIFF::DIIN::Tag::setGenre(const String &)
|
||||
{
|
||||
}
|
||||
|
||||
void DSDIFF::DIIN::Tag::setYear(unsigned int)
|
||||
{
|
||||
}
|
||||
|
||||
void DSDIFF::DIIN::Tag::setTrack(unsigned int)
|
||||
{
|
||||
}
|
||||
|
||||
void DSDIFF::DIIN::Tag::setPictures( const PictureMap& l )
|
||||
{
|
||||
}
|
||||
|
||||
PropertyMap DSDIFF::DIIN::Tag::properties() const
|
||||
{
|
||||
PropertyMap properties;
|
||||
properties["TITLE"] = d->title;
|
||||
properties["ARTIST"] = d->artist;
|
||||
return properties;
|
||||
}
|
||||
|
||||
PropertyMap DSDIFF::DIIN::Tag::setProperties(const PropertyMap &origProps)
|
||||
{
|
||||
PropertyMap properties(origProps);
|
||||
properties.removeEmpty();
|
||||
StringList oneValueSet;
|
||||
|
||||
if(properties.contains("TITLE")) {
|
||||
d->title = properties["TITLE"].front();
|
||||
oneValueSet.append("TITLE");
|
||||
} else
|
||||
d->title = String();
|
||||
|
||||
if(properties.contains("ARTIST")) {
|
||||
d->artist = properties["ARTIST"].front();
|
||||
oneValueSet.append("ARTIST");
|
||||
} else
|
||||
d->artist = String();
|
||||
|
||||
// for each tag that has been set above, remove the first entry in the corresponding
|
||||
// value list. The others will be returned as unsupported by this format.
|
||||
for(StringList::Iterator it = oneValueSet.begin(); it != oneValueSet.end(); ++it) {
|
||||
if(properties[*it].size() == 1)
|
||||
properties.erase(*it);
|
||||
else
|
||||
properties[*it].erase(properties[*it].begin());
|
||||
}
|
||||
|
||||
return properties;
|
||||
}
|
||||
|
||||
160
taglib/dsdiff/dsdiffdiintag.h
Normal file
160
taglib/dsdiff/dsdiffdiintag.h
Normal file
@@ -0,0 +1,160 @@
|
||||
/***************************************************************************
|
||||
copyright : (C) 2016 by Damien Plisson, Audirvana
|
||||
email : damien78@audirvana.com
|
||||
***************************************************************************/
|
||||
|
||||
/***************************************************************************
|
||||
* This library is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU Lesser General Public License version *
|
||||
* 2.1 as published by the Free Software Foundation. *
|
||||
* *
|
||||
* This library is distributed in the hope that it will be useful, but *
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
||||
* Lesser General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Lesser General Public *
|
||||
* License along with this library; if not, write to the Free Software *
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
|
||||
* 02110-1301 USA *
|
||||
* *
|
||||
* Alternatively, this file is available under the Mozilla Public *
|
||||
* License Version 1.1. You may obtain a copy of the License at *
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef TAGLIB_DSDIFFDIINTAG_H
|
||||
#define TAGLIB_DSDIFFDIINTAG_H
|
||||
|
||||
#include "tag.h"
|
||||
|
||||
namespace TagLib {
|
||||
|
||||
namespace DSDIFF {
|
||||
|
||||
namespace DIIN {
|
||||
|
||||
/*!
|
||||
* Tags from the Edited Master Chunk Info
|
||||
*
|
||||
* Only Title and Artist tags are supported
|
||||
*/
|
||||
class TAGLIB_EXPORT Tag : public TagLib::Tag
|
||||
{
|
||||
public:
|
||||
Tag();
|
||||
virtual ~Tag();
|
||||
|
||||
/*!
|
||||
* Returns the track name; if no track name is present in the tag
|
||||
* String() will be returned.
|
||||
*/
|
||||
virtual String title() const;
|
||||
|
||||
/*!
|
||||
* Returns the artist name; if no artist name is present in the tag
|
||||
* String() will be returned.
|
||||
*/
|
||||
virtual String artist() const;
|
||||
|
||||
/*!
|
||||
* Not supported. Therefore always returns String().
|
||||
*/
|
||||
virtual String album() const;
|
||||
|
||||
/*!
|
||||
* Not supported. Therefore always returns String().
|
||||
*/
|
||||
virtual String comment() const;
|
||||
|
||||
/*!
|
||||
* Not supported. Therefore always returns String().
|
||||
*/
|
||||
virtual String genre() const;
|
||||
|
||||
/*!
|
||||
* Not supported. Therefore always returns 0.
|
||||
*/
|
||||
virtual unsigned int year() const;
|
||||
|
||||
/*!
|
||||
* Not supported. Therefore always returns 0.
|
||||
*/
|
||||
virtual unsigned int track() const;
|
||||
|
||||
/*!
|
||||
* Not supported. Therefore always returns an empty list.
|
||||
*/
|
||||
virtual PictureMap pictures() const;
|
||||
|
||||
/*!
|
||||
* Sets the title to \a title. If \a title is String() then this
|
||||
* value will be cleared.
|
||||
*/
|
||||
virtual void setTitle(const String &title);
|
||||
|
||||
/*!
|
||||
* Sets the artist to \a artist. If \a artist is String() then this
|
||||
* value will be cleared.
|
||||
*/
|
||||
virtual void setArtist(const String &artist);
|
||||
|
||||
/*!
|
||||
* Not supported and therefore ignored.
|
||||
*/
|
||||
virtual void setAlbum(const String &album);
|
||||
|
||||
/*!
|
||||
* Not supported and therefore ignored.
|
||||
*/
|
||||
virtual void setComment(const String &comment);
|
||||
|
||||
/*!
|
||||
* Not supported and therefore ignored.
|
||||
*/
|
||||
virtual void setGenre(const String &genre);
|
||||
|
||||
/*!
|
||||
* Not supported and therefore ignored.
|
||||
*/
|
||||
virtual void setYear(unsigned int year);
|
||||
|
||||
/*!
|
||||
* Not supported and therefore ignored.
|
||||
*/
|
||||
virtual void setTrack(unsigned int track);
|
||||
|
||||
/*!
|
||||
* Not supported and therefore ignored.
|
||||
*/
|
||||
virtual void setPictures( const PictureMap& l );
|
||||
|
||||
/*!
|
||||
* Implements the unified property interface -- export function.
|
||||
* Since the DIIN tag is very limited, the exported map is as well.
|
||||
*/
|
||||
PropertyMap properties() const;
|
||||
|
||||
/*!
|
||||
* Implements the unified property interface -- import function.
|
||||
* Because of the limitations of the DIIN file tag, any tags besides
|
||||
* TITLE and ARTIST, will be
|
||||
* returned. Additionally, if the map contains tags with multiple values,
|
||||
* all but the first will be contained in the returned map of unsupported
|
||||
* properties.
|
||||
*/
|
||||
PropertyMap setProperties(const PropertyMap &);
|
||||
|
||||
private:
|
||||
Tag(const Tag &);
|
||||
Tag &operator=(const Tag &);
|
||||
|
||||
class TagPrivate;
|
||||
TagPrivate *d;
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
844
taglib/dsdiff/dsdifffile.cpp
Normal file
844
taglib/dsdiff/dsdifffile.cpp
Normal file
@@ -0,0 +1,844 @@
|
||||
/***************************************************************************
|
||||
copyright : (C) 2016 by Damien Plisson, Audirvana
|
||||
email : damien78@audirvana.com
|
||||
***************************************************************************/
|
||||
|
||||
/***************************************************************************
|
||||
* This library is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU Lesser General Public License version *
|
||||
* 2.1 as published by the Free Software Foundation. *
|
||||
* *
|
||||
* This library is distributed in the hope that it will be useful, but *
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
||||
* Lesser General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Lesser General Public *
|
||||
* License along with this library; if not, write to the Free Software *
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
|
||||
* 02110-1301 USA *
|
||||
* *
|
||||
* Alternatively, this file is available under the Mozilla Public *
|
||||
* License Version 1.1. You may obtain a copy of the License at *
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#include <tbytevector.h>
|
||||
#include <tdebug.h>
|
||||
#include <id3v2tag.h>
|
||||
#include <tstringlist.h>
|
||||
#include <tpropertymap.h>
|
||||
#include <tagutils.h>
|
||||
#include <tsmartptr.h>
|
||||
|
||||
#include "tagunion.h"
|
||||
#include "dsdifffile.h"
|
||||
|
||||
using namespace TagLib;
|
||||
|
||||
struct Chunk64
|
||||
{
|
||||
ByteVector name;
|
||||
unsigned long long offset;
|
||||
unsigned long long size;
|
||||
char padding;
|
||||
};
|
||||
|
||||
namespace
|
||||
{
|
||||
enum {
|
||||
ID3v2Index = 0,
|
||||
DIINIndex = 1
|
||||
};
|
||||
enum {
|
||||
PROPChunk = 0,
|
||||
DIINChunk = 1
|
||||
};
|
||||
}
|
||||
|
||||
class DSDIFF::File::FilePrivate
|
||||
{
|
||||
public:
|
||||
FilePrivate() :
|
||||
endianness(BigEndian),
|
||||
size(0),
|
||||
isID3InPropChunk(false),
|
||||
duplicateID3V2chunkIndex(-1),
|
||||
id3v2TagChunkID("ID3 "),
|
||||
hasID3v2(false),
|
||||
hasDiin(false)
|
||||
{
|
||||
childChunkIndex[ID3v2Index] = -1;
|
||||
childChunkIndex[DIINIndex] = -1;
|
||||
}
|
||||
|
||||
Endianness endianness;
|
||||
ByteVector type;
|
||||
unsigned long long size;
|
||||
ByteVector format;
|
||||
std::vector<Chunk64> chunks;
|
||||
std::vector<Chunk64> childChunks[2];
|
||||
int childChunkIndex[2];
|
||||
bool isID3InPropChunk; // Two possibilities can be found: ID3V2 chunk inside PROP chunk or at root level
|
||||
int duplicateID3V2chunkIndex; // 2 ID3 chunks are present. This is then the index of the one in
|
||||
// PROP chunk that will be removed upon next save to remove duplicates.
|
||||
|
||||
SCOPED_PTR<AudioProperties> properties;
|
||||
|
||||
DoubleTagUnion tag;
|
||||
|
||||
ByteVector id3v2TagChunkID;
|
||||
|
||||
bool hasID3v2;
|
||||
bool hasDiin;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// static members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
bool DSDIFF::File::isSupported(IOStream *stream)
|
||||
{
|
||||
// A DSDIFF file has to start with "FRM8????????DSD ".
|
||||
|
||||
const ByteVector id = Utils::readHeader(stream, 16, false);
|
||||
return (id.startsWith("FRM8") && id.containsAt("DSD ", 12));
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// public members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
DSDIFF::File::File(FileName file, bool readProperties,
|
||||
AudioProperties::ReadStyle propertiesStyle) : TagLib::File(file)
|
||||
{
|
||||
d = new FilePrivate;
|
||||
d->endianness = BigEndian;
|
||||
if(isOpen())
|
||||
read(readProperties, propertiesStyle);
|
||||
}
|
||||
|
||||
DSDIFF::File::File(IOStream *stream, bool readProperties,
|
||||
AudioProperties::ReadStyle propertiesStyle) : TagLib::File(stream)
|
||||
{
|
||||
d = new FilePrivate;
|
||||
d->endianness = BigEndian;
|
||||
if(isOpen())
|
||||
read(readProperties, propertiesStyle);
|
||||
}
|
||||
|
||||
DSDIFF::File::~File()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
|
||||
TagLib::Tag *DSDIFF::File::tag() const
|
||||
{
|
||||
return &d->tag;
|
||||
}
|
||||
|
||||
ID3v2::Tag *DSDIFF::File::ID3v2Tag() const
|
||||
{
|
||||
return d->tag.access<ID3v2::Tag>(ID3v2Index, false);
|
||||
}
|
||||
|
||||
bool DSDIFF::File::hasID3v2Tag() const
|
||||
{
|
||||
return d->hasID3v2;
|
||||
}
|
||||
|
||||
DSDIFF::DIIN::Tag *DSDIFF::File::DIINTag() const
|
||||
{
|
||||
return d->tag.access<DSDIFF::DIIN::Tag>(DIINIndex, false);
|
||||
}
|
||||
|
||||
bool DSDIFF::File::hasDIINTag() const
|
||||
{
|
||||
return d->hasDiin;
|
||||
}
|
||||
|
||||
PropertyMap DSDIFF::File::properties() const
|
||||
{
|
||||
if(d->hasID3v2)
|
||||
return d->tag.access<ID3v2::Tag>(ID3v2Index, false)->properties();
|
||||
|
||||
return PropertyMap();
|
||||
}
|
||||
|
||||
void DSDIFF::File::removeUnsupportedProperties(const StringList &unsupported)
|
||||
{
|
||||
if(d->hasID3v2)
|
||||
d->tag.access<ID3v2::Tag>(ID3v2Index, false)->removeUnsupportedProperties(unsupported);
|
||||
|
||||
if(d->hasDiin)
|
||||
d->tag.access<DSDIFF::DIIN::Tag>(DIINIndex, false)->removeUnsupportedProperties(unsupported);
|
||||
}
|
||||
|
||||
PropertyMap DSDIFF::File::setProperties(const PropertyMap &properties)
|
||||
{
|
||||
return d->tag.access<ID3v2::Tag>(ID3v2Index, true)->setProperties(properties);
|
||||
}
|
||||
|
||||
DSDIFF::AudioProperties *DSDIFF::File::audioProperties() const
|
||||
{
|
||||
return d->properties.get();
|
||||
}
|
||||
|
||||
bool DSDIFF::File::save()
|
||||
{
|
||||
if(readOnly()) {
|
||||
debug("DSDIFF::File::save() -- File is read only.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!isValid()) {
|
||||
debug("DSDIFF::File::save() -- Trying to save invalid file.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// First: save ID3V2 chunk
|
||||
ID3v2::Tag *id3v2Tag = d->tag.access<ID3v2::Tag>(ID3v2Index, false);
|
||||
if(d->isID3InPropChunk) {
|
||||
if(id3v2Tag != NULL && !id3v2Tag->isEmpty()) {
|
||||
setChildChunkData(d->id3v2TagChunkID, id3v2Tag->render(), PROPChunk);
|
||||
d->hasID3v2 = true;
|
||||
}
|
||||
else {
|
||||
// Empty tag: remove it
|
||||
setChildChunkData(d->id3v2TagChunkID, ByteVector(), PROPChunk);
|
||||
d->hasID3v2 = false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if(id3v2Tag != NULL && !id3v2Tag->isEmpty()) {
|
||||
setRootChunkData(d->id3v2TagChunkID, id3v2Tag->render());
|
||||
d->hasID3v2 = true;
|
||||
}
|
||||
else {
|
||||
// Empty tag: remove it
|
||||
setRootChunkData(d->id3v2TagChunkID, ByteVector());
|
||||
d->hasID3v2 = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Second: save the DIIN chunk
|
||||
if(d->hasDiin) {
|
||||
DSDIFF::DIIN::Tag *diinTag = d->tag.access<DSDIFF::DIIN::Tag>(DIINIndex, false);
|
||||
|
||||
if(!diinTag->title().isEmpty()) {
|
||||
ByteVector diinTitle;
|
||||
if(d->endianness == BigEndian)
|
||||
diinTitle.append(ByteVector::fromUInt32BE(diinTag->title().size()));
|
||||
else
|
||||
diinTitle.append(ByteVector::fromUInt32LE(diinTag->title().size()));
|
||||
diinTitle.append(ByteVector::fromCString(diinTag->title().toCString()));
|
||||
setChildChunkData("DITI", diinTitle, DIINChunk);
|
||||
}
|
||||
else
|
||||
setChildChunkData("DITI", ByteVector(), DIINChunk);
|
||||
|
||||
if(!diinTag->artist().isEmpty()) {
|
||||
ByteVector diinArtist;
|
||||
if(d->endianness == BigEndian)
|
||||
diinArtist.append(ByteVector::fromUInt32BE(diinTag->artist().size()));
|
||||
else
|
||||
diinArtist.append(ByteVector::fromUInt32LE(diinTag->artist().size()));
|
||||
diinArtist.append(ByteVector::fromCString(diinTag->artist().toCString()));
|
||||
setChildChunkData("DIAR", diinArtist, DIINChunk);
|
||||
}
|
||||
else
|
||||
setChildChunkData("DIAR", ByteVector(), DIINChunk);
|
||||
}
|
||||
|
||||
// Third: remove the duplicate ID3V2 chunk (inside PROP chunk) if any
|
||||
if(d->duplicateID3V2chunkIndex>=0) {
|
||||
setChildChunkData(d->duplicateID3V2chunkIndex, ByteVector(), PROPChunk);
|
||||
d->duplicateID3V2chunkIndex = -1;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// private members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void DSDIFF::File::setRootChunkData(unsigned int i, const ByteVector &data)
|
||||
{
|
||||
if(data.isEmpty()) {
|
||||
// Null data: remove chunk
|
||||
// Update global size
|
||||
unsigned long long removedChunkTotalSize = d->chunks[i].size + d->chunks[i].padding + 12;
|
||||
d->size -= removedChunkTotalSize;
|
||||
if(d->endianness == BigEndian)
|
||||
insert(ByteVector::fromUInt64BE(d->size), 4, 8);
|
||||
else
|
||||
insert(ByteVector::fromUInt64LE(d->size), 4, 8);
|
||||
|
||||
removeBlock(d->chunks[i].offset - 12, removedChunkTotalSize);
|
||||
|
||||
// Update the internal offsets
|
||||
for(unsigned long r = i + 1; r < d->chunks.size(); r++)
|
||||
d->chunks[r].offset = d->chunks[r - 1].offset + 12
|
||||
+ d->chunks[r - 1].size + d->chunks[r - 1].padding;
|
||||
|
||||
d->chunks.erase(d->chunks.begin() + i);
|
||||
}
|
||||
else {
|
||||
// Non null data: update chunk
|
||||
// First we update the global size
|
||||
d->size += ((data.size() + 1) & ~1) - (d->chunks[i].size + d->chunks[i].padding);
|
||||
if(d->endianness == BigEndian)
|
||||
insert(ByteVector::fromUInt64BE(d->size), 4, 8);
|
||||
else
|
||||
insert(ByteVector::fromUInt64LE(d->size), 4, 8);
|
||||
|
||||
// Now update the specific chunk
|
||||
writeChunk(d->chunks[i].name,
|
||||
data,
|
||||
d->chunks[i].offset - 12,
|
||||
d->chunks[i].size + d->chunks[i].padding + 12);
|
||||
|
||||
d->chunks[i].size = data.size();
|
||||
d->chunks[i].padding = (data.size() & 0x01) ? 1 : 0;
|
||||
|
||||
// Finally update the internal offsets
|
||||
updateRootChunksStructure(i + 1);
|
||||
}
|
||||
}
|
||||
|
||||
void DSDIFF::File::setRootChunkData(const ByteVector &name, const ByteVector &data)
|
||||
{
|
||||
if(d->chunks.size() == 0) {
|
||||
debug("DSDIFF::File::setPropChunkData - No valid chunks found.");
|
||||
return;
|
||||
}
|
||||
|
||||
for(unsigned int i = 0; i < d->chunks.size(); i++) {
|
||||
if(d->chunks[i].name == name) {
|
||||
setRootChunkData(i, data);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Couldn't find an existing chunk, so let's create a new one.
|
||||
unsigned int i = d->chunks.size() - 1;
|
||||
unsigned long offset = d->chunks[i].offset + d->chunks[i].size + d->chunks[i].padding;
|
||||
|
||||
// First we update the global size
|
||||
d->size += (offset & 1) + ((data.size() + 1) & ~1) + 12;
|
||||
if(d->endianness == BigEndian)
|
||||
insert(ByteVector::fromUInt64BE(d->size), 4, 8);
|
||||
else
|
||||
insert(ByteVector::fromUInt64LE(d->size), 4, 8);
|
||||
|
||||
// Now add the chunk to the file
|
||||
writeChunk(name,
|
||||
data,
|
||||
offset,
|
||||
std::max<unsigned long long>(0, length() - offset),
|
||||
(offset & 1) ? 1 : 0);
|
||||
|
||||
Chunk64 chunk;
|
||||
chunk.name = name;
|
||||
chunk.size = data.size();
|
||||
chunk.offset = offset + 12;
|
||||
chunk.padding = (data.size() & 0x01) ? 1 : 0;
|
||||
|
||||
d->chunks.push_back(chunk);
|
||||
}
|
||||
|
||||
void DSDIFF::File::setChildChunkData(unsigned int i,
|
||||
const ByteVector &data,
|
||||
unsigned int childChunkNum)
|
||||
{
|
||||
std::vector<Chunk64> &childChunks = d->childChunks[childChunkNum];
|
||||
|
||||
if(data.isEmpty()) {
|
||||
// Null data: remove chunk
|
||||
// Update global size
|
||||
unsigned long long removedChunkTotalSize = childChunks[i].size + childChunks[i].padding + 12;
|
||||
d->size -= removedChunkTotalSize;
|
||||
if(d->endianness == BigEndian)
|
||||
insert(ByteVector::fromUInt64BE(d->size), 4, 8);
|
||||
else
|
||||
insert(ByteVector::fromUInt64LE(d->size), 4, 8);
|
||||
// Update child chunk size
|
||||
d->chunks[d->childChunkIndex[childChunkNum]].size -= removedChunkTotalSize;
|
||||
if(d->endianness == BigEndian)
|
||||
insert(ByteVector::fromUInt64BE(d->chunks[d->childChunkIndex[childChunkNum]].size),
|
||||
d->chunks[d->childChunkIndex[childChunkNum]].offset - 8, 8);
|
||||
else
|
||||
insert(ByteVector::fromUInt64LE(d->chunks[d->childChunkIndex[childChunkNum]].size),
|
||||
d->chunks[d->childChunkIndex[childChunkNum]].offset - 8, 8);
|
||||
|
||||
// Remove the chunk
|
||||
removeBlock(childChunks[i].offset - 12, removedChunkTotalSize);
|
||||
|
||||
// Update the internal offsets
|
||||
// For child chunks
|
||||
if((i + 1) < childChunks.size()) {
|
||||
childChunks[i + 1].offset = childChunks[i].offset;
|
||||
i++;
|
||||
for(i++; i < childChunks.size(); i++)
|
||||
childChunks[i].offset = childChunks[i - 1].offset + 12
|
||||
+ childChunks[i - 1].size + childChunks[i - 1].padding;
|
||||
}
|
||||
|
||||
// And for root chunks
|
||||
for(i = d->childChunkIndex[childChunkNum] + 1; i < d->chunks.size(); i++)
|
||||
d->chunks[i].offset = d->chunks[i - 1].offset + 12
|
||||
+ d->chunks[i - 1].size + d->chunks[i - 1].padding;
|
||||
|
||||
childChunks.erase(childChunks.begin() + i);
|
||||
}
|
||||
else {
|
||||
// Non null data: update chunk
|
||||
// First we update the global size
|
||||
d->size += ((data.size() + 1) & ~1) - (childChunks[i].size + childChunks[i].padding);
|
||||
if(d->endianness == BigEndian)
|
||||
insert(ByteVector::fromUInt64BE(d->size), 4, 8);
|
||||
else
|
||||
insert(ByteVector::fromUInt64LE(d->size), 4, 8);
|
||||
// And the PROP chunk size
|
||||
d->chunks[d->childChunkIndex[childChunkNum]].size += ((data.size() + 1) & ~1)
|
||||
- (childChunks[i].size + childChunks[i].padding);
|
||||
if(d->endianness == BigEndian)
|
||||
insert(ByteVector::fromUInt64BE(d->chunks[d->childChunkIndex[childChunkNum]].size),
|
||||
d->chunks[d->childChunkIndex[childChunkNum]].offset - 8, 8);
|
||||
else
|
||||
insert(ByteVector::fromUInt64LE(d->chunks[d->childChunkIndex[childChunkNum]].size),
|
||||
d->chunks[d->childChunkIndex[childChunkNum]].offset - 8, 8);
|
||||
|
||||
// Now update the specific chunk
|
||||
writeChunk(childChunks[i].name,
|
||||
data,
|
||||
childChunks[i].offset - 12,
|
||||
childChunks[i].size + childChunks[i].padding + 12);
|
||||
|
||||
childChunks[i].size = data.size();
|
||||
childChunks[i].padding = (data.size() & 0x01) ? 1 : 0;
|
||||
|
||||
// Now update the internal offsets
|
||||
// For child Chunks
|
||||
for(i++; i < childChunks.size(); i++)
|
||||
childChunks[i].offset = childChunks[i - 1].offset + 12
|
||||
+ childChunks[i - 1].size + childChunks[i - 1].padding;
|
||||
|
||||
// And for root chunks
|
||||
updateRootChunksStructure(d->childChunkIndex[childChunkNum] + 1);
|
||||
}
|
||||
}
|
||||
|
||||
void DSDIFF::File::setChildChunkData(const ByteVector &name,
|
||||
const ByteVector &data,
|
||||
unsigned int childChunkNum)
|
||||
{
|
||||
std::vector<Chunk64> &childChunks = d->childChunks[childChunkNum];
|
||||
|
||||
if(childChunks.size() == 0) {
|
||||
debug("DSDIFF::File::setPropChunkData - No valid chunks found.");
|
||||
return;
|
||||
}
|
||||
|
||||
for(unsigned int i = 0; i < childChunks.size(); i++) {
|
||||
if(childChunks[i].name == name) {
|
||||
setChildChunkData(i, data, childChunkNum);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Do not attempt to remove a non existing chunk
|
||||
if(data.isEmpty())
|
||||
return;
|
||||
|
||||
// Couldn't find an existing chunk, so let's create a new one.
|
||||
unsigned int i = childChunks.size() - 1;
|
||||
unsigned long offset = childChunks[i].offset + childChunks[i].size + childChunks[i].padding;
|
||||
|
||||
// First we update the global size
|
||||
d->size += (offset & 1) + ((data.size() + 1) & ~1) + 12;
|
||||
if(d->endianness == BigEndian)
|
||||
insert(ByteVector::fromUInt64BE(d->size), 4, 8);
|
||||
else
|
||||
insert(ByteVector::fromUInt64LE(d->size), 4, 8);
|
||||
// And the child chunk size
|
||||
d->chunks[d->childChunkIndex[childChunkNum]].size += (offset & 1)
|
||||
+ ((data.size() + 1) & ~1) + 12;
|
||||
if(d->endianness == BigEndian)
|
||||
insert(ByteVector::fromUInt64BE(d->chunks[d->childChunkIndex[childChunkNum]].size),
|
||||
d->chunks[d->childChunkIndex[childChunkNum]].offset - 8, 8);
|
||||
else
|
||||
insert(ByteVector::fromUInt64LE(d->chunks[d->childChunkIndex[childChunkNum]].size),
|
||||
d->chunks[d->childChunkIndex[childChunkNum]].offset - 8, 8);
|
||||
|
||||
// Now add the chunk to the file
|
||||
unsigned long long nextRootChunkIdx = length();
|
||||
if((d->childChunkIndex[childChunkNum] + 1) < static_cast<int>(d->chunks.size()))
|
||||
nextRootChunkIdx = d->chunks[d->childChunkIndex[childChunkNum] + 1].offset - 12;
|
||||
|
||||
writeChunk(name, data, offset,
|
||||
std::max<unsigned long long>(0, nextRootChunkIdx - offset),
|
||||
(offset & 1) ? 1 : 0);
|
||||
|
||||
// For root chunks
|
||||
updateRootChunksStructure(d->childChunkIndex[childChunkNum] + 1);
|
||||
|
||||
Chunk64 chunk;
|
||||
chunk.name = name;
|
||||
chunk.size = data.size();
|
||||
chunk.offset = offset + 12;
|
||||
chunk.padding = (data.size() & 0x01) ? 1 : 0;
|
||||
|
||||
childChunks.push_back(chunk);
|
||||
}
|
||||
|
||||
static bool isValidChunkID(const ByteVector &name)
|
||||
{
|
||||
if(name.size() != 4)
|
||||
return false;
|
||||
|
||||
for(int i = 0; i < 4; i++) {
|
||||
if(name[i] < 32 || name[i] > 127)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void DSDIFF::File::updateRootChunksStructure(unsigned int startingChunk)
|
||||
{
|
||||
for(unsigned int i = startingChunk; i < d->chunks.size(); i++)
|
||||
d->chunks[i].offset = d->chunks[i - 1].offset + 12
|
||||
+ d->chunks[i - 1].size + d->chunks[i - 1].padding;
|
||||
|
||||
// Update childchunks structure as well
|
||||
if(d->childChunkIndex[PROPChunk] >= static_cast<int>(startingChunk)) {
|
||||
std::vector<Chunk64> &childChunksToUpdate = d->childChunks[PROPChunk];
|
||||
if(childChunksToUpdate.size() > 0) {
|
||||
childChunksToUpdate[0].offset = d->chunks[d->childChunkIndex[PROPChunk]].offset + 12;
|
||||
for(unsigned int i = 1; i < childChunksToUpdate.size(); i++)
|
||||
childChunksToUpdate[i].offset = childChunksToUpdate[i - 1].offset + 12
|
||||
+ childChunksToUpdate[i - 1].size + childChunksToUpdate[i - 1].padding;
|
||||
}
|
||||
}
|
||||
if(d->childChunkIndex[DIINChunk] >= static_cast<int>(startingChunk)) {
|
||||
std::vector<Chunk64> &childChunksToUpdate = d->childChunks[DIINChunk];
|
||||
if(childChunksToUpdate.size() > 0) {
|
||||
childChunksToUpdate[0].offset = d->chunks[d->childChunkIndex[DIINChunk]].offset + 12;
|
||||
for(unsigned int i = 1; i < childChunksToUpdate.size(); i++)
|
||||
childChunksToUpdate[i].offset = childChunksToUpdate[i - 1].offset + 12
|
||||
+ childChunksToUpdate[i - 1].size + childChunksToUpdate[i - 1].padding;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DSDIFF::File::read(bool readProperties, AudioProperties::ReadStyle propertiesStyle)
|
||||
{
|
||||
bool bigEndian = (d->endianness == BigEndian);
|
||||
|
||||
d->type = readBlock(4);
|
||||
d->size = bigEndian ? readBlock(8).toInt64BE(0) : readBlock(8).toInt64LE(0);
|
||||
d->format = readBlock(4);
|
||||
|
||||
// + 12: chunk header at least, fix for additional junk bytes
|
||||
while(tell() + 12 <= length()) {
|
||||
ByteVector chunkName = readBlock(4);
|
||||
unsigned long long chunkSize = bigEndian ? readBlock(8).toInt64BE(0) : readBlock(8).toInt64LE(0);
|
||||
|
||||
if(!isValidChunkID(chunkName)) {
|
||||
debug("DSDIFF::File::read() -- Chunk '" + chunkName + "' has invalid ID");
|
||||
setValid(false);
|
||||
break;
|
||||
}
|
||||
|
||||
if(static_cast<unsigned long long>(tell()) + chunkSize > static_cast<unsigned long long>(length())) {
|
||||
debug("DSDIFF::File::read() -- Chunk '" + chunkName
|
||||
+ "' has invalid size (larger than the file size)");
|
||||
setValid(false);
|
||||
break;
|
||||
}
|
||||
|
||||
Chunk64 chunk;
|
||||
chunk.name = chunkName;
|
||||
chunk.size = chunkSize;
|
||||
chunk.offset = tell();
|
||||
|
||||
seek(chunk.size, Current);
|
||||
|
||||
// Check padding
|
||||
chunk.padding = 0;
|
||||
long uPosNotPadded = tell();
|
||||
if((uPosNotPadded & 0x01) != 0) {
|
||||
ByteVector iByte = readBlock(1);
|
||||
if((iByte.size() != 1) || (iByte[0] != 0))
|
||||
// Not well formed, re-seek
|
||||
seek(uPosNotPadded, Beginning);
|
||||
else
|
||||
chunk.padding = 1;
|
||||
}
|
||||
d->chunks.push_back(chunk);
|
||||
}
|
||||
|
||||
unsigned long long lengthDSDSamplesTimeChannels = 0; // For DSD uncompressed
|
||||
unsigned long long audioDataSizeinBytes = 0; // For computing bitrate
|
||||
unsigned long dstNumFrames = 0; // For DST compressed frames
|
||||
unsigned short dstFrameRate = 0; // For DST compressed frames
|
||||
|
||||
for(unsigned int i = 0; i < d->chunks.size(); i++) {
|
||||
if(d->chunks[i].name == "DSD ") {
|
||||
lengthDSDSamplesTimeChannels = d->chunks[i].size * 8;
|
||||
audioDataSizeinBytes = d->chunks[i].size;
|
||||
}
|
||||
else if(d->chunks[i].name == "DST ") {
|
||||
// Now decode the chunks inside the DST chunk to read the DST Frame Information one
|
||||
long long dstChunkEnd = d->chunks[i].offset + d->chunks[i].size;
|
||||
seek(d->chunks[i].offset);
|
||||
|
||||
audioDataSizeinBytes = d->chunks[i].size;
|
||||
|
||||
while(tell() + 12 <= dstChunkEnd) {
|
||||
ByteVector dstChunkName = readBlock(4);
|
||||
long long dstChunkSize = bigEndian ? readBlock(8).toInt64BE(0) : readBlock(8).toInt64LE(0);
|
||||
|
||||
if(!isValidChunkID(dstChunkName)) {
|
||||
debug("DSDIFF::File::read() -- DST Chunk '" + dstChunkName + "' has invalid ID");
|
||||
setValid(false);
|
||||
break;
|
||||
}
|
||||
|
||||
if(static_cast<long long>(tell()) + dstChunkSize > dstChunkEnd) {
|
||||
debug("DSDIFF::File::read() -- DST Chunk '" + dstChunkName
|
||||
+ "' has invalid size (larger than the DST chunk)");
|
||||
setValid(false);
|
||||
break;
|
||||
}
|
||||
|
||||
if(dstChunkName == "FRTE") {
|
||||
// Found the DST frame information chunk
|
||||
dstNumFrames = bigEndian ? readBlock(4).toUInt32BE(0) : readBlock(4).toUInt32LE(0);
|
||||
dstFrameRate = bigEndian ? readBlock(2).toUInt16BE(0) : readBlock(2).toUInt16LE(0);
|
||||
break; // Found the wanted one, no need to look at the others
|
||||
}
|
||||
|
||||
seek(dstChunkSize, Current);
|
||||
|
||||
// Check padding
|
||||
long uPosNotPadded = tell();
|
||||
if((uPosNotPadded & 0x01) != 0) {
|
||||
ByteVector iByte = readBlock(1);
|
||||
if((iByte.size() != 1) || (iByte[0] != 0))
|
||||
// Not well formed, re-seek
|
||||
seek(uPosNotPadded, Beginning);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(d->chunks[i].name == "PROP") {
|
||||
d->childChunkIndex[PROPChunk] = i;
|
||||
// Now decodes the chunks inside the PROP chunk
|
||||
long long propChunkEnd = d->chunks[i].offset + d->chunks[i].size;
|
||||
seek(d->chunks[i].offset + 4); // +4 to remove the 'SND ' marker at beginning of 'PROP' chunk
|
||||
while(tell() + 12 <= propChunkEnd) {
|
||||
ByteVector propChunkName = readBlock(4);
|
||||
long long propChunkSize = bigEndian ? readBlock(8).toInt64BE(0) : readBlock(8).toInt64LE(0);
|
||||
|
||||
if(!isValidChunkID(propChunkName)) {
|
||||
debug("DSDIFF::File::read() -- PROP Chunk '" + propChunkName + "' has invalid ID");
|
||||
setValid(false);
|
||||
break;
|
||||
}
|
||||
|
||||
if(static_cast<long long>(tell()) + propChunkSize > propChunkEnd) {
|
||||
debug("DSDIFF::File::read() -- PROP Chunk '" + propChunkName
|
||||
+ "' has invalid size (larger than the PROP chunk)");
|
||||
setValid(false);
|
||||
break;
|
||||
}
|
||||
|
||||
Chunk64 chunk;
|
||||
chunk.name = propChunkName;
|
||||
chunk.size = propChunkSize;
|
||||
chunk.offset = tell();
|
||||
|
||||
seek(chunk.size, Current);
|
||||
|
||||
// Check padding
|
||||
chunk.padding = 0;
|
||||
long uPosNotPadded = tell();
|
||||
if((uPosNotPadded & 0x01) != 0) {
|
||||
ByteVector iByte = readBlock(1);
|
||||
if((iByte.size() != 1) || (iByte[0] != 0))
|
||||
// Not well formed, re-seek
|
||||
seek(uPosNotPadded, Beginning);
|
||||
else
|
||||
chunk.padding = 1;
|
||||
}
|
||||
d->childChunks[PROPChunk].push_back(chunk);
|
||||
}
|
||||
}
|
||||
else if(d->chunks[i].name == "DIIN") {
|
||||
d->childChunkIndex[DIINChunk] = i;
|
||||
d->hasDiin = true;
|
||||
// Now decode the chunks inside the DIIN chunk
|
||||
long long diinChunkEnd = d->chunks[i].offset + d->chunks[i].size;
|
||||
seek(d->chunks[i].offset);
|
||||
|
||||
while(tell() + 12 <= diinChunkEnd) {
|
||||
ByteVector diinChunkName = readBlock(4);
|
||||
long long diinChunkSize = bigEndian ? readBlock(8).toInt64BE(0) : readBlock(8).toInt64LE(0);
|
||||
|
||||
if(!isValidChunkID(diinChunkName)) {
|
||||
debug("DSDIFF::File::read() -- DIIN Chunk '" + diinChunkName + "' has invalid ID");
|
||||
setValid(false);
|
||||
break;
|
||||
}
|
||||
|
||||
if(static_cast<long long>(tell()) + diinChunkSize > diinChunkEnd) {
|
||||
debug("DSDIFF::File::read() -- DIIN Chunk '" + diinChunkName
|
||||
+ "' has invalid size (larger than the DIIN chunk)");
|
||||
setValid(false);
|
||||
break;
|
||||
}
|
||||
|
||||
Chunk64 chunk;
|
||||
chunk.name = diinChunkName;
|
||||
chunk.size = diinChunkSize;
|
||||
chunk.offset = tell();
|
||||
|
||||
seek(chunk.size, Current);
|
||||
|
||||
// Check padding
|
||||
chunk.padding = 0;
|
||||
long uPosNotPadded = tell();
|
||||
if((uPosNotPadded & 0x01) != 0) {
|
||||
ByteVector iByte = readBlock(1);
|
||||
if((iByte.size() != 1) || (iByte[0] != 0))
|
||||
// Not well formed, re-seek
|
||||
seek(uPosNotPadded, Beginning);
|
||||
else
|
||||
chunk.padding = 1;
|
||||
}
|
||||
d->childChunks[DIINChunk].push_back(chunk);
|
||||
}
|
||||
}
|
||||
else if(d->chunks[i].name == "ID3 " || d->chunks[i].name == "id3 ") {
|
||||
d->id3v2TagChunkID = d->chunks[i].name;
|
||||
d->tag.set(ID3v2Index, new ID3v2::Tag(this, d->chunks[i].offset));
|
||||
d->isID3InPropChunk = false;
|
||||
d->hasID3v2 = true;
|
||||
}
|
||||
}
|
||||
|
||||
if(!isValid())
|
||||
return;
|
||||
|
||||
if(d->childChunkIndex[PROPChunk] < 0) {
|
||||
debug("DSDIFF::File::read() -- no PROP chunk found");
|
||||
setValid(false);
|
||||
return;
|
||||
}
|
||||
|
||||
// Read properties
|
||||
|
||||
unsigned int sampleRate=0;
|
||||
unsigned short channels=0;
|
||||
|
||||
for(unsigned int i = 0; i < d->childChunks[PROPChunk].size(); i++) {
|
||||
if(d->childChunks[PROPChunk][i].name == "ID3 " || d->childChunks[PROPChunk][i].name == "id3 ") {
|
||||
if(d->hasID3v2) {
|
||||
d->duplicateID3V2chunkIndex = i;
|
||||
continue; // ID3V2 tag has already been found at root level
|
||||
}
|
||||
d->id3v2TagChunkID = d->childChunks[PROPChunk][i].name;
|
||||
d->tag.set(ID3v2Index, new ID3v2::Tag(this, d->childChunks[PROPChunk][i].offset));
|
||||
d->isID3InPropChunk = true;
|
||||
d->hasID3v2 = true;
|
||||
}
|
||||
else if(d->childChunks[PROPChunk][i].name == "FS ") {
|
||||
// Sample rate
|
||||
seek(d->childChunks[PROPChunk][i].offset);
|
||||
sampleRate = bigEndian ? readBlock(4).toUInt32BE(0) : readBlock(4).toUInt32LE(0);
|
||||
}
|
||||
else if(d->childChunks[PROPChunk][i].name == "CHNL") {
|
||||
// Channels
|
||||
seek(d->childChunks[PROPChunk][i].offset);
|
||||
channels = bigEndian ? readBlock(2).toInt16BE(0) : readBlock(2).toInt16LE(0);
|
||||
}
|
||||
}
|
||||
|
||||
// Read title & artist from DIIN chunk
|
||||
d->tag.access<DSDIFF::DIIN::Tag>(DIINIndex, true);
|
||||
|
||||
if(d->hasDiin) {
|
||||
for(unsigned int i = 0; i < d->childChunks[DIINChunk].size(); i++) {
|
||||
if(d->childChunks[DIINChunk][i].name == "DITI") {
|
||||
seek(d->childChunks[DIINChunk][i].offset);
|
||||
unsigned int titleStrLength = bigEndian ? readBlock(4).toUInt32BE(0) : readBlock(4).toUInt32LE(0);
|
||||
if(titleStrLength <= d->childChunks[DIINChunk][i].size) {
|
||||
ByteVector titleStr = readBlock(titleStrLength);
|
||||
d->tag.access<DSDIFF::DIIN::Tag>(DIINIndex, false)->setTitle(titleStr);
|
||||
}
|
||||
}
|
||||
else if(d->childChunks[DIINChunk][i].name == "DIAR") {
|
||||
seek(d->childChunks[DIINChunk][i].offset);
|
||||
unsigned int artistStrLength = bigEndian ? readBlock(4).toUInt32BE(0) : readBlock(4).toUInt32LE(0);
|
||||
if(artistStrLength <= d->childChunks[DIINChunk][i].size) {
|
||||
ByteVector artistStr = readBlock(artistStrLength);
|
||||
d->tag.access<DSDIFF::DIIN::Tag>(DIINIndex, false)->setArtist(artistStr);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(readProperties) {
|
||||
if(lengthDSDSamplesTimeChannels == 0) {
|
||||
// DST compressed signal : need to compute length of DSD uncompressed frames
|
||||
if(dstFrameRate > 0)
|
||||
lengthDSDSamplesTimeChannels = (unsigned long long)dstNumFrames
|
||||
* (unsigned long long)sampleRate / (unsigned long long)dstFrameRate;
|
||||
else
|
||||
lengthDSDSamplesTimeChannels = 0;
|
||||
}
|
||||
else {
|
||||
// In DSD uncompressed files, the read number of samples is the total for each channel
|
||||
if(channels > 0)
|
||||
lengthDSDSamplesTimeChannels /= channels;
|
||||
}
|
||||
int bitrate = 0;
|
||||
if(lengthDSDSamplesTimeChannels > 0)
|
||||
bitrate = (audioDataSizeinBytes*8*sampleRate) / lengthDSDSamplesTimeChannels / 1000;
|
||||
|
||||
d->properties.reset(new AudioProperties(sampleRate,
|
||||
channels,
|
||||
lengthDSDSamplesTimeChannels,
|
||||
bitrate,
|
||||
propertiesStyle));
|
||||
}
|
||||
|
||||
if(!ID3v2Tag()) {
|
||||
d->tag.access<ID3v2::Tag>(ID3v2Index, true);
|
||||
d->isID3InPropChunk = false; // By default, ID3 chunk is at root level
|
||||
d->hasID3v2 = false;
|
||||
}
|
||||
}
|
||||
|
||||
void DSDIFF::File::writeChunk(const ByteVector &name, const ByteVector &data,
|
||||
unsigned long long offset, unsigned long replace,
|
||||
unsigned int leadingPadding)
|
||||
{
|
||||
ByteVector combined;
|
||||
if(leadingPadding)
|
||||
combined.append(ByteVector(leadingPadding, '\x00'));
|
||||
|
||||
combined.append(name);
|
||||
if(d->endianness == BigEndian)
|
||||
combined.append(ByteVector::fromUInt64BE(data.size()));
|
||||
else
|
||||
combined.append(ByteVector::fromUInt64LE(data.size()));
|
||||
combined.append(data);
|
||||
if((data.size() & 0x01) != 0)
|
||||
combined.append('\x00');
|
||||
|
||||
insert(combined, offset, replace);
|
||||
}
|
||||
|
||||
260
taglib/dsdiff/dsdifffile.h
Normal file
260
taglib/dsdiff/dsdifffile.h
Normal file
@@ -0,0 +1,260 @@
|
||||
/***************************************************************************
|
||||
copyright : (C) 2016 by Damien Plisson, Audirvana
|
||||
email : damien78@audirvana.com
|
||||
***************************************************************************/
|
||||
|
||||
/***************************************************************************
|
||||
* This library is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU Lesser General Public License version *
|
||||
* 2.1 as published by the Free Software Foundation. *
|
||||
* *
|
||||
* This library is distributed in the hope that it will be useful, but *
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
||||
* Lesser General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Lesser General Public *
|
||||
* License along with this library; if not, write to the Free Software *
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
|
||||
* 02110-1301 USA *
|
||||
* *
|
||||
* Alternatively, this file is available under the Mozilla Public *
|
||||
* License Version 1.1. You may obtain a copy of the License at *
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef TAGLIB_DSDIFFFILE_H
|
||||
#define TAGLIB_DSDIFFFILE_H
|
||||
|
||||
#include "rifffile.h"
|
||||
#include "id3v2tag.h"
|
||||
#include "dsdiffproperties.h"
|
||||
#include "dsdiffdiintag.h"
|
||||
|
||||
namespace TagLib {
|
||||
|
||||
//! An implementation of DSDIFF metadata
|
||||
|
||||
/*!
|
||||
* This is implementation of DSDIFF metadata.
|
||||
*
|
||||
* This supports an ID3v2 tag as well as reading stream from the ID3 RIFF
|
||||
* chunk as well as properties from the file.
|
||||
* Description of the DSDIFF format is available
|
||||
* at http://dsd-guide.com/sites/default/files/white-papers/DSDIFF_1.5_Spec.pdf
|
||||
* DSDIFF standard does not explictly specify the ID3V2 chunk
|
||||
* It can be found at the root level, but also sometimes inside the PROP chunk
|
||||
* In addition, title and artist info are stored as part of the standard
|
||||
*/
|
||||
|
||||
namespace DSDIFF {
|
||||
|
||||
//! An implementation of TagLib::File with DSDIFF specific methods
|
||||
|
||||
/*!
|
||||
* This implements and provides an interface for DSDIFF files to the
|
||||
* TagLib::Tag and TagLib::AudioProperties interfaces by way of implementing
|
||||
* the abstract TagLib::File API as well as providing some additional
|
||||
* information specific to DSDIFF files.
|
||||
*/
|
||||
|
||||
class TAGLIB_EXPORT File : public TagLib::File
|
||||
{
|
||||
public:
|
||||
/*!
|
||||
* Constructs an DSDIFF file from \a file. If \a readProperties is true
|
||||
* the file's audio properties will also be read.
|
||||
*
|
||||
* \note In the current implementation, \a propertiesStyle is ignored.
|
||||
*/
|
||||
File(FileName file, bool readProperties = true,
|
||||
AudioProperties::ReadStyle propertiesStyle = AudioProperties::Average);
|
||||
|
||||
/*!
|
||||
* Constructs an DSDIFF file from \a stream. If \a readProperties is true
|
||||
* the file's audio properties will also be read.
|
||||
*
|
||||
* \note TagLib will *not* take ownership of the stream, the caller is
|
||||
* responsible for deleting it after the File object.
|
||||
*
|
||||
* \note In the current implementation, \a propertiesStyle is ignored.
|
||||
*/
|
||||
File(IOStream *stream, bool readProperties = true,
|
||||
AudioProperties::ReadStyle propertiesStyle = AudioProperties::Average);
|
||||
|
||||
/*!
|
||||
* Destroys this instance of the File.
|
||||
*/
|
||||
virtual ~File();
|
||||
|
||||
/*!
|
||||
* Returns a pointer to a tag that is the union of the ID3v2 and DIIN
|
||||
* tags. The ID3v2 tag is given priority in reading the information -- if
|
||||
* requested information exists in both the ID3v2 tag and the ID3v1 tag,
|
||||
* the information from the ID3v2 tag will be returned.
|
||||
*
|
||||
* If you would like more granular control over the content of the tags,
|
||||
* with the concession of generality, use the tag-type specific calls.
|
||||
*
|
||||
* \note As this tag is not implemented as an ID3v2 tag or a DIIN tag,
|
||||
* but a union of the two this pointer may not be cast to the specific
|
||||
* tag types.
|
||||
*
|
||||
* \see ID3v2Tag()
|
||||
* \see DIINTag()
|
||||
*/
|
||||
virtual Tag *tag() const;
|
||||
|
||||
/*!
|
||||
* Returns the ID3V2 Tag for this file.
|
||||
*
|
||||
* \note This always returns a valid pointer regardless of whether or not
|
||||
* the file on disk has an ID3v2 tag. Use hasID3v2Tag() to check if the
|
||||
* file on disk actually has an ID3v2 tag.
|
||||
*
|
||||
* \see hasID3v2Tag()
|
||||
*/
|
||||
virtual ID3v2::Tag *ID3v2Tag() const;
|
||||
|
||||
/*!
|
||||
* Returns the DSDIFF DIIN Tag for this file
|
||||
*
|
||||
*/
|
||||
DSDIFF::DIIN::Tag *DIINTag() const;
|
||||
|
||||
/*!
|
||||
* Implements the unified property interface -- export function.
|
||||
* This method forwards to ID3v2::Tag::properties().
|
||||
*/
|
||||
PropertyMap properties() const;
|
||||
|
||||
void removeUnsupportedProperties(const StringList &properties);
|
||||
|
||||
/*!
|
||||
* Implements the unified property interface -- import function.
|
||||
* This method forwards to ID3v2::Tag::setProperties().
|
||||
*/
|
||||
PropertyMap setProperties(const PropertyMap &);
|
||||
|
||||
/*!
|
||||
* Returns the DSDIFF::Properties for this file. If no audio properties
|
||||
* were read then this will return a null pointer.
|
||||
*/
|
||||
virtual AudioProperties *audioProperties() const;
|
||||
|
||||
/*!
|
||||
* Save the file. If at least one tag -- ID3v1 or DIIN -- exists this
|
||||
* will duplicate its content into the other tag. This returns true
|
||||
* if saving was successful.
|
||||
*
|
||||
* If neither exists or if both tags are empty, this will strip the tags
|
||||
* from the file.
|
||||
*
|
||||
* This is the same as calling save(AllTags);
|
||||
*
|
||||
* If you would like more granular control over the content of the tags,
|
||||
* with the concession of generality, use paramaterized save call below.
|
||||
*
|
||||
* \see save(int tags)
|
||||
*/
|
||||
virtual bool save();
|
||||
|
||||
/*!
|
||||
* Save the file. This will attempt to save all of the tag types that are
|
||||
* specified by OR-ing together TagTypes values. The save() method above
|
||||
* uses AllTags. This returns true if saving was successful.
|
||||
*
|
||||
* This strips all tags not included in the mask, but does not modify them
|
||||
* in memory, so later calls to save() which make use of these tags will
|
||||
* remain valid. This also strips empty tags.
|
||||
*/
|
||||
bool save(int tags);
|
||||
|
||||
/*!
|
||||
* Returns whether or not the file on disk actually has an ID3v2 tag.
|
||||
*
|
||||
* \see ID3v2Tag()
|
||||
*/
|
||||
bool hasID3v2Tag() const;
|
||||
|
||||
/*!
|
||||
* Returns whether or not the file on disk actually has the DSDIFF
|
||||
* Title & Artist tag.
|
||||
*
|
||||
* \see DIINTag()
|
||||
*/
|
||||
bool hasDIINTag() const;
|
||||
|
||||
/*!
|
||||
* Returns whether or not the given \a stream can be opened as a DSDIFF
|
||||
* file.
|
||||
*
|
||||
* \note This method is designed to do a quick check. The result may
|
||||
* not necessarily be correct.
|
||||
*/
|
||||
static bool isSupported(IOStream *stream);
|
||||
|
||||
protected:
|
||||
enum Endianness { BigEndian, LittleEndian };
|
||||
|
||||
File(FileName file, Endianness endianness);
|
||||
File(IOStream *stream, Endianness endianness);
|
||||
|
||||
private:
|
||||
File(const File &);
|
||||
File &operator=(const File &);
|
||||
|
||||
/*!
|
||||
* Sets the data for the the specified chunk at root level to \a data.
|
||||
*
|
||||
* \warning This will update the file immediately.
|
||||
*/
|
||||
void setRootChunkData(unsigned int i, const ByteVector &data);
|
||||
|
||||
/*!
|
||||
* Sets the data for the root-level chunk \a name to \a data.
|
||||
* If a root-level chunk with the given name already exists
|
||||
* it will be overwritten, otherwise it will be
|
||||
* created after the existing chunks.
|
||||
*
|
||||
* \warning This will update the file immediately.
|
||||
*/
|
||||
void setRootChunkData(const ByteVector &name, const ByteVector &data);
|
||||
|
||||
/*!
|
||||
* Sets the data for the the specified child chunk to \a data.
|
||||
*
|
||||
* If data is null, then remove the chunk
|
||||
*
|
||||
* \warning This will update the file immediately.
|
||||
*/
|
||||
void setChildChunkData(unsigned int i, const ByteVector &data,
|
||||
unsigned int childChunkNum);
|
||||
|
||||
/*!
|
||||
* Sets the data for the child chunk \a name to \a data. If a chunk with
|
||||
* the given name already exists it will be overwritten, otherwise it will
|
||||
* be created after the existing chunks inside child chunk.
|
||||
*
|
||||
* If data is null, then remove the chunks with \a name name
|
||||
*
|
||||
* \warning This will update the file immediately.
|
||||
*/
|
||||
void setChildChunkData(const ByteVector &name, const ByteVector &data,
|
||||
unsigned int childChunkNum);
|
||||
|
||||
void updateRootChunksStructure(unsigned int startingChunk);
|
||||
|
||||
void read(bool readProperties, AudioProperties::ReadStyle propertiesStyle);
|
||||
void writeChunk(const ByteVector &name, const ByteVector &data,
|
||||
unsigned long long offset, unsigned long replace = 0,
|
||||
unsigned int leadingPadding = 0);
|
||||
|
||||
class FilePrivate;
|
||||
FilePrivate *d;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
117
taglib/dsdiff/dsdiffproperties.cpp
Normal file
117
taglib/dsdiff/dsdiffproperties.cpp
Normal file
@@ -0,0 +1,117 @@
|
||||
/***************************************************************************
|
||||
copyright : (C) 2016 by Damien Plisson, Audirvana
|
||||
email : damien78@audirvana.com
|
||||
***************************************************************************/
|
||||
|
||||
/***************************************************************************
|
||||
* This library is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU Lesser General Public License version *
|
||||
* 2.1 as published by the Free Software Foundation. *
|
||||
* *
|
||||
* This library is distributed in the hope that it will be useful, but *
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
||||
* Lesser General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Lesser General Public *
|
||||
* License along with this library; if not, write to the Free Software *
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
|
||||
* 02110-1301 USA *
|
||||
* *
|
||||
* Alternatively, this file is available under the Mozilla Public *
|
||||
* License Version 1.1. You may obtain a copy of the License at *
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#include <tstring.h>
|
||||
#include <tdebug.h>
|
||||
|
||||
#include "dsdiffproperties.h"
|
||||
|
||||
using namespace TagLib;
|
||||
|
||||
class DSDIFF::AudioProperties::AudioPropertiesPrivate
|
||||
{
|
||||
public:
|
||||
AudioPropertiesPrivate() :
|
||||
length(0),
|
||||
bitrate(0),
|
||||
sampleRate(0),
|
||||
channels(0),
|
||||
sampleWidth(0),
|
||||
sampleCount(0)
|
||||
{
|
||||
}
|
||||
|
||||
int length;
|
||||
int bitrate;
|
||||
int sampleRate;
|
||||
int channels;
|
||||
int sampleWidth;
|
||||
unsigned long long sampleCount;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// public members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
DSDIFF::AudioProperties::AudioProperties(const unsigned int sampleRate,
|
||||
const unsigned short channels,
|
||||
const unsigned long long samplesCount,
|
||||
const int bitrate,
|
||||
ReadStyle style) : TagLib::AudioProperties(), d(new AudioPropertiesPrivate)
|
||||
{
|
||||
d->channels = channels;
|
||||
d->sampleCount = samplesCount;
|
||||
d->sampleWidth = 1;
|
||||
d->sampleRate = sampleRate;
|
||||
d->bitrate = bitrate;
|
||||
d->length = d->sampleRate > 0
|
||||
? static_cast<int>((d->sampleCount * 1000.0) / d->sampleRate + 0.5)
|
||||
: 0;
|
||||
}
|
||||
|
||||
DSDIFF::AudioProperties::~AudioProperties()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
|
||||
int DSDIFF::AudioProperties::length() const
|
||||
{
|
||||
return lengthInSeconds();
|
||||
}
|
||||
|
||||
int DSDIFF::AudioProperties::lengthInSeconds() const
|
||||
{
|
||||
return d->length / 1000;
|
||||
}
|
||||
|
||||
int DSDIFF::AudioProperties::lengthInMilliseconds() const
|
||||
{
|
||||
return d->length;
|
||||
}
|
||||
|
||||
int DSDIFF::AudioProperties::bitrate() const
|
||||
{
|
||||
return d->bitrate;
|
||||
}
|
||||
|
||||
int DSDIFF::AudioProperties::sampleRate() const
|
||||
{
|
||||
return d->sampleRate;
|
||||
}
|
||||
|
||||
int DSDIFF::AudioProperties::channels() const
|
||||
{
|
||||
return d->channels;
|
||||
}
|
||||
|
||||
int DSDIFF::AudioProperties::bitsPerSample() const
|
||||
{
|
||||
return d->sampleWidth;
|
||||
}
|
||||
|
||||
long long DSDIFF::AudioProperties::sampleCount() const
|
||||
{
|
||||
return d->sampleCount;
|
||||
}
|
||||
83
taglib/dsdiff/dsdiffproperties.h
Normal file
83
taglib/dsdiff/dsdiffproperties.h
Normal file
@@ -0,0 +1,83 @@
|
||||
/***************************************************************************
|
||||
copyright : (C) 2016 by Damien Plisson, Audirvana
|
||||
email : damien78@audirvana.com
|
||||
***************************************************************************/
|
||||
|
||||
/***************************************************************************
|
||||
* This library is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU Lesser General Public License version *
|
||||
* 2.1 as published by the Free Software Foundation. *
|
||||
* *
|
||||
* This library is distributed in the hope that it will be useful, but *
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
||||
* Lesser General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Lesser General Public *
|
||||
* License along with this library; if not, write to the Free Software *
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
|
||||
* 02110-1301 USA *
|
||||
* *
|
||||
* Alternatively, this file is available under the Mozilla Public *
|
||||
* License Version 1.1. You may obtain a copy of the License at *
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef TAGLIB_DSDIFFPROPERTIES_H
|
||||
#define TAGLIB_DSDIFFPROPERTIES_H
|
||||
|
||||
#include "audioproperties.h"
|
||||
|
||||
namespace TagLib {
|
||||
|
||||
namespace DSDIFF {
|
||||
|
||||
class File;
|
||||
|
||||
//! An implementation of audio property reading for DSDIFF
|
||||
|
||||
/*!
|
||||
* This reads the data from an DSDIFF stream found in the AudioProperties
|
||||
* API.
|
||||
*/
|
||||
|
||||
class TAGLIB_EXPORT AudioProperties : public TagLib::AudioProperties
|
||||
{
|
||||
public:
|
||||
/*!
|
||||
* Create an instance of DSDIFF::AudioProperties with the data read from the
|
||||
* ByteVector \a data.
|
||||
*/
|
||||
AudioProperties(const unsigned int sampleRate, const unsigned short channels,
|
||||
const unsigned long long samplesCount, const int bitrate,
|
||||
ReadStyle style);
|
||||
|
||||
/*!
|
||||
* Destroys this DSDIFF::AudioProperties instance.
|
||||
*/
|
||||
virtual ~AudioProperties();
|
||||
|
||||
// Reimplementations.
|
||||
|
||||
virtual int length() const;
|
||||
virtual int lengthInSeconds() const;
|
||||
virtual int lengthInMilliseconds() const;
|
||||
virtual int bitrate() const;
|
||||
virtual int sampleRate() const;
|
||||
virtual int channels() const;
|
||||
|
||||
int bitsPerSample() const;
|
||||
long long sampleCount() const;
|
||||
|
||||
private:
|
||||
AudioProperties(const AudioProperties &);
|
||||
AudioProperties &operator=(const AudioProperties &);
|
||||
|
||||
class AudioPropertiesPrivate;
|
||||
AudioPropertiesPrivate *d;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
234
taglib/dsf/dsffile.cpp
Normal file
234
taglib/dsf/dsffile.cpp
Normal file
@@ -0,0 +1,234 @@
|
||||
/***************************************************************************
|
||||
copyright : (C) 2013 - 2018 by Stephen F. Booth
|
||||
email : me@sbooth.org
|
||||
***************************************************************************/
|
||||
|
||||
/***************************************************************************
|
||||
* This library is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU Lesser General Public License version *
|
||||
* 2.1 as published by the Free Software Foundation. *
|
||||
* *
|
||||
* This library is distributed in the hope that it will be useful, but *
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
||||
* Lesser General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Lesser General Public *
|
||||
* License along with this library; if not, write to the Free Software *
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
|
||||
* 02110-1301 USA *
|
||||
* *
|
||||
* Alternatively, this file is available under the Mozilla Public *
|
||||
* License Version 1.1. You may obtain a copy of the License at *
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#include <tbytevector.h>
|
||||
#include <tdebug.h>
|
||||
#include <id3v2tag.h>
|
||||
#include <tstringlist.h>
|
||||
#include <tpropertymap.h>
|
||||
#include <tsmartptr.h>
|
||||
#include <tagutils.h>
|
||||
|
||||
#include "dsffile.h"
|
||||
|
||||
using namespace TagLib;
|
||||
|
||||
// The DSF specification is located at http://dsd-guide.com/sites/default/files/white-papers/DSFFileFormatSpec_E.pdf
|
||||
|
||||
class DSF::File::FilePrivate
|
||||
{
|
||||
public:
|
||||
FilePrivate() :
|
||||
fileSize(0),
|
||||
metadataOffset(0) {}
|
||||
|
||||
long long fileSize;
|
||||
long long metadataOffset;
|
||||
|
||||
SCOPED_PTR<AudioProperties> properties;
|
||||
SCOPED_PTR<ID3v2::Tag> tag;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// static members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
bool DSF::File::isSupported(IOStream *stream)
|
||||
{
|
||||
// A DSF file has to start with "DSD "
|
||||
const ByteVector id = Utils::readHeader(stream, 4, false);
|
||||
return id.startsWith("DSD ");
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// public members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
DSF::File::File(FileName file, bool readProperties,
|
||||
AudioProperties::ReadStyle propertiesStyle) :
|
||||
TagLib::File(file),
|
||||
d(new FilePrivate())
|
||||
{
|
||||
if(isOpen())
|
||||
read(readProperties, propertiesStyle);
|
||||
}
|
||||
|
||||
DSF::File::File(IOStream *stream, bool readProperties,
|
||||
AudioProperties::ReadStyle propertiesStyle) :
|
||||
TagLib::File(stream),
|
||||
d(new FilePrivate())
|
||||
{
|
||||
if(isOpen())
|
||||
read(readProperties, propertiesStyle);
|
||||
}
|
||||
|
||||
DSF::File::~File()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
|
||||
ID3v2::Tag *DSF::File::tag() const
|
||||
{
|
||||
return d->tag.get();
|
||||
}
|
||||
|
||||
DSF::AudioProperties *DSF::File::audioProperties() const
|
||||
{
|
||||
return d->properties.get();
|
||||
}
|
||||
|
||||
PropertyMap DSF::File::properties() const
|
||||
{
|
||||
return d->tag->properties();
|
||||
}
|
||||
|
||||
PropertyMap DSF::File::setProperties(const PropertyMap &properties)
|
||||
{
|
||||
return d->tag->setProperties(properties);
|
||||
}
|
||||
|
||||
bool DSF::File::save()
|
||||
{
|
||||
if(readOnly()) {
|
||||
debug("DSF::File::save() -- File is read only.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!isValid()) {
|
||||
debug("DSF::File::save() -- Trying to save invalid file.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Three things must be updated: the file size, the tag data, and the metadata offset
|
||||
|
||||
if(d->tag->isEmpty()) {
|
||||
long long newFileSize = d->metadataOffset ? d->metadataOffset : d->fileSize;
|
||||
|
||||
// Update the file size
|
||||
if(d->fileSize != newFileSize) {
|
||||
insert(ByteVector::fromUInt64LE(newFileSize), 12, 8);
|
||||
d->fileSize = newFileSize;
|
||||
}
|
||||
|
||||
// Update the metadata offset to 0 since there is no longer a tag
|
||||
if(d->metadataOffset) {
|
||||
insert(ByteVector::fromUInt64LE(0ULL), 20, 8);
|
||||
d->metadataOffset = 0;
|
||||
}
|
||||
|
||||
// Delete the old tag
|
||||
truncate(newFileSize);
|
||||
}
|
||||
else {
|
||||
ByteVector tagData = d->tag->render();
|
||||
|
||||
long long newMetadataOffset = d->metadataOffset ? d->metadataOffset : d->fileSize;
|
||||
long long newFileSize = newMetadataOffset + tagData.size();
|
||||
long long oldTagSize = d->fileSize - newMetadataOffset;
|
||||
|
||||
// Update the file size
|
||||
if(d->fileSize != newFileSize) {
|
||||
insert(ByteVector::fromUInt64LE(newFileSize), 12, 8);
|
||||
d->fileSize = newFileSize;
|
||||
}
|
||||
|
||||
// Update the metadata offset
|
||||
if(d->metadataOffset != newMetadataOffset) {
|
||||
insert(ByteVector::fromUInt64LE(newMetadataOffset), 20, 8);
|
||||
d->metadataOffset = newMetadataOffset;
|
||||
}
|
||||
|
||||
// Delete the old tag and write the new one
|
||||
insert(tagData, newMetadataOffset, static_cast<size_t>(oldTagSize));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// private members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void DSF::File::read(bool readProperties, AudioProperties::ReadStyle propertiesStyle)
|
||||
{
|
||||
// A DSF file consists of four chunks: DSD chunk, format chunk, data chunk, and metadata chunk
|
||||
// The file format is not chunked in the sense of a RIFF File, though
|
||||
|
||||
// DSD chunk
|
||||
ByteVector chunkName = readBlock(4);
|
||||
if(chunkName != "DSD ") {
|
||||
debug("DSF::File::read() -- Not a DSF file.");
|
||||
setValid(false);
|
||||
return;
|
||||
}
|
||||
|
||||
long long chunkSize = readBlock(8).toInt64LE(0);
|
||||
|
||||
// Integrity check
|
||||
if(28 != chunkSize) {
|
||||
debug("DSF::File::read() -- File is corrupted.");
|
||||
setValid(false);
|
||||
return;
|
||||
}
|
||||
|
||||
d->fileSize = readBlock(8).toInt64LE(0);
|
||||
|
||||
// File is malformed or corrupted
|
||||
if(d->fileSize != length()) {
|
||||
debug("DSF::File::read() -- File is corrupted.");
|
||||
setValid(false);
|
||||
return;
|
||||
}
|
||||
|
||||
d->metadataOffset = readBlock(8).toInt64LE(0);
|
||||
|
||||
// File is malformed or corrupted
|
||||
if(d->metadataOffset > d->fileSize) {
|
||||
debug("DSF::File::read() -- Invalid metadata offset.");
|
||||
setValid(false);
|
||||
return;
|
||||
}
|
||||
|
||||
// Format chunk
|
||||
chunkName = readBlock(4);
|
||||
if(chunkName != "fmt ") {
|
||||
debug("DSF::File::read() -- Missing 'fmt ' chunk.");
|
||||
setValid(false);
|
||||
return;
|
||||
}
|
||||
|
||||
chunkSize = readBlock(8).toInt64LE(0);
|
||||
|
||||
d->properties.reset(
|
||||
new AudioProperties(readBlock(static_cast<size_t>(chunkSize)), propertiesStyle));
|
||||
|
||||
// Skip the data chunk
|
||||
|
||||
// A metadata offset of 0 indicates the absence of an ID3v2 tag
|
||||
if(0 == d->metadataOffset)
|
||||
d->tag.reset(new ID3v2::Tag());
|
||||
else
|
||||
d->tag.reset(new ID3v2::Tag(this, d->metadataOffset));
|
||||
}
|
||||
127
taglib/dsf/dsffile.h
Normal file
127
taglib/dsf/dsffile.h
Normal file
@@ -0,0 +1,127 @@
|
||||
/***************************************************************************
|
||||
copyright : (C) 2013 - 2018 by Stephen F. Booth
|
||||
email : me@sbooth.org
|
||||
***************************************************************************/
|
||||
|
||||
/***************************************************************************
|
||||
* This library is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU Lesser General Public License version *
|
||||
* 2.1 as published by the Free Software Foundation. *
|
||||
* *
|
||||
* This library is distributed in the hope that it will be useful, but *
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
||||
* Lesser General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Lesser General Public *
|
||||
* License along with this library; if not, write to the Free Software *
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
|
||||
* 02110-1301 USA *
|
||||
* *
|
||||
* Alternatively, this file is available under the Mozilla Public *
|
||||
* License Version 1.1. You may obtain a copy of the License at *
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef TAGLIB_DSFFILE_H
|
||||
#define TAGLIB_DSFFILE_H
|
||||
|
||||
#include "tfile.h"
|
||||
#include "id3v2tag.h"
|
||||
#include "dsfproperties.h"
|
||||
|
||||
namespace TagLib {
|
||||
|
||||
//! An implementation of DSF metadata
|
||||
|
||||
/*!
|
||||
* This is implementation of DSF metadata.
|
||||
*
|
||||
* This supports an ID3v2 tag as well as properties from the file.
|
||||
*/
|
||||
|
||||
namespace DSF {
|
||||
|
||||
//! An implementation of TagLib::File with DSF specific methods
|
||||
|
||||
/*!
|
||||
* This implements and provides an interface for DSF files to the
|
||||
* TagLib::Tag and TagLib::AudioProperties interfaces by way of implementing
|
||||
* the abstract TagLib::File API as well as providing some additional
|
||||
* information specific to DSF files.
|
||||
*/
|
||||
|
||||
class TAGLIB_EXPORT File : public TagLib::File
|
||||
{
|
||||
public:
|
||||
/*!
|
||||
* Contructs an DSF file from \a file. If \a readProperties is true the
|
||||
* file's audio properties will also be read using \a propertiesStyle. If
|
||||
* false, \a propertiesStyle is ignored.
|
||||
*/
|
||||
File(FileName file, bool readProperties = true,
|
||||
AudioProperties::ReadStyle propertiesStyle = AudioProperties::Average);
|
||||
|
||||
/*!
|
||||
* Contructs an DSF file from \a file. If \a readProperties is true the
|
||||
* file's audio properties will also be read using \a propertiesStyle. If
|
||||
* false, \a propertiesStyle is ignored.
|
||||
*/
|
||||
File(IOStream *stream, bool readProperties = true,
|
||||
AudioProperties::ReadStyle propertiesStyle = AudioProperties::Average);
|
||||
|
||||
/*!
|
||||
* Destroys this instance of the File.
|
||||
*/
|
||||
virtual ~File();
|
||||
|
||||
/*!
|
||||
* Returns the Tag for this file.
|
||||
*/
|
||||
virtual ID3v2::Tag *tag() const;
|
||||
|
||||
/*!
|
||||
* Returns the DSF::AudioProperties for this file. If no audio properties
|
||||
* were read then this will return a null pointer.
|
||||
*/
|
||||
virtual AudioProperties *audioProperties() const;
|
||||
|
||||
/*!
|
||||
* Implements the unified property interface -- export function.
|
||||
* This method forwards to ID3v2::Tag::properties().
|
||||
*/
|
||||
virtual PropertyMap properties() const;
|
||||
|
||||
/*!
|
||||
* Implements the unified property interface -- import function.
|
||||
* This method forwards to ID3v2::Tag::setProperties().
|
||||
*/
|
||||
virtual PropertyMap setProperties(const PropertyMap &);
|
||||
|
||||
/*!
|
||||
* Saves the file.
|
||||
*/
|
||||
virtual bool save();
|
||||
|
||||
/*!
|
||||
* Returns whether or not the given \a stream can be opened as a DSF
|
||||
* file.
|
||||
*
|
||||
* \note This method is designed to do a quick check. The result may
|
||||
* not necessarily be correct.
|
||||
*/
|
||||
static bool isSupported(IOStream *stream);
|
||||
|
||||
private:
|
||||
File(const File &);
|
||||
File &operator=(const File &);
|
||||
|
||||
void read(bool readProperties, AudioProperties::ReadStyle propertiesStyle);
|
||||
|
||||
class FilePrivate;
|
||||
FilePrivate *d;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
158
taglib/dsf/dsfproperties.cpp
Normal file
158
taglib/dsf/dsfproperties.cpp
Normal file
@@ -0,0 +1,158 @@
|
||||
/***************************************************************************
|
||||
copyright : (C) 2013 by Stephen F. Booth
|
||||
email : me@sbooth.org
|
||||
***************************************************************************/
|
||||
|
||||
/***************************************************************************
|
||||
* This library is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU Lesser General Public License version *
|
||||
* 2.1 as published by the Free Software Foundation. *
|
||||
* *
|
||||
* This library is distributed in the hope that it will be useful, but *
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
||||
* Lesser General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Lesser General Public *
|
||||
* License along with this library; if not, write to the Free Software *
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
|
||||
* 02110-1301 USA *
|
||||
* *
|
||||
* Alternatively, this file is available under the Mozilla Public *
|
||||
* License Version 1.1. You may obtain a copy of the License at *
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#include <tstring.h>
|
||||
#include <tdebug.h>
|
||||
|
||||
#include "dsfproperties.h"
|
||||
|
||||
using namespace TagLib;
|
||||
|
||||
class DSF::AudioProperties::PropertiesPrivate
|
||||
{
|
||||
public:
|
||||
PropertiesPrivate() :
|
||||
formatVersion(0),
|
||||
formatID(0),
|
||||
channelType(0),
|
||||
channelNum(0),
|
||||
samplingFrequency(0),
|
||||
bitsPerSample(0),
|
||||
sampleCount(0),
|
||||
blockSizePerChannel(0),
|
||||
bitrate(0),
|
||||
length(0) {}
|
||||
|
||||
// Nomenclature is from DSF file format specification
|
||||
unsigned int formatVersion;
|
||||
unsigned int formatID;
|
||||
unsigned int channelType;
|
||||
unsigned int channelNum;
|
||||
unsigned int samplingFrequency;
|
||||
unsigned int bitsPerSample;
|
||||
long long sampleCount;
|
||||
unsigned int blockSizePerChannel;
|
||||
|
||||
// Computed
|
||||
int bitrate;
|
||||
int length;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// public members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
DSF::AudioProperties::AudioProperties(const ByteVector &data, ReadStyle style) :
|
||||
d(new PropertiesPrivate())
|
||||
{
|
||||
read(data);
|
||||
}
|
||||
|
||||
DSF::AudioProperties::~AudioProperties()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
|
||||
int DSF::AudioProperties::length() const
|
||||
{
|
||||
return lengthInSeconds();
|
||||
}
|
||||
|
||||
int DSF::AudioProperties::lengthInSeconds() const
|
||||
{
|
||||
return d->length / 1000;
|
||||
}
|
||||
|
||||
int DSF::AudioProperties::lengthInMilliseconds() const
|
||||
{
|
||||
return d->length;
|
||||
}
|
||||
|
||||
int DSF::AudioProperties::bitrate() const
|
||||
{
|
||||
return d->bitrate;
|
||||
}
|
||||
|
||||
int DSF::AudioProperties::sampleRate() const
|
||||
{
|
||||
return d->samplingFrequency;
|
||||
}
|
||||
|
||||
int DSF::AudioProperties::channels() const
|
||||
{
|
||||
return d->channelNum;
|
||||
}
|
||||
|
||||
// DSF specific
|
||||
int DSF::AudioProperties::formatVersion() const
|
||||
{
|
||||
return d->formatVersion;
|
||||
}
|
||||
|
||||
int DSF::AudioProperties::formatID() const
|
||||
{
|
||||
return d->formatID;
|
||||
}
|
||||
|
||||
int DSF::AudioProperties::channelType() const
|
||||
{
|
||||
return d->channelType;
|
||||
}
|
||||
|
||||
int DSF::AudioProperties::bitsPerSample() const
|
||||
{
|
||||
return d->bitsPerSample;
|
||||
}
|
||||
|
||||
long long DSF::AudioProperties::sampleCount() const
|
||||
{
|
||||
return d->sampleCount;
|
||||
}
|
||||
|
||||
int DSF::AudioProperties::blockSizePerChannel() const
|
||||
{
|
||||
return d->blockSizePerChannel;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// private members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void DSF::AudioProperties::read(const ByteVector &data)
|
||||
{
|
||||
d->formatVersion = data.toUInt32LE(0);
|
||||
d->formatID = data.toUInt32LE(4);
|
||||
d->channelType = data.toUInt32LE(8);
|
||||
d->channelNum = data.toUInt32LE(12);
|
||||
d->samplingFrequency = data.toUInt32LE(16);
|
||||
d->bitsPerSample = data.toUInt32LE(20);
|
||||
d->sampleCount = data.toInt64LE(24);
|
||||
d->blockSizePerChannel = data.toUInt32LE(32);
|
||||
|
||||
d->bitrate = static_cast<int>(d->samplingFrequency * d->bitsPerSample * d->channelNum / 1000.0 + 0.5);
|
||||
|
||||
if(d->samplingFrequency > 0)
|
||||
d->length = static_cast<int>(d->sampleCount * 1000.0 / d->samplingFrequency + 0.5);
|
||||
}
|
||||
91
taglib/dsf/dsfproperties.h
Normal file
91
taglib/dsf/dsfproperties.h
Normal file
@@ -0,0 +1,91 @@
|
||||
/***************************************************************************
|
||||
copyright : (C) 2013 by Stephen F. Booth
|
||||
email : me@sbooth.org
|
||||
***************************************************************************/
|
||||
|
||||
/***************************************************************************
|
||||
* This library is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU Lesser General Public License version *
|
||||
* 2.1 as published by the Free Software Foundation. *
|
||||
* *
|
||||
* This library is distributed in the hope that it will be useful, but *
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
||||
* Lesser General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Lesser General Public *
|
||||
* License along with this library; if not, write to the Free Software *
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
|
||||
* 02110-1301 USA *
|
||||
* *
|
||||
* Alternatively, this file is available under the Mozilla Public *
|
||||
* License Version 1.1. You may obtain a copy of the License at *
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef TAGLIB_DSFPROPERTIES_H
|
||||
#define TAGLIB_DSFPROPERTIES_H
|
||||
|
||||
#include "audioproperties.h"
|
||||
|
||||
namespace TagLib {
|
||||
|
||||
namespace DSF {
|
||||
|
||||
class File;
|
||||
|
||||
//! An implementation of audio property reading for DSF
|
||||
|
||||
/*!
|
||||
* This reads the data from a DSF stream found in the AudioProperties
|
||||
* API.
|
||||
*/
|
||||
|
||||
class TAGLIB_EXPORT AudioProperties : public TagLib::AudioProperties
|
||||
{
|
||||
public:
|
||||
/*!
|
||||
* Create an instance of DSF::AudioProperties with the data read from the
|
||||
* ByteVector \a data.
|
||||
*/
|
||||
AudioProperties(const ByteVector &data, ReadStyle style);
|
||||
|
||||
/*!
|
||||
* Destroys this DSF::AudioProperties instance.
|
||||
*/
|
||||
virtual ~AudioProperties();
|
||||
|
||||
// Reimplementations.
|
||||
|
||||
virtual int length() const;
|
||||
virtual int lengthInSeconds() const;
|
||||
virtual int lengthInMilliseconds() const;
|
||||
virtual int bitrate() const;
|
||||
virtual int sampleRate() const;
|
||||
virtual int channels() const;
|
||||
|
||||
int formatVersion() const;
|
||||
int formatID() const;
|
||||
|
||||
/*!
|
||||
* Channel type values: 1 = mono, 2 = stereo, 3 = 3 channels,
|
||||
* 4 = quad, 5 = 4 channels, 6 = 5 channels, 7 = 5.1 channels
|
||||
*/
|
||||
int channelType() const;
|
||||
int bitsPerSample() const;
|
||||
long long sampleCount() const;
|
||||
int blockSizePerChannel() const;
|
||||
|
||||
private:
|
||||
AudioProperties(const AudioProperties &);
|
||||
AudioProperties &operator=(const AudioProperties &);
|
||||
|
||||
void read(const ByteVector &data);
|
||||
|
||||
class PropertiesPrivate;
|
||||
PropertiesPrivate *d;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
58
taglib/ebml/ebmlconstants.h
Normal file
58
taglib/ebml/ebmlconstants.h
Normal file
@@ -0,0 +1,58 @@
|
||||
/***************************************************************************
|
||||
copyright : (C) 2013 by Sebastian Rachuj
|
||||
email : rachus@web.de
|
||||
***************************************************************************/
|
||||
|
||||
/***************************************************************************
|
||||
* This library is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU Lesser General Public License version *
|
||||
* 2.1 as published by the Free Software Foundation. *
|
||||
* *
|
||||
* This library is distributed in the hope that it will be useful, but *
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
||||
* Lesser General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Lesser General Public *
|
||||
* License along with this library; if not, write to the Free Software *
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
|
||||
* 02110-1301 USA *
|
||||
* *
|
||||
* Alternatively, this file is available under the Mozilla Public *
|
||||
* License Version 1.1. You may obtain a copy of the License at *
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef TAGLIB_EBML_CONSTANTS
|
||||
#define TAGLIB_EBML_CONSTANTS
|
||||
|
||||
namespace TagLib {
|
||||
|
||||
namespace EBML {
|
||||
//! Shorter representation of the type.
|
||||
typedef unsigned long long int ulli;
|
||||
|
||||
//! The id of an EBML Void element that is just a placeholder.
|
||||
const ulli Void = 0xecL;
|
||||
|
||||
//! The id of an EBML CRC32 element that contains a crc32 value.
|
||||
const ulli CRC32 = 0xc3L;
|
||||
|
||||
//! A namespace containing the ids of the EBML header's elements.
|
||||
namespace Header {
|
||||
const ulli EBML = 0x1a45dfa3L;
|
||||
const ulli EBMLVersion = 0x4286L;
|
||||
const ulli EBMLReadVersion = 0x42f7L;
|
||||
const ulli EBMLMaxIDWidth = 0x42f2L;
|
||||
const ulli EBMLMaxSizeWidth = 0x42f3L;
|
||||
const ulli DocType = 0x4282L;
|
||||
const ulli DocTypeVersion = 0x4287L;
|
||||
const ulli DocTypeReadVersion = 0x4285L;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
489
taglib/ebml/ebmlelement.cpp
Normal file
489
taglib/ebml/ebmlelement.cpp
Normal file
@@ -0,0 +1,489 @@
|
||||
/***************************************************************************
|
||||
copyright : (C) 2013 by Sebastian Rachuj
|
||||
email : rachus@web.de
|
||||
***************************************************************************/
|
||||
|
||||
/***************************************************************************
|
||||
* This library is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU Lesser General Public License version *
|
||||
* 2.1 as published by the Free Software Foundation. *
|
||||
* *
|
||||
* This library is distributed in the hope that it will be useful, but *
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
||||
* Lesser General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Lesser General Public *
|
||||
* License along with this library; if not, write to the Free Software *
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
|
||||
* 02110-1301 USA *
|
||||
* *
|
||||
* Alternatively, this file is available under the Mozilla Public *
|
||||
* License Version 1.1. You may obtain a copy of the License at *
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#include "tdebug.h"
|
||||
#include "ebmlelement.h"
|
||||
|
||||
using namespace TagLib;
|
||||
|
||||
class EBML::Element::ElementPrivate
|
||||
{
|
||||
public:
|
||||
// The id of this element.
|
||||
ulli id;
|
||||
|
||||
// The position of the element, where the header begins.
|
||||
long long position;
|
||||
|
||||
// The size of the element as read from the header. Note: Actually an ulli but
|
||||
// due to the variable integer size limited, thus long long is ok.
|
||||
long long size;
|
||||
|
||||
// The position of the element's data.
|
||||
long long data;
|
||||
|
||||
// The element's children.
|
||||
List<Element *> children;
|
||||
|
||||
// True: Treated this element as container and read children.
|
||||
bool populated;
|
||||
|
||||
// The parent element. If NULL (0) this is the document root.
|
||||
Element *parent;
|
||||
|
||||
// The file used to read and write.
|
||||
File *document;
|
||||
|
||||
// Destructor: Clean up all children.
|
||||
~ElementPrivate()
|
||||
{
|
||||
for(List<Element *>::Iterator i = children.begin(); i != children.end(); ++i) {
|
||||
delete *i;
|
||||
}
|
||||
}
|
||||
|
||||
// Reads a variable length integer from the file at the given position
|
||||
// and saves its value to result. If cutOne is true the first one of the
|
||||
// binary representation of the result is removed (required for size). If
|
||||
// cutOne is false the one will remain in the result (required for id).
|
||||
// This method returns the position directly after the read integer.
|
||||
long long readVInt(long long position, ulli *result, bool cutOne = true)
|
||||
{
|
||||
document->seek(position);
|
||||
|
||||
// Determine the length of the integer
|
||||
char firstByte = document->readBlock(1)[0];
|
||||
size_t byteSize = 1;
|
||||
for(size_t i = 0; i < 8 && ((firstByte << i) & (1 << 7)) == 0; ++i)
|
||||
++byteSize;
|
||||
|
||||
// Load the integer
|
||||
document->seek(position);
|
||||
ByteVector vint = document->readBlock(byteSize);
|
||||
|
||||
// Cut the one if requested
|
||||
if(cutOne)
|
||||
vint[0] = (vint[0] & (~(1 << (8 - byteSize))));
|
||||
|
||||
// Store the result and return the current position
|
||||
if(result)
|
||||
*result = static_cast<ulli>(vint.toInt64BE(0));
|
||||
return position + byteSize;
|
||||
}
|
||||
|
||||
// Returns a BytVector containing the given number in the variable integer
|
||||
// format. Truncates numbers > 2^56 (^ means potency in this case).
|
||||
// If addOne is true, the ByteVector will remain the One to determine the
|
||||
// integer's length.
|
||||
// If shortest is true, the ByteVector will be as short as possible (required
|
||||
// for the id)
|
||||
ByteVector createVInt(ulli number, bool addOne = true, bool shortest = true)
|
||||
{
|
||||
ByteVector vint = ByteVector::fromUInt64BE(number);
|
||||
|
||||
// Do we actually need to calculate the length of the variable length
|
||||
// integer? If not, then prepend the 0b0000 0001 if necessary and return the
|
||||
// vint.
|
||||
if(!shortest) {
|
||||
if(addOne)
|
||||
vint[0] = 1;
|
||||
return vint;
|
||||
}
|
||||
|
||||
// Calculate the minimal length of the variable length integer
|
||||
size_t byteSize = vint.size();
|
||||
for(size_t i = 0; byteSize > 0 && vint[i] == 0; ++i)
|
||||
--byteSize;
|
||||
|
||||
if(!addOne)
|
||||
return ByteVector(vint.data() + vint.size() - byteSize, byteSize);
|
||||
|
||||
ulli firstByte = (1ULL << (vint.size() - byteSize));
|
||||
// The most significant byte loses #bytSize bits for storing information.
|
||||
// Therefore, we might need to increase byteSize.
|
||||
if(number >= (firstByte << (8 * (byteSize - 1))) && byteSize < vint.size())
|
||||
++byteSize;
|
||||
// Add the one at the correct position
|
||||
size_t firstBytePosition = vint.size() - byteSize;
|
||||
vint[firstBytePosition] |= (1 << firstBytePosition);
|
||||
return ByteVector(vint.data() + firstBytePosition, byteSize);
|
||||
}
|
||||
|
||||
// Returns a void element within this element which is at least "least" in
|
||||
// size. Uses best fit method. Returns a null pointer if no suitable element
|
||||
// was found.
|
||||
Element *searchVoid(long long least = 0L)
|
||||
{
|
||||
Element *currentBest = 0;
|
||||
for(List<Element *>::Iterator i = children.begin(); i != children.end(); ++i) {
|
||||
if((*i)->d->id == Void &&
|
||||
// We need room for the header if we don't remove the element.
|
||||
((((*i)->d->size + (*i)->d->data - (*i)->d->position) == least || ((*i)->d->size >= least)) &&
|
||||
// best fit
|
||||
(!currentBest || (*i)->d->size < currentBest->d->size))
|
||||
) {
|
||||
currentBest = *i;
|
||||
}
|
||||
}
|
||||
return currentBest;
|
||||
}
|
||||
|
||||
// Replaces this element by a Void element. Returns true on success and false
|
||||
// on error.
|
||||
bool makeVoid()
|
||||
{
|
||||
ulli realSize = size + data - position;
|
||||
ByteVector header(createVInt(Void, false));
|
||||
ulli leftSize = realSize - (header.size() + sizeof(ulli));
|
||||
// Does not make sense to create a Void element
|
||||
if (leftSize > realSize)
|
||||
return false;
|
||||
header.append(createVInt(leftSize, true, false));
|
||||
// Write to file
|
||||
document->seek(position);
|
||||
document->writeBlock(header);
|
||||
// Update data
|
||||
data = position + header.size();
|
||||
size = leftSize;
|
||||
return true;
|
||||
|
||||
// XXX: We actually should merge Voids, if possible.
|
||||
}
|
||||
|
||||
// Reading constructor: Reads all unknown information from the file.
|
||||
ElementPrivate(File *p_document, Element *p_parent = 0, long long p_position = 0) :
|
||||
id(0),
|
||||
position(p_position),
|
||||
data(0),
|
||||
populated(false),
|
||||
parent(p_parent),
|
||||
document(p_document)
|
||||
{
|
||||
if(parent) {
|
||||
ulli ssize;
|
||||
data = readVInt(readVInt(position, &id, false), &ssize);
|
||||
size = static_cast<long long>(ssize);
|
||||
}
|
||||
else {
|
||||
document->seek(0, File::End);
|
||||
size = document->tell();
|
||||
}
|
||||
}
|
||||
|
||||
// Writing constructor: Takes given information, calculates missing information
|
||||
// and writes everything to the file.
|
||||
// Tries to use void elements if available in the parent.
|
||||
ElementPrivate(ulli p_id, File *p_document, Element *p_parent,
|
||||
long long p_position, long long p_size) :
|
||||
id(p_id),
|
||||
position(p_position),
|
||||
size(p_size),
|
||||
populated(true), // It is a new element so we know, there are no children.
|
||||
parent(p_parent),
|
||||
document(p_document)
|
||||
{
|
||||
// header
|
||||
ByteVector content(createVInt(id, false).append(createVInt(size, true, false)));
|
||||
data = position + content.size();
|
||||
// space for children
|
||||
content.resize(static_cast<size_t>(data - position + size));
|
||||
|
||||
Element *freeSpace;
|
||||
if (!(freeSpace = searchVoid(content.size()))) {
|
||||
// We have to make room
|
||||
document->insert(content, position);
|
||||
// Update parents
|
||||
for(Element *current = parent; current->d->parent; current = current->d->parent) {
|
||||
current->d->size += content.size();
|
||||
// Create new header and write it.
|
||||
ByteVector parentHeader(createVInt(current->d->id, false).append(createVInt(current->d->size, true, false)));
|
||||
size_t oldHeaderSize = static_cast<size_t>(current->d->data - current->d->position);
|
||||
if(oldHeaderSize < parentHeader.size()) {
|
||||
ByteVector secondHeader(createVInt(current->d->id, false).append(createVInt(current->d->size)));
|
||||
if(oldHeaderSize == secondHeader.size()) {
|
||||
// Write the header where the old one was.
|
||||
document->seek(current->d->position);
|
||||
document->writeBlock(secondHeader);
|
||||
continue; // Very important here!
|
||||
}
|
||||
}
|
||||
// Insert the new header
|
||||
document->insert(parentHeader, current->d->position, oldHeaderSize);
|
||||
current->d->data = current->d->position + parentHeader.size();
|
||||
}
|
||||
}
|
||||
else {
|
||||
document->seek(freeSpace->d->position);
|
||||
if((freeSpace->d->size + freeSpace->d->data - freeSpace->d->position)
|
||||
== static_cast<long long>(content.size())) {
|
||||
// Write to file
|
||||
document->writeBlock(content);
|
||||
// Update parent
|
||||
for(List<Element *>::Iterator i = parent->d->children.begin();
|
||||
i != parent->d->children.end(); ++i) {
|
||||
if(freeSpace == *i)
|
||||
parent->d->children.erase(i);
|
||||
}
|
||||
delete freeSpace;
|
||||
}
|
||||
else {
|
||||
ulli newSize = freeSpace->d->size - content.size();
|
||||
ByteVector newVoid(createVInt(Void, false).append(createVInt(newSize, true, false)));
|
||||
|
||||
// Check if the original size of the size field was really 8 byte
|
||||
if (static_cast<long long>(newVoid.size()) != (freeSpace->d->data - freeSpace->d->position))
|
||||
newVoid = createVInt(Void, false).append(createVInt(newSize));
|
||||
// Update freeSpace
|
||||
freeSpace->d->size = newSize;
|
||||
freeSpace->d->data = freeSpace->d->position + newVoid.size();
|
||||
// Write to file
|
||||
document->writeBlock(
|
||||
newVoid.resize(static_cast<size_t>(newVoid.size() + newSize)).append(content));
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// public members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
EBML::Element::~Element()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
|
||||
EBML::Element::Element(EBML::File *document)
|
||||
: d(new EBML::Element::ElementPrivate(document))
|
||||
{
|
||||
}
|
||||
|
||||
EBML::Element *EBML::Element::getChild(EBML::ulli id)
|
||||
{
|
||||
populate();
|
||||
for(List<Element *>::Iterator i = d->children.begin(); i != d->children.end();
|
||||
++i) {
|
||||
if ((*i)->d->id == id)
|
||||
return *i;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
List<EBML::Element *> EBML::Element::getChildren(EBML::ulli id)
|
||||
{
|
||||
populate();
|
||||
List<Element *> result;
|
||||
for(List<Element *>::Iterator i = d->children.begin(); i != d->children.end();
|
||||
++i) {
|
||||
if ((*i)->d->id == id)
|
||||
result.append(*i);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
List<EBML::Element *> EBML::Element::getChildren()
|
||||
{
|
||||
populate();
|
||||
return d->children;
|
||||
}
|
||||
|
||||
EBML::Element *EBML::Element::getParent()
|
||||
{
|
||||
return d->parent;
|
||||
}
|
||||
|
||||
ByteVector EBML::Element::getAsBinary()
|
||||
{
|
||||
d->document->seek(d->data);
|
||||
return d->document->readBlock(static_cast<size_t>(d->size));
|
||||
}
|
||||
|
||||
String EBML::Element::getAsString()
|
||||
{
|
||||
return String(getAsBinary(), String::UTF8);
|
||||
}
|
||||
|
||||
long long EBML::Element::getAsInt()
|
||||
{
|
||||
// The debug note about returning 0 because of empty data is irrelevant. The
|
||||
// behavior is as expected.
|
||||
return getAsBinary().toInt64BE(0);
|
||||
}
|
||||
|
||||
EBML::ulli EBML::Element::getAsUnsigned()
|
||||
{
|
||||
// The debug note about returning 0 because of empty data is irrelevant. The
|
||||
// behavior is as expected.
|
||||
return static_cast<ulli>(getAsBinary().toInt64BE(0));
|
||||
}
|
||||
|
||||
long double EBML::Element::getAsFloat()
|
||||
{
|
||||
const ByteVector bin = getAsBinary();
|
||||
switch (bin.size())
|
||||
{
|
||||
case 4:
|
||||
return bin.toFloat32BE(0);
|
||||
case 8:
|
||||
return bin.toFloat64BE(0);
|
||||
case 10:
|
||||
return bin.toFloat80BE(0);
|
||||
default:
|
||||
debug("EBML::Element::getAsFloat() - Invalid data size. Returning 0.");
|
||||
return 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
EBML::Element *EBML::Element::addElement(EBML::ulli id)
|
||||
{
|
||||
Element *elem = new Element(
|
||||
new ElementPrivate(id, d->document, this, d->data + d->size, 0)
|
||||
);
|
||||
d->children.append(elem);
|
||||
return elem;
|
||||
}
|
||||
|
||||
EBML::Element *EBML::Element::addElement(EBML::ulli id, const ByteVector &binary)
|
||||
{
|
||||
Element *elem = new Element(
|
||||
new ElementPrivate(id, d->document, this, d->data + d->size, binary.size())
|
||||
);
|
||||
d->document->seek(elem->d->data);
|
||||
d->document->writeBlock(binary);
|
||||
d->children.append(elem);
|
||||
return elem;
|
||||
}
|
||||
|
||||
EBML::Element *EBML::Element::addElement(EBML::ulli id, const String &string)
|
||||
{
|
||||
return addElement(id, string.data(String::UTF8));
|
||||
}
|
||||
|
||||
EBML::Element *EBML::Element::addElement(EBML::ulli id, signed long long number)
|
||||
{
|
||||
return addElement(id, ByteVector::fromUInt64BE(number));
|
||||
}
|
||||
|
||||
EBML::Element *EBML::Element::addElement(EBML::ulli id, EBML::ulli number)
|
||||
{
|
||||
return addElement(id, ByteVector::fromUInt64BE(number));
|
||||
}
|
||||
|
||||
EBML::Element *EBML::Element::addElement(EBML::ulli id, long double number)
|
||||
{
|
||||
// Probably, we will never need this method.
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool EBML::Element::removeChildren(EBML::ulli id, bool useVoid)
|
||||
{
|
||||
bool result = false;
|
||||
for(List<Element *>::Iterator i = d->children.begin(); i != d->children.end(); ++i)
|
||||
if((*i)->d->id == id) {
|
||||
removeChild(*i, useVoid);
|
||||
result = true;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool EBML::Element::removeChildren(bool useVoid)
|
||||
{
|
||||
// Maybe a better implementation, because we probably create a lot of voids
|
||||
// in a row where a huge Void would be more appropriate.
|
||||
if (d->children.isEmpty())
|
||||
return false;
|
||||
|
||||
for(List<Element *>::Iterator i = d->children.begin(); i != d->children.end(); ++i)
|
||||
removeChild(*i, useVoid);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool EBML::Element::removeChild(Element *element, bool useVoid)
|
||||
{
|
||||
if (!d->children.contains(element))
|
||||
return false;
|
||||
|
||||
if(!useVoid || !element->d->makeVoid()) {
|
||||
d->document->removeBlock(element->d->position, static_cast<size_t>(element->d->size));
|
||||
// Update parents
|
||||
for(Element* current = this; current; current = current->d->parent)
|
||||
current->d->size -= element->d->size;
|
||||
// Update this element
|
||||
for(List<Element *>::Iterator i = d->children.begin(); i != d->children.end(); ++i)
|
||||
if(element == *i)
|
||||
d->children.erase(i);
|
||||
delete element;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void EBML::Element::setAsBinary(const ByteVector &binary)
|
||||
{
|
||||
// Maybe: Search for void element after this one
|
||||
d->document->insert(binary, d->data, static_cast<size_t>(d->size));
|
||||
}
|
||||
|
||||
void EBML::Element::setAsString(const String &string)
|
||||
{
|
||||
setAsBinary(string.data(String::UTF8));
|
||||
}
|
||||
|
||||
void EBML::Element::setAsInt(signed long long number)
|
||||
{
|
||||
setAsBinary(ByteVector::fromUInt64BE(number));
|
||||
}
|
||||
|
||||
void EBML::Element::setAsUnsigned(EBML::ulli number)
|
||||
{
|
||||
setAsBinary(ByteVector::fromUInt64BE(number));
|
||||
}
|
||||
|
||||
void EBML::Element::setAsFloat(long double)
|
||||
{
|
||||
// Probably, we will never need this method.
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// private members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void EBML::Element::populate()
|
||||
{
|
||||
if(!d->populated) {
|
||||
d->populated = true;
|
||||
long long end = d->data + d->size;
|
||||
|
||||
for(long long i = d->data; i < end;) {
|
||||
Element *elem = new Element(
|
||||
new ElementPrivate(d->document, this, i)
|
||||
);
|
||||
d->children.append(elem);
|
||||
i = elem->d->data + elem->d->size;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
EBML::Element::Element(EBML::Element::ElementPrivate *pe) : d(pe)
|
||||
{}
|
||||
276
taglib/ebml/ebmlelement.h
Normal file
276
taglib/ebml/ebmlelement.h
Normal file
@@ -0,0 +1,276 @@
|
||||
/***************************************************************************
|
||||
copyright : (C) 2013 by Sebastian Rachuj
|
||||
email : rachus@web.de
|
||||
***************************************************************************/
|
||||
|
||||
/***************************************************************************
|
||||
* This library is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU Lesser General Public License version *
|
||||
* 2.1 as published by the Free Software Foundation. *
|
||||
* *
|
||||
* This library is distributed in the hope that it will be useful, but *
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
||||
* Lesser General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Lesser General Public *
|
||||
* License along with this library; if not, write to the Free Software *
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
|
||||
* 02110-1301 USA *
|
||||
* *
|
||||
* Alternatively, this file is available under the Mozilla Public *
|
||||
* License Version 1.1. You may obtain a copy of the License at *
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef TAGLIB_EBMLELEMENT_H
|
||||
#define TAGLIB_EBMLELEMENT_H
|
||||
|
||||
#include "tlist.h"
|
||||
#include "tbytevector.h"
|
||||
#include "tstring.h"
|
||||
|
||||
#include "ebmlfile.h"
|
||||
|
||||
namespace TagLib {
|
||||
|
||||
namespace EBML {
|
||||
|
||||
/*!
|
||||
* Represents an element of the EBML. The only instance of this child, that
|
||||
* is directly used is the root element. Every other element is accessed
|
||||
* via pointers to the elements within the root element.
|
||||
*
|
||||
* Just create one root instance per file to prevent race conditions.
|
||||
*
|
||||
* Changes of the document tree will be directly written back to the file.
|
||||
* Invalid values (exceeding the maximal value defined in the RFC) will be
|
||||
* truncated.
|
||||
*
|
||||
* This class should not be used by library users since the proper file
|
||||
* class should handle the internals.
|
||||
*
|
||||
* NOTE: Currently does not adjust CRC32 values.
|
||||
*/
|
||||
class TAGLIB_EXPORT Element
|
||||
{
|
||||
public:
|
||||
//! Destroys the instance of the element.
|
||||
~Element();
|
||||
|
||||
/*!
|
||||
* Creates an root element using document.
|
||||
*/
|
||||
explicit Element(File *document);
|
||||
|
||||
/*!
|
||||
* Returns the first found child element with the given id. Returns a null
|
||||
* pointer if the child does not exist.
|
||||
*
|
||||
* Do not call this method if *this element is not a container element (see
|
||||
* corresponding DTD)
|
||||
*/
|
||||
Element *getChild(const ulli id);
|
||||
|
||||
/*!
|
||||
* Returns a list of all child elements with the given id. Returns an
|
||||
* empty list if no such element exists.
|
||||
*
|
||||
* Do not call this method if *this element is not a container element (see
|
||||
* corresponding DTD)
|
||||
*/
|
||||
List<Element *> getChildren(const ulli id);
|
||||
|
||||
/*!
|
||||
* Returns a list of every child elements available. Returns an empty list
|
||||
* if there are no children.
|
||||
*
|
||||
* Do not call this method if *this element is not a container element (see
|
||||
* corresponding DTD)
|
||||
*/
|
||||
List<Element *> getChildren();
|
||||
|
||||
/*!
|
||||
* Returns the parent element or null if no such element exists.
|
||||
*/
|
||||
Element *getParent();
|
||||
|
||||
/*!
|
||||
* Returns the raw content of the element.
|
||||
*/
|
||||
ByteVector getAsBinary();
|
||||
|
||||
/*!
|
||||
* Returns the content of this element interpreted as a string.
|
||||
*/
|
||||
String getAsString();
|
||||
|
||||
/*!
|
||||
* Returns the content of this element interpreted as an signed integer.
|
||||
*
|
||||
* Do not call this method if *this element is not an INT element (see
|
||||
* corresponding DTD)
|
||||
*/
|
||||
long long getAsInt();
|
||||
|
||||
/*!
|
||||
* Returns the content of this element interpreted as an unsigned integer.
|
||||
*
|
||||
* Do not call this method if *this element is not an UINT element (see
|
||||
* corresponding DTD)
|
||||
*/
|
||||
ulli getAsUnsigned();
|
||||
|
||||
/*!
|
||||
* Returns the content of this element interpreted as a floating point
|
||||
* type.
|
||||
*
|
||||
* Do not call this method if *this element is not an FLOAT element (see
|
||||
* corresponding DTD)
|
||||
*
|
||||
* NOTE: There are 10 byte floats defined, therefore we might need a long
|
||||
* double to store the value.
|
||||
*/
|
||||
long double getAsFloat();
|
||||
|
||||
/*!
|
||||
* Adds an empty element with given id to this element. Returns a pointer
|
||||
* to the new element.
|
||||
*
|
||||
* Do not call this method if *this element is not a container element (see
|
||||
* corresponding DTD)
|
||||
*/
|
||||
Element *addElement(ulli id);
|
||||
|
||||
/*!
|
||||
* Adds a new element, containing the given binary, to this element.
|
||||
* Returns a pointer to the new element.
|
||||
*
|
||||
* Do not call this method if *this element is not a container element (see
|
||||
* corresponding DTD)
|
||||
*/
|
||||
Element *addElement(ulli id, const ByteVector &binary);
|
||||
|
||||
/*!
|
||||
* Adds a new element, containing the given string, to this element.
|
||||
* Returns a pointer to the new element.
|
||||
*
|
||||
* Do not call this method if *this element is not a container element (see
|
||||
* corresponding DTD)
|
||||
*/
|
||||
Element *addElement(ulli id, const String &string);
|
||||
|
||||
/*!
|
||||
* Adds a new element, containing the given integer, to this element.
|
||||
* Returns a pointer to the new element.
|
||||
*
|
||||
* Do not call this method if *this element is not a container element (see
|
||||
* corresponding DTD)
|
||||
*/
|
||||
Element *addElement(ulli id, signed long long number);
|
||||
|
||||
/*!
|
||||
* Adds a new element, containing the given unsigned integer, to this element.
|
||||
* Returns a pointer to the new element.
|
||||
*
|
||||
* Do not call this method if *this element is not a container element (see
|
||||
* corresponding DTD)
|
||||
*/
|
||||
Element *addElement(ulli id, ulli number);
|
||||
|
||||
/*!
|
||||
* Adds a new element, containing the given floating point value, to this element.
|
||||
* Returns a pointer to the new element.
|
||||
*
|
||||
* Do not call this method if *this element is not a container element (see
|
||||
* corresponding DTD)
|
||||
*
|
||||
* This method is not implemented!
|
||||
*/
|
||||
Element *addElement(ulli id, long double number);
|
||||
|
||||
/*!
|
||||
* Removes all children with the given id. Returns false if there was no
|
||||
* such element.
|
||||
* If useVoid is true, the element will be changed to a void element.
|
||||
*
|
||||
* Do not call this method if *this element is not a container element (see
|
||||
* corresponding DTD)
|
||||
*
|
||||
* Every pointer to a removed element is invalidated.
|
||||
*/
|
||||
bool removeChildren(ulli id, bool useVoid = true);
|
||||
|
||||
/*!
|
||||
* Removes all children. Returns false if this element had no children.
|
||||
* If useVoid ist rue, the element will be changed to a void element.
|
||||
*
|
||||
* Do not call this method if *this element is not a container element (see
|
||||
* corresponding DTD)
|
||||
*
|
||||
* Every pointer to a removed element is invalidated.
|
||||
*/
|
||||
bool removeChildren(bool useVoid = true);
|
||||
|
||||
/*!
|
||||
* Removes the given element.
|
||||
* If useVoid is true, the element will be changed to a void element.
|
||||
*
|
||||
* Do not call this method if *this element is not a container element (see
|
||||
* corresponding DTD)
|
||||
*
|
||||
* The pointer to the given element is invalidated.
|
||||
*/
|
||||
bool removeChild(Element *element, bool useVoid = true);
|
||||
|
||||
/*!
|
||||
* Writes the given binary to this element.
|
||||
*/
|
||||
void setAsBinary(const ByteVector &binary);
|
||||
|
||||
/*!
|
||||
* Writes the given string to this element.
|
||||
*/
|
||||
void setAsString(const String &string);
|
||||
|
||||
/*!
|
||||
* Writes the given integer to this element.
|
||||
*/
|
||||
void setAsInt(signed long long number);
|
||||
|
||||
/*!
|
||||
* Writes the given unsigned integer to this element.
|
||||
*/
|
||||
void setAsUnsigned(ulli number);
|
||||
|
||||
/*!
|
||||
* Writes the given floating point variable to this element.
|
||||
*
|
||||
* This method is not implemented!
|
||||
*/
|
||||
void setAsFloat(long double number);
|
||||
|
||||
private:
|
||||
//! Non-copyable
|
||||
Element(const Element &);
|
||||
//! Non-copyable
|
||||
Element &operator=(const File &);
|
||||
|
||||
//! Lazy parsing. This method will be triggered when trying to access
|
||||
//! children.
|
||||
void populate();
|
||||
|
||||
class ElementPrivate;
|
||||
ElementPrivate *d;
|
||||
|
||||
//! Creates a new Element from an ElementPrivate. (The constructor takes
|
||||
//! ownership of the pointer and will delete it when the element is
|
||||
//! destroyed.
|
||||
Element(ElementPrivate *pe);
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
102
taglib/ebml/ebmlfile.cpp
Normal file
102
taglib/ebml/ebmlfile.cpp
Normal file
@@ -0,0 +1,102 @@
|
||||
/***************************************************************************
|
||||
copyright : (C) 2013 by Sebastian Rachuj
|
||||
email : rachus@web.de
|
||||
***************************************************************************/
|
||||
|
||||
/***************************************************************************
|
||||
* This library is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU Lesser General Public License version *
|
||||
* 2.1 as published by the Free Software Foundation. *
|
||||
* *
|
||||
* This library is distributed in the hope that it will be useful, but *
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
||||
* Lesser General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Lesser General Public *
|
||||
* License along with this library; if not, write to the Free Software *
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
|
||||
* 02110-1301 USA *
|
||||
* *
|
||||
* Alternatively, this file is available under the Mozilla Public *
|
||||
* License Version 1.1. You may obtain a copy of the License at *
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#include "ebmlelement.h"
|
||||
|
||||
using namespace TagLib;
|
||||
|
||||
class EBML::File::FilePrivate
|
||||
{
|
||||
public:
|
||||
explicit FilePrivate(File *document) : root(document)
|
||||
{
|
||||
}
|
||||
|
||||
// Performs a few basic checks and creates the FilePrivate if they were
|
||||
// successful.
|
||||
static FilePrivate *checkAndCreate(File* document)
|
||||
{
|
||||
document->seek(0);
|
||||
ByteVector magical = document->readBlock(4);
|
||||
if(static_cast<ulli>(magical.toUInt32BE(0)) != Header::EBML)
|
||||
return 0;
|
||||
FilePrivate *d = new FilePrivate(document);
|
||||
Element *head = d->root.getChild(Header::EBML);
|
||||
Element *p;
|
||||
if(!head ||
|
||||
!((p = head->getChild(Header::EBMLVersion)) && p->getAsUnsigned() == 1L) ||
|
||||
!((p = head->getChild(Header::EBMLReadVersion)) && p->getAsUnsigned() == 1L) ||
|
||||
// Actually 4 is the current maximum of the EBML spec, but we support up to 8
|
||||
!((p = head->getChild(Header::EBMLMaxIDWidth)) && p->getAsUnsigned() <= 8) ||
|
||||
!((p = head->getChild(Header::EBMLMaxSizeWidth)) && p->getAsUnsigned() <= 8)
|
||||
) {
|
||||
delete d;
|
||||
return 0;
|
||||
}
|
||||
return d;
|
||||
}
|
||||
|
||||
Element root;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// public members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
EBML::File::~File()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
|
||||
EBML::Element *EBML::File::getDocumentRoot()
|
||||
{
|
||||
if(!d && isValid())
|
||||
d = new FilePrivate(this);
|
||||
return &d->root;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// protected members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
EBML::File::File(FileName file) :
|
||||
TagLib::File(file)
|
||||
{
|
||||
if(isOpen()) {
|
||||
d = FilePrivate::checkAndCreate(this);
|
||||
if(!d)
|
||||
setValid(false);
|
||||
}
|
||||
}
|
||||
|
||||
EBML::File::File(IOStream *stream) :
|
||||
TagLib::File(stream)
|
||||
{
|
||||
if(isOpen()) {
|
||||
d = FilePrivate::checkAndCreate(this);
|
||||
if(!d)
|
||||
setValid(false);
|
||||
}
|
||||
}
|
||||
86
taglib/ebml/ebmlfile.h
Normal file
86
taglib/ebml/ebmlfile.h
Normal file
@@ -0,0 +1,86 @@
|
||||
/***************************************************************************
|
||||
copyright : (C) 2013 by Sebastian Rachuj
|
||||
email : rachus@web.de
|
||||
***************************************************************************/
|
||||
|
||||
/***************************************************************************
|
||||
* This library is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU Lesser General Public License version *
|
||||
* 2.1 as published by the Free Software Foundation. *
|
||||
* *
|
||||
* This library is distributed in the hope that it will be useful, but *
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
||||
* Lesser General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Lesser General Public *
|
||||
* License along with this library; if not, write to the Free Software *
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
|
||||
* 02110-1301 USA *
|
||||
* *
|
||||
* Alternatively, this file is available under the Mozilla Public *
|
||||
* License Version 1.1. You may obtain a copy of the License at *
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef TAGLIB_EBMLFILE_H
|
||||
#define TAGLIB_EBMLFILE_H
|
||||
|
||||
#include "taglib_export.h"
|
||||
#include "tfile.h"
|
||||
|
||||
#include "ebmlconstants.h"
|
||||
|
||||
namespace TagLib {
|
||||
|
||||
//! A namespace for the classes used by EBML-based metadata files
|
||||
namespace EBML {
|
||||
|
||||
class Element;
|
||||
|
||||
/*!
|
||||
* Represents an EBML file. It offers access to the root element which can
|
||||
* be used to obtain the necessary information and to change the file
|
||||
* according to changes.
|
||||
*/
|
||||
class TAGLIB_EXPORT File : public TagLib::File
|
||||
{
|
||||
public:
|
||||
//! Destroys the instance of the file.
|
||||
virtual ~File();
|
||||
|
||||
/*!
|
||||
* Returns a pointer to the document root element of the EBML file.
|
||||
*/
|
||||
Element *getDocumentRoot();
|
||||
|
||||
protected:
|
||||
/*!
|
||||
* Constructs an instance of an EBML file from \a file.
|
||||
*
|
||||
* This constructor is protected since an object should be created
|
||||
* through a specific subclass.
|
||||
*/
|
||||
explicit File(FileName file);
|
||||
|
||||
/*!
|
||||
* Constructs an instance of an EBML file from an IOStream.
|
||||
*
|
||||
* This constructor is protected since an object should be created
|
||||
* through a specific subclass.
|
||||
*/
|
||||
explicit File(IOStream *stream);
|
||||
|
||||
private:
|
||||
//! Non-copyable
|
||||
File(const File&);
|
||||
File &operator=(const File &);
|
||||
|
||||
class FilePrivate;
|
||||
FilePrivate *d;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
135
taglib/ebml/matroska/ebmlmatroskaaudio.cpp
Normal file
135
taglib/ebml/matroska/ebmlmatroskaaudio.cpp
Normal file
@@ -0,0 +1,135 @@
|
||||
/***************************************************************************
|
||||
copyright : (C) 2013 by Sebastian Rachuj
|
||||
email : rachus@web.de
|
||||
***************************************************************************/
|
||||
|
||||
/***************************************************************************
|
||||
* This library is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU Lesser General Public License version *
|
||||
* 2.1 as published by the Free Software Foundation. *
|
||||
* *
|
||||
* This library is distributed in the hope that it will be useful, but *
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
||||
* Lesser General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Lesser General Public *
|
||||
* License along with this library; if not, write to the Free Software *
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
|
||||
* 02110-1301 USA *
|
||||
* *
|
||||
* Alternatively, this file is available under the Mozilla Public *
|
||||
* License Version 1.1. You may obtain a copy of the License at *
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#include "ebmlmatroskaconstants.h"
|
||||
#include "ebmlmatroskaaudio.h"
|
||||
|
||||
using namespace TagLib;
|
||||
|
||||
class EBML::Matroska::AudioProperties::AudioPropertiesPrivate
|
||||
{
|
||||
public:
|
||||
// Constructor
|
||||
AudioPropertiesPrivate() :
|
||||
length(0),
|
||||
bitrate(0),
|
||||
channels(1),
|
||||
samplerate(8000) {}
|
||||
|
||||
// The length of the file
|
||||
int length;
|
||||
|
||||
// The bitrate
|
||||
int bitrate;
|
||||
|
||||
// The amount of channels
|
||||
int channels;
|
||||
|
||||
// The sample rate
|
||||
int samplerate;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// public members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
EBML::Matroska::AudioProperties::AudioProperties(File *document) :
|
||||
d(new AudioPropertiesPrivate())
|
||||
{
|
||||
read(document);
|
||||
}
|
||||
|
||||
EBML::Matroska::AudioProperties::~AudioProperties()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
|
||||
int EBML::Matroska::AudioProperties::length() const
|
||||
{
|
||||
return lengthInSeconds();
|
||||
}
|
||||
|
||||
int EBML::Matroska::AudioProperties::lengthInSeconds() const
|
||||
{
|
||||
return d->length / 1000;
|
||||
}
|
||||
|
||||
int EBML::Matroska::AudioProperties::lengthInMilliseconds() const
|
||||
{
|
||||
return d->length;
|
||||
}
|
||||
|
||||
int EBML::Matroska::AudioProperties::bitrate() const
|
||||
{
|
||||
return d->bitrate;
|
||||
}
|
||||
|
||||
int EBML::Matroska::AudioProperties::channels() const
|
||||
{
|
||||
return d->channels;
|
||||
}
|
||||
|
||||
int EBML::Matroska::AudioProperties::sampleRate() const
|
||||
{
|
||||
return d->samplerate;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// private members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void EBML::Matroska::AudioProperties::read(File *document)
|
||||
{
|
||||
Element *elem = document->getDocumentRoot()->getChild(Constants::Segment);
|
||||
Element *info = elem->getChild(Constants::SegmentInfo);
|
||||
Element *value;
|
||||
|
||||
if(info && (value = info->getChild(Constants::Duration))) {
|
||||
|
||||
const double length = value->getAsFloat() / 1000000.0;
|
||||
|
||||
if((value = info->getChild(Constants::TimecodeScale)))
|
||||
d->length = static_cast<int>(length * value->getAsUnsigned() + 0.5);
|
||||
else
|
||||
d->length = static_cast<int>(length * 1000000 + 0.5);
|
||||
}
|
||||
|
||||
info = elem->getChild(Constants::Tracks);
|
||||
if(!info || !(info = info->getChild(Constants::TrackEntry)) ||
|
||||
!(info = info->getChild(Constants::Audio))) {
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Dirty bitrate:
|
||||
if(d->length > 0)
|
||||
d->bitrate = static_cast<int>(document->length() * 8.0 / d->length + 0.5);
|
||||
|
||||
if((value = info->getChild(Constants::Channels)))
|
||||
d->channels = static_cast<int>(value->getAsUnsigned());
|
||||
|
||||
if((value = info->getChild(Constants::SamplingFrequency)))
|
||||
d->samplerate = static_cast<int>(value->getAsFloat());
|
||||
}
|
||||
92
taglib/ebml/matroska/ebmlmatroskaaudio.h
Normal file
92
taglib/ebml/matroska/ebmlmatroskaaudio.h
Normal file
@@ -0,0 +1,92 @@
|
||||
/***************************************************************************
|
||||
copyright : (C) 2013 by Sebastian Rachuj
|
||||
email : rachus@web.de
|
||||
***************************************************************************/
|
||||
|
||||
/***************************************************************************
|
||||
* This library is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU Lesser General Public License version *
|
||||
* 2.1 as published by the Free Software Foundation. *
|
||||
* *
|
||||
* This library is distributed in the hope that it will be useful, but *
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
||||
* Lesser General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Lesser General Public *
|
||||
* License along with this library; if not, write to the Free Software *
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
|
||||
* 02110-1301 USA *
|
||||
* *
|
||||
* Alternatively, this file is available under the Mozilla Public *
|
||||
* License Version 1.1. You may obtain a copy of the License at *
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef TAGLIB_EBMLMATROSKAAUDIO_H
|
||||
#define TAGLIB_EBMLMATROSKAAUDIO_H
|
||||
|
||||
#include "ebmlmatroskafile.h"
|
||||
|
||||
#include "audioproperties.h"
|
||||
|
||||
namespace TagLib {
|
||||
|
||||
namespace EBML {
|
||||
|
||||
namespace Matroska {
|
||||
|
||||
/*!
|
||||
* This class represents the audio properties of a matroska file.
|
||||
* Currently all information are read from the container format and
|
||||
* could be inexact.
|
||||
*/
|
||||
class TAGLIB_EXPORT AudioProperties : public TagLib::AudioProperties
|
||||
{
|
||||
public:
|
||||
//! Destructor
|
||||
virtual ~AudioProperties();
|
||||
|
||||
/*!
|
||||
* Constructs an instance from a file.
|
||||
*/
|
||||
explicit AudioProperties(File *document);
|
||||
|
||||
/*!
|
||||
* Returns the length of the file.
|
||||
*/
|
||||
virtual int length() const;
|
||||
virtual int lengthInSeconds() const;
|
||||
virtual int lengthInMilliseconds() const;
|
||||
|
||||
/*!
|
||||
* Returns the bit rate of the file. Since the container format does not
|
||||
* offer a proper value, it ist currently calculated by dividing the
|
||||
* file size by the length.
|
||||
*/
|
||||
virtual int bitrate() const;
|
||||
|
||||
/*!
|
||||
* Returns the amount of channels of the file.
|
||||
*/
|
||||
virtual int channels() const;
|
||||
|
||||
/*!
|
||||
* Returns the sample rate of the file.
|
||||
*/
|
||||
virtual int sampleRate() const;
|
||||
|
||||
private:
|
||||
void read(File *document);
|
||||
|
||||
class AudioPropertiesPrivate;
|
||||
AudioPropertiesPrivate *d;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
140
taglib/ebml/matroska/ebmlmatroskaconstants.h
Normal file
140
taglib/ebml/matroska/ebmlmatroskaconstants.h
Normal file
@@ -0,0 +1,140 @@
|
||||
/***************************************************************************
|
||||
copyright : (C) 2013 by Sebastian Rachuj
|
||||
email : rachus@web.de
|
||||
***************************************************************************/
|
||||
|
||||
/***************************************************************************
|
||||
* This library is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU Lesser General Public License version *
|
||||
* 2.1 as published by the Free Software Foundation. *
|
||||
* *
|
||||
* This library is distributed in the hope that it will be useful, but *
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
||||
* Lesser General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Lesser General Public *
|
||||
* License along with this library; if not, write to the Free Software *
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
|
||||
* 02110-1301 USA *
|
||||
* *
|
||||
* Alternatively, this file is available under the Mozilla Public *
|
||||
* License Version 1.1. You may obtain a copy of the License at *
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef TAGLIB_EBMLMATROSKACONSTANTS_H
|
||||
#define TAGLIB_EBMLMATROSKACONSTANTS_H
|
||||
|
||||
#include "ebmlconstants.h"
|
||||
#include "tstring.h"
|
||||
|
||||
namespace TagLib {
|
||||
|
||||
namespace EBML {
|
||||
|
||||
namespace Matroska {
|
||||
|
||||
namespace Constants {
|
||||
|
||||
//! ID of an Matroska segment.
|
||||
const ulli Segment = 0x18538067L;
|
||||
|
||||
//! ID of the tags element.
|
||||
const ulli Tags = 0x1254c367L;
|
||||
|
||||
//! ID of the tag element.
|
||||
const ulli Tag = 0x7373L;
|
||||
|
||||
//! ID of the targets element.
|
||||
const ulli Targets = 0x63c0L;
|
||||
|
||||
//! ID of the target type value element.
|
||||
const ulli TargetTypeValue = 0x68caL;
|
||||
|
||||
//! ID of the target type element.
|
||||
const ulli TargetType = 0x63caL;
|
||||
|
||||
//! ID of a simple tag element.
|
||||
const ulli SimpleTag = 0x67c8L;
|
||||
|
||||
//! ID of the tag name.
|
||||
const ulli TagName = 0x45a3L;
|
||||
|
||||
//! ID of the tag content.
|
||||
const ulli TagString = 0x4487L;
|
||||
|
||||
//! The DocType of a matroska file.
|
||||
const String DocTypeMatroska = "matroska";
|
||||
|
||||
//! The DocType of a WebM file.
|
||||
const String DocTypeWebM = "webm";
|
||||
|
||||
|
||||
|
||||
//! The TITLE entry
|
||||
const String TITLE = "TITLE";
|
||||
|
||||
//! The ARTIST entry
|
||||
const String ARTIST = "ARTIST";
|
||||
|
||||
//! The COMMENT entry
|
||||
const String COMMENT = "COMMENT";
|
||||
|
||||
//! The GENRE entry
|
||||
const String GENRE = "GENRE";
|
||||
|
||||
//! The DATE_RELEASE entry
|
||||
const String DATE_RELEASE = "DATE_RELEASE";
|
||||
|
||||
//! The PART_NUMBER entry
|
||||
const String PART_NUMBER = "PART_NUMBER";
|
||||
|
||||
//! The TargetTypeValue of the most common grouping level (e.g. album)
|
||||
const ulli MostCommonGroupingValue = 50;
|
||||
|
||||
//! The TargetTypeValue of the most common parts of a group (e.g. track)
|
||||
const ulli MostCommonPartValue = 30;
|
||||
|
||||
//! Name of the TargetType of an album.
|
||||
const String ALBUM = "ALBUM";
|
||||
|
||||
//! Name of the TargetType of a track.
|
||||
const String TRACK = "TRACK";
|
||||
|
||||
|
||||
|
||||
// For AudioProperties
|
||||
|
||||
//! ID of the Info block within the Segment.
|
||||
const ulli SegmentInfo = 0x1549a966L;
|
||||
|
||||
//! ID of the duration element.
|
||||
const ulli Duration = 0x4489L;
|
||||
|
||||
//! ID of TimecodeScale element.
|
||||
const ulli TimecodeScale = 0x2ad7b1L;
|
||||
|
||||
//! ID of the Tracks container
|
||||
const ulli Tracks = 0x1654ae6bL;
|
||||
|
||||
//! ID of a TrackEntry element.
|
||||
const ulli TrackEntry = 0xaeL;
|
||||
|
||||
//! ID of the Audio container.
|
||||
const ulli Audio = 0xe1L;
|
||||
|
||||
//! ID of the SamplingFrequency element.
|
||||
const ulli SamplingFrequency = 0xb5L;
|
||||
|
||||
//! ID of the Channels element.
|
||||
const ulli Channels = 0x9fL;
|
||||
|
||||
//! ID of the BitDepth element.
|
||||
const ulli BitDepth = 0x6264L;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
563
taglib/ebml/matroska/ebmlmatroskafile.cpp
Normal file
563
taglib/ebml/matroska/ebmlmatroskafile.cpp
Normal file
@@ -0,0 +1,563 @@
|
||||
/***************************************************************************
|
||||
copyright : (C) 2013 by Sebastian Rachuj
|
||||
email : rachus@web.de
|
||||
***************************************************************************/
|
||||
|
||||
/***************************************************************************
|
||||
* This library is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU Lesser General Public License version *
|
||||
* 2.1 as published by the Free Software Foundation. *
|
||||
* *
|
||||
* This library is distributed in the hope that it will be useful, but *
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
||||
* Lesser General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Lesser General Public *
|
||||
* License along with this library; if not, write to the Free Software *
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
|
||||
* 02110-1301 USA *
|
||||
* *
|
||||
* Alternatively, this file is available under the Mozilla Public *
|
||||
* License Version 1.1. You may obtain a copy of the License at *
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#include "ebmlmatroskaconstants.h"
|
||||
#include "ebmlmatroskaaudio.h"
|
||||
|
||||
#include "tpicturemap.h"
|
||||
#include "tpropertymap.h"
|
||||
|
||||
using namespace TagLib;
|
||||
|
||||
class EBML::Matroska::File::FilePrivate
|
||||
{
|
||||
public:
|
||||
// Returns true if simpleTag has a TagName and TagString child and writes
|
||||
// their contents into name and value.
|
||||
bool extractContent(Element *simpleTag, String &name, String &value)
|
||||
{
|
||||
Element *n = simpleTag->getChild(Constants::TagName);
|
||||
Element *v = simpleTag->getChild(Constants::TagString);
|
||||
if(!n || !v)
|
||||
return false;
|
||||
|
||||
name = n->getAsString();
|
||||
value = v->getAsString();
|
||||
return true;
|
||||
}
|
||||
|
||||
explicit FilePrivate(File *p_document) : tag(0), document(p_document)
|
||||
{
|
||||
// Just get the first segment, because "Typically a Matroska file is
|
||||
// composed of 1 segment."
|
||||
Element* elem = document->getDocumentRoot()->getChild(Constants::Segment);
|
||||
|
||||
// We take the first tags element (there shouldn't be more), if there is
|
||||
// non such element, consider the file as not compatible.
|
||||
if(!elem || !(elem = elem->getChild(Constants::Tags))) {
|
||||
document->setValid(false);
|
||||
return;
|
||||
}
|
||||
|
||||
// Load all Tag entries
|
||||
List<Element *> entries = elem->getChildren(Constants::Tag);
|
||||
for(List<Element *>::Iterator i = entries.begin(); i != entries.end(); ++i) {
|
||||
Element *target = (*i)->getChild(Constants::Targets);
|
||||
ulli ttvalue = 50; // 50 is default (see spec.)
|
||||
if(target && (target = target->getChild(Constants::TargetTypeValue)))
|
||||
ttvalue = target->getAsUnsigned();
|
||||
|
||||
// Load all SimpleTags
|
||||
PropertyMap tagEntries;
|
||||
List<Element *> simpleTags = (*i)->getChildren(Constants::SimpleTag);
|
||||
for(List<Element *>::Iterator j = simpleTags.begin(); j != simpleTags.end();
|
||||
++j) {
|
||||
String name, value;
|
||||
if(!extractContent(*j, name, value))
|
||||
continue;
|
||||
tagEntries.insert(name, StringList(value));
|
||||
}
|
||||
|
||||
tags.append(std::pair<PropertyMap, std::pair<Element *, ulli> >(tagEntries, std::pair<Element *, ulli>(*i, ttvalue)));
|
||||
}
|
||||
}
|
||||
|
||||
// Creates Tag and AudioProperties. Late creation because both require a fully
|
||||
// functional FilePrivate (well AudioProperties doesn't...)
|
||||
void lateCreate()
|
||||
{
|
||||
tag = new Tag(document);
|
||||
audio = new AudioProperties(document);
|
||||
}
|
||||
|
||||
// Checks the EBML header and creates the FilePrivate.
|
||||
static FilePrivate *checkAndCreate(File *document)
|
||||
{
|
||||
Element *elem = document->getDocumentRoot()->getChild(Header::EBML);
|
||||
Element *child = elem->getChild(Header::DocType);
|
||||
if(child) {
|
||||
String dt = child->getAsString();
|
||||
if (dt == Constants::DocTypeMatroska || dt == Constants::DocTypeWebM) {
|
||||
FilePrivate *fp = new FilePrivate(document);
|
||||
return fp;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// The tags with their Element and TargetTypeValue
|
||||
List<std::pair<PropertyMap, std::pair<Element *, ulli> > > tags;
|
||||
|
||||
// The tag
|
||||
Tag *tag;
|
||||
|
||||
// The audio properties
|
||||
AudioProperties *audio;
|
||||
|
||||
// The corresponding file.
|
||||
File *document;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// public members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
EBML::Matroska::File::~File()
|
||||
{
|
||||
if (d) {
|
||||
delete d->tag;
|
||||
delete d->audio;
|
||||
delete d;
|
||||
}
|
||||
}
|
||||
|
||||
EBML::Matroska::File::File(FileName file, bool, AudioProperties::ReadStyle) : EBML::File(file), d(0)
|
||||
{
|
||||
if(isValid() && isOpen()) {
|
||||
d = FilePrivate::checkAndCreate(this);
|
||||
if(!d)
|
||||
setValid(false);
|
||||
else
|
||||
d->lateCreate();
|
||||
}
|
||||
}
|
||||
|
||||
EBML::Matroska::File::File(IOStream *stream, bool, AudioProperties::ReadStyle) : EBML::File(stream), d(0)
|
||||
{
|
||||
if(isValid() && isOpen()) {
|
||||
d = FilePrivate::checkAndCreate(this);
|
||||
if(!d)
|
||||
setValid(false);
|
||||
else
|
||||
d->lateCreate();
|
||||
}
|
||||
}
|
||||
|
||||
Tag *EBML::Matroska::File::tag() const
|
||||
{
|
||||
return d->tag;
|
||||
}
|
||||
|
||||
PropertyMap EBML::Matroska::File::properties() const
|
||||
{
|
||||
List<std::pair<PropertyMap, std::pair<Element *, ulli> > >::Iterator best = d->tags.end();
|
||||
for(List<std::pair<PropertyMap, std::pair<Element *, ulli> > >::Iterator i = d->tags.begin();
|
||||
i != d->tags.end(); ++i) {
|
||||
if(best == d->tags.end() || best->second.second > i->second.second)
|
||||
best = i;
|
||||
}
|
||||
return best->first;
|
||||
}
|
||||
|
||||
PropertyMap EBML::Matroska::File::setProperties(const PropertyMap &properties)
|
||||
{
|
||||
List<std::pair<PropertyMap, std::pair<Element *, ulli> > >::Iterator best = d->tags.end();
|
||||
for(List<std::pair<PropertyMap, std::pair<Element *, ulli> > >::Iterator i = d->tags.begin();
|
||||
i != d->tags.end(); ++i) {
|
||||
if(best == d->tags.end() || best->second.second > i->second.second)
|
||||
best = i;
|
||||
}
|
||||
|
||||
std::pair<PropertyMap, std::pair<Element *, ulli> > replace(properties, best->second);
|
||||
d->tags.erase(best);
|
||||
d->tags.prepend(replace);
|
||||
|
||||
return PropertyMap();
|
||||
}
|
||||
|
||||
AudioProperties *EBML::Matroska::File::audioProperties() const
|
||||
{
|
||||
return d->audio;
|
||||
}
|
||||
|
||||
bool EBML::Matroska::File::save()
|
||||
{
|
||||
if(readOnly())
|
||||
return false;
|
||||
|
||||
// C++11 features would be nice: for(auto &i : d->tags) { /* ... */ }
|
||||
// Well, here we just iterate each extracted element.
|
||||
for(List<std::pair<PropertyMap, std::pair<Element *, ulli> > >::Iterator i = d->tags.begin();
|
||||
i != d->tags.end(); ++i) {
|
||||
|
||||
for(PropertyMap::Iterator j = i->first.begin(); j != i->first.end(); ++j) {
|
||||
|
||||
// No element? Create it!
|
||||
if(!i->second.first) {
|
||||
// Should be save, since we already checked, when creating the object.
|
||||
Element *container = d->document->getDocumentRoot()
|
||||
->getChild(Constants::Segment)->getChild(Constants::Tags);
|
||||
|
||||
// Create Targets container
|
||||
i->second.first = container->addElement(Constants::Tag);
|
||||
Element *target = i->second.first->addElement(Constants::Targets);
|
||||
|
||||
if(i->second.second == Constants::MostCommonPartValue)
|
||||
target->addElement(Constants::TargetType, Constants::TRACK);
|
||||
else if(i->second.second == Constants::MostCommonGroupingValue)
|
||||
target->addElement(Constants::TargetType, Constants::ALBUM);
|
||||
|
||||
target->addElement(Constants::TargetTypeValue, i->second.second);
|
||||
}
|
||||
|
||||
// Find entries
|
||||
List<Element *> simpleTags = i->second.first->getChildren(Constants::SimpleTag);
|
||||
StringList::Iterator str = j->second.begin();
|
||||
List<Element *>::Iterator k = simpleTags.begin();
|
||||
for(; k != simpleTags.end(); ++k) {
|
||||
|
||||
String name, value;
|
||||
if(!d->extractContent(*k, name, value))
|
||||
continue;
|
||||
|
||||
// Write entry from StringList
|
||||
if(name == j->first) {
|
||||
if(str == j->second.end()) {
|
||||
// We have all StringList elements but still found another element
|
||||
// with the same name? Let's delete it!
|
||||
i->second.first->removeChild(*k);
|
||||
}
|
||||
else {
|
||||
if(value != *str) {
|
||||
// extractContent already checked for availability
|
||||
(*k)->getChild(Constants::TagString)->setAsString(*str);
|
||||
}
|
||||
++str;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If we didn't write the complete StringList, we have to write the rest.
|
||||
for(; str != j->second.end(); ++str) {
|
||||
Element *stag = i->second.first->addElement(Constants::SimpleTag);
|
||||
stag->addElement(Constants::TagName, j->first);
|
||||
stag->addElement(Constants::TagString, *str);
|
||||
}
|
||||
}
|
||||
|
||||
// Finally, we have to find elements that are not in the PropertyMap and
|
||||
// remove them.
|
||||
List<Element *> simpleTags = i->second.first->getChildren(Constants::SimpleTag);
|
||||
for(List<Element *>::Iterator j = simpleTags.begin(); j != simpleTags.end(); ++j) {
|
||||
|
||||
String name, value;
|
||||
if(!d->extractContent(*j, name, value))
|
||||
continue;
|
||||
|
||||
if(i->first.find(name) == i->first.end()){
|
||||
i->second.first->removeChild(*j);}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Tag
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
class EBML::Matroska::File::Tag::TagPrivate
|
||||
{
|
||||
public:
|
||||
// Creates a TagPrivate instance
|
||||
explicit TagPrivate(File *p_document) :
|
||||
document(p_document),
|
||||
title(document->d->tags.end()),
|
||||
artist(document->d->tags.end()),
|
||||
album(document->d->tags.end()),
|
||||
comment(document->d->tags.end()),
|
||||
genre(document->d->tags.end()),
|
||||
year(document->d->tags.end()),
|
||||
track(document->d->tags.end())
|
||||
{
|
||||
|
||||
for(List<std::pair<PropertyMap, std::pair<Element *, ulli> > >::Iterator i =
|
||||
document->d->tags.begin(); i != document->d->tags.end(); ++i) {
|
||||
|
||||
// Just save it, if the title is more specific, or there is no title yet.
|
||||
if(i->first.find(Constants::TITLE) != i->first.end() &&
|
||||
(title == document->d->tags.end() ||
|
||||
title->second.second > i->second.second ||
|
||||
i->second.second == Constants::MostCommonPartValue)) {
|
||||
|
||||
title = i;
|
||||
}
|
||||
|
||||
// Same goes for artist.
|
||||
if(i->first.find(Constants::ARTIST) != i->first.end() &&
|
||||
(artist == document->d->tags.end() ||
|
||||
artist->second.second > i->second.second ||
|
||||
i->second.second == Constants::MostCommonPartValue)) {
|
||||
|
||||
artist = i;
|
||||
}
|
||||
|
||||
// Here, we also look for a title (the album title), but since we
|
||||
// specified the granularity, we have to search for it exactly.
|
||||
// Therefore it is possible, that title and album are the same (if only
|
||||
// the title of the album is given).
|
||||
if(i->first.find(Constants::TITLE) != i->first.end() &&
|
||||
i->second.second == Constants::MostCommonGroupingValue) {
|
||||
|
||||
album = i;
|
||||
}
|
||||
|
||||
// Again the same as title and artist.
|
||||
if(i->first.find(Constants::COMMENT) != i->first.end() &&
|
||||
(comment == document->d->tags.end() ||
|
||||
comment->second.second > i->second.second ||
|
||||
i->second.second == Constants::MostCommonPartValue)) {
|
||||
|
||||
comment = i;
|
||||
}
|
||||
|
||||
// Same goes for genre.
|
||||
if(i->first.find(Constants::GENRE) != i->first.end() &&
|
||||
(genre == document->d->tags.end() ||
|
||||
genre->second.second > i->second.second ||
|
||||
i->second.second == Constants::MostCommonPartValue)) {
|
||||
|
||||
genre = i;
|
||||
}
|
||||
|
||||
// And year (in our case: DATE_REALEASE)
|
||||
if(i->first.find(Constants::DATE_RELEASE) != i->first.end() &&
|
||||
(year == document->d->tags.end() ||
|
||||
year->second.second > i->second.second ||
|
||||
i->second.second == Constants::MostCommonPartValue)) {
|
||||
|
||||
year = i;
|
||||
}
|
||||
|
||||
// And track (in our case: PART_NUMBER)
|
||||
if(i->first.find(Constants::PART_NUMBER) != i->first.end() &&
|
||||
(track == document->d->tags.end() ||
|
||||
track->second.second > i->second.second ||
|
||||
i->second.second == Constants::MostCommonPartValue)) {
|
||||
|
||||
track = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Searches for the Tag with given TargetTypeValue (returns the first one)
|
||||
List<std::pair<PropertyMap, std::pair<Element *, ulli> > >::Iterator
|
||||
find(ulli ttv)
|
||||
{
|
||||
for(List<std::pair<PropertyMap, std::pair<Element *, ulli> > >::Iterator i =
|
||||
document->d->tags.begin(); i != document->d->tags.end(); ++i) {
|
||||
|
||||
if(i->second.second == ttv)
|
||||
return i;
|
||||
}
|
||||
return document->d->tags.end();
|
||||
}
|
||||
|
||||
// Updates the given information
|
||||
void update(
|
||||
List<std::pair<PropertyMap, std::pair<Element *, ulli> > >::Iterator t,
|
||||
const String &tagname,
|
||||
const String &s
|
||||
)
|
||||
{
|
||||
t->first.find(tagname)->second.front() = s;
|
||||
}
|
||||
|
||||
// Inserts a tag with given information
|
||||
void insert(const String &tagname, const ulli ttv, const String &s)
|
||||
{
|
||||
for(List<std::pair<PropertyMap, std::pair<Element *, ulli> > >::Iterator i =
|
||||
document->d->tags.begin(); i != document->d->tags.end(); ++i) {
|
||||
|
||||
if(i->second.second == ttv) {
|
||||
i->first.insert(tagname, StringList(s));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Not found? Create new!
|
||||
PropertyMap pm;
|
||||
pm.insert(tagname, StringList(s));
|
||||
document->d->tags.append(
|
||||
std::pair<PropertyMap, std::pair<Element *, ulli> >(pm,
|
||||
std::pair<Element *, ulli>(static_cast<Element *>(0), ttv)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// The PropertyMap from the Matroska::File
|
||||
File *document;
|
||||
|
||||
// Iterators to the tags.
|
||||
List<std::pair<PropertyMap, std::pair<Element *, ulli> > >::Iterator title;
|
||||
List<std::pair<PropertyMap, std::pair<Element *, ulli> > >::Iterator artist;
|
||||
List<std::pair<PropertyMap, std::pair<Element *, ulli> > >::Iterator album;
|
||||
List<std::pair<PropertyMap, std::pair<Element *, ulli> > >::Iterator comment;
|
||||
List<std::pair<PropertyMap, std::pair<Element *, ulli> > >::Iterator genre;
|
||||
List<std::pair<PropertyMap, std::pair<Element *, ulli> > >::Iterator year;
|
||||
List<std::pair<PropertyMap, std::pair<Element *, ulli> > >::Iterator track;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// public members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
EBML::Matroska::File::Tag::~Tag()
|
||||
{
|
||||
delete e;
|
||||
}
|
||||
|
||||
EBML::Matroska::File::Tag::Tag(EBML::Matroska::File *document) :
|
||||
e(new EBML::Matroska::File::Tag::TagPrivate(document))
|
||||
{
|
||||
}
|
||||
|
||||
String EBML::Matroska::File::Tag::title() const
|
||||
{
|
||||
if(e->title != e->document->d->tags.end())
|
||||
return e->title->first.find(Constants::TITLE)->second.front();
|
||||
else
|
||||
return String();
|
||||
}
|
||||
|
||||
String EBML::Matroska::File::Tag::artist() const
|
||||
{
|
||||
if(e->artist != e->document->d->tags.end())
|
||||
return e->artist->first.find(Constants::ARTIST)->second.front();
|
||||
else
|
||||
return String();
|
||||
}
|
||||
|
||||
String EBML::Matroska::File::Tag::album() const
|
||||
{
|
||||
if(e->album != e->document->d->tags.end())
|
||||
return e->album->first.find(Constants::TITLE)->second.front();
|
||||
else
|
||||
return String();
|
||||
}
|
||||
|
||||
PictureMap EBML::Matroska::File::Tag::pictures() const
|
||||
{
|
||||
return PictureMap();
|
||||
}
|
||||
|
||||
String EBML::Matroska::File::Tag::comment() const
|
||||
{
|
||||
if(e->comment != e->document->d->tags.end())
|
||||
return e->comment->first.find(Constants::COMMENT)->second.front();
|
||||
else
|
||||
return String();
|
||||
}
|
||||
|
||||
String EBML::Matroska::File::Tag::genre() const
|
||||
{
|
||||
if(e->genre != e->document->d->tags.end())
|
||||
return e->genre->first.find(Constants::GENRE)->second.front();
|
||||
else
|
||||
return String();
|
||||
}
|
||||
|
||||
unsigned int EBML::Matroska::File::Tag::year() const
|
||||
{
|
||||
if(e->year != e->document->d->tags.end())
|
||||
return e->year->first.find(Constants::DATE_RELEASE)->second.front().toInt();
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned int EBML::Matroska::File::Tag::track() const
|
||||
{
|
||||
if(e->track != e->document->d->tags.end())
|
||||
return e->track->first.find(Constants::PART_NUMBER)->second.front().toInt();
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
void EBML::Matroska::File::Tag::setTitle(const String &s)
|
||||
{
|
||||
if(e->title != e->document->d->tags.end())
|
||||
e->update(e->title, Constants::TITLE, s);
|
||||
else
|
||||
e->insert(Constants::TITLE, Constants::MostCommonPartValue, s);
|
||||
}
|
||||
|
||||
void EBML::Matroska::File::Tag::setArtist(const String &s)
|
||||
{
|
||||
if(e->artist != e->document->d->tags.end())
|
||||
e->update(e->artist, Constants::ARTIST, s);
|
||||
else
|
||||
e->insert(Constants::ARTIST, Constants::MostCommonPartValue, s);
|
||||
}
|
||||
|
||||
void EBML::Matroska::File::Tag::setAlbum(const String &s)
|
||||
{
|
||||
if(e->album != e->document->d->tags.end())
|
||||
e->update(e->album, Constants::TITLE, s);
|
||||
else
|
||||
e->insert(Constants::TITLE, Constants::MostCommonGroupingValue, s);
|
||||
}
|
||||
|
||||
void EBML::Matroska::File::Tag::setPictures(const PictureMap& p )
|
||||
{
|
||||
(void)p; // avoid warning for unused variable
|
||||
}
|
||||
|
||||
void EBML::Matroska::File::Tag::setComment(const String &s)
|
||||
{
|
||||
if(e->comment != e->document->d->tags.end())
|
||||
e->update(e->comment, Constants::COMMENT, s);
|
||||
else
|
||||
e->insert(Constants::COMMENT, Constants::MostCommonPartValue, s);
|
||||
}
|
||||
|
||||
void EBML::Matroska::File::Tag::setGenre(const String &s)
|
||||
{
|
||||
if(e->genre != e->document->d->tags.end())
|
||||
e->update(e->genre, Constants::GENRE, s);
|
||||
else
|
||||
e->insert(Constants::GENRE, Constants::MostCommonPartValue, s);
|
||||
}
|
||||
|
||||
void EBML::Matroska::File::Tag::setYear(unsigned int i)
|
||||
{
|
||||
String s = String::number(i);
|
||||
if(e->year != e->document->d->tags.end())
|
||||
e->update(e->year, Constants::DATE_RELEASE, s);
|
||||
else
|
||||
e->insert(Constants::DATE_RELEASE, Constants::MostCommonPartValue, s);
|
||||
}
|
||||
|
||||
void EBML::Matroska::File::Tag::setTrack(unsigned int i)
|
||||
{
|
||||
String s = String::number(i);
|
||||
if(e->track != e->document->d->tags.end())
|
||||
e->update(e->track, Constants::PART_NUMBER, s);
|
||||
else
|
||||
e->insert(Constants::PART_NUMBER, Constants::MostCommonPartValue, s);
|
||||
}
|
||||
228
taglib/ebml/matroska/ebmlmatroskafile.h
Normal file
228
taglib/ebml/matroska/ebmlmatroskafile.h
Normal file
@@ -0,0 +1,228 @@
|
||||
/***************************************************************************
|
||||
copyright : (C) 2013 by Sebastian Rachuj
|
||||
email : rachus@web.de
|
||||
***************************************************************************/
|
||||
|
||||
/***************************************************************************
|
||||
* This library is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU Lesser General Public License version *
|
||||
* 2.1 as published by the Free Software Foundation. *
|
||||
* *
|
||||
* This library is distributed in the hope that it will be useful, but *
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
||||
* Lesser General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Lesser General Public *
|
||||
* License along with this library; if not, write to the Free Software *
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
|
||||
* 02110-1301 USA *
|
||||
* *
|
||||
* Alternatively, this file is available under the Mozilla Public *
|
||||
* License Version 1.1. You may obtain a copy of the License at *
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
|
||||
#ifndef TAGLIB_EBMLMATROSKAFILE_H
|
||||
#define TAGLIB_EBMLMATROSKAFILE_H
|
||||
|
||||
#include "ebmlelement.h"
|
||||
#include "audioproperties.h"
|
||||
|
||||
namespace TagLib {
|
||||
|
||||
namespace EBML {
|
||||
|
||||
//! Implementation for reading Matroska tags.
|
||||
namespace Matroska {
|
||||
|
||||
/*!
|
||||
* Implements the TagLib::File API and offers access to the tags of the
|
||||
* matroska file.
|
||||
*/
|
||||
class TAGLIB_EXPORT File : public EBML::File
|
||||
{
|
||||
public:
|
||||
//! Destroys the instance of the file.
|
||||
virtual ~File();
|
||||
|
||||
/*!
|
||||
* Constructs a Matroska file from \a file. If \a readProperties is true the
|
||||
* file's audio properties will also be read using \a propertiesStyle. If
|
||||
* false, \a propertiesStyle is ignored.
|
||||
*
|
||||
* \note In the current implementation, both \a readProperties and
|
||||
* \a propertiesStyle are ignored. The audio properties are always
|
||||
* read.
|
||||
*/
|
||||
explicit File(FileName file, bool readProperties = true,
|
||||
AudioProperties::ReadStyle propertiesStyle = AudioProperties::Average);
|
||||
|
||||
/*!
|
||||
* Constructs a Matroska file from \a stream. If \a readProperties is true the
|
||||
* file's audio properties will also be read using \a propertiesStyle. If
|
||||
* false, \a propertiesStyle is ignored.
|
||||
*
|
||||
* \note In the current implementation, both \a readProperties and
|
||||
* \a propertiesStyle are ignored. The audio properties are always
|
||||
* read.
|
||||
*/
|
||||
explicit File(IOStream *stream, bool readproperties = true,
|
||||
AudioProperties::ReadStyle propertiesStyle = AudioProperties::Average);
|
||||
|
||||
/*!
|
||||
* Returns the pointer to a tag that allow access on common tags.
|
||||
*/
|
||||
virtual TagLib::Tag *tag() const;
|
||||
|
||||
/*!
|
||||
* Exports the tags to a PropertyMap. Due to the diversity of the
|
||||
* Matroska format (e.g. multiple media streams in one file, each with
|
||||
* its own tags), only the best fitting tags are taken into account.
|
||||
* There are no unsupported tags.
|
||||
*/
|
||||
virtual PropertyMap properties() const;
|
||||
|
||||
/*!
|
||||
* Sets the tags of the file to those specified in properties. The
|
||||
* returned PropertyMap is always empty.
|
||||
* Note: Only the best fitting tags are taken into account.
|
||||
*/
|
||||
virtual PropertyMap setProperties(const PropertyMap &properties);
|
||||
|
||||
/*!
|
||||
* Returns a pointer to this file's audio properties.
|
||||
*
|
||||
* I'm glad about not having a setAudioProperties method ;)
|
||||
*/
|
||||
virtual AudioProperties *audioProperties() const;
|
||||
|
||||
/*!
|
||||
* Saves the file. Returns true on success.
|
||||
*/
|
||||
bool save();
|
||||
|
||||
/*!
|
||||
* Offers access to a few common tag entries.
|
||||
*/
|
||||
class Tag : public TagLib::Tag
|
||||
{
|
||||
public:
|
||||
//! Destroys the tag.
|
||||
~Tag();
|
||||
|
||||
/*!
|
||||
* Creates a new Tag for Matroska files. The given properties are gained
|
||||
* by the Matroska::File.
|
||||
*/
|
||||
explicit Tag(File *document);
|
||||
|
||||
/*!
|
||||
* Returns the track name; if no track name is present in the tag
|
||||
* String::null will be returned.
|
||||
*/
|
||||
virtual String title() const;
|
||||
|
||||
/*!
|
||||
* Returns the artist name; if no artist name is present in the tag
|
||||
* String::null will be returned.
|
||||
*/
|
||||
virtual String artist() const;
|
||||
|
||||
/*!
|
||||
* Returns the album name; if no album name is present in the tag
|
||||
* String::null will be returned.
|
||||
*/
|
||||
virtual String album() const;
|
||||
|
||||
/*!
|
||||
* Returns a list of pictures; if no picture is present in the tag
|
||||
* an empty PictureMap is returned
|
||||
*/
|
||||
virtual PictureMap pictures() const;
|
||||
|
||||
/*!
|
||||
* Returns the track comment; if no comment is present in the tag
|
||||
* String::null will be returned.
|
||||
*/
|
||||
virtual String comment() const;
|
||||
|
||||
/*!
|
||||
* Returns the genre name; if no genre is present in the tag String::null
|
||||
* will be returned.
|
||||
*/
|
||||
virtual String genre() const;
|
||||
|
||||
/*!
|
||||
* Returns the year; if there is no year set, this will return 0.
|
||||
*/
|
||||
virtual unsigned int year() const;
|
||||
|
||||
/*!
|
||||
* Returns the track number; if there is no track number set, this will
|
||||
* return 0.
|
||||
*/
|
||||
virtual unsigned int track() const;
|
||||
|
||||
/*!
|
||||
* Sets the title to s. If s is String::null then this value will be
|
||||
* cleared.
|
||||
*/
|
||||
virtual void setTitle(const String &s);
|
||||
|
||||
/*!
|
||||
* Sets the artist to s. If s is String::null then this value will be
|
||||
* cleared.
|
||||
*/
|
||||
virtual void setArtist(const String &s);
|
||||
|
||||
/*!
|
||||
* Sets the album to s. If s is String::null then this value will be
|
||||
* cleared.
|
||||
*/
|
||||
virtual void setAlbum(const String &s);
|
||||
|
||||
/*!
|
||||
* Sets the picture map to p. If p is empty then this value will be
|
||||
* cleared
|
||||
*/
|
||||
virtual void setPictures(const PictureMap& p );
|
||||
|
||||
/*!
|
||||
* Sets the comment to s. If s is String::null then this value will be
|
||||
* cleared.
|
||||
*/
|
||||
virtual void setComment(const String &s);
|
||||
|
||||
/*!
|
||||
* Sets the genre to s. If s is String::null then this value will be
|
||||
* cleared.
|
||||
*/
|
||||
virtual void setGenre(const String &s);
|
||||
|
||||
/*!
|
||||
* Sets the year to i. If s is 0 then this value will be cleared.
|
||||
*/
|
||||
virtual void setYear(unsigned int i);
|
||||
|
||||
/*!
|
||||
* Sets the track to i. If s is 0 then this value will be cleared.
|
||||
*/
|
||||
virtual void setTrack(unsigned int i);
|
||||
|
||||
private:
|
||||
class TagPrivate;
|
||||
TagPrivate *e;
|
||||
};
|
||||
|
||||
private:
|
||||
class FilePrivate;
|
||||
FilePrivate *d;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -28,9 +28,10 @@
|
||||
***************************************************************************/
|
||||
|
||||
#include <tfile.h>
|
||||
#include <tfilestream.h>
|
||||
#include <tstring.h>
|
||||
#include <tdebug.h>
|
||||
#include <trefcounter.h>
|
||||
#include <tsmartptr.h>
|
||||
|
||||
#include "fileref.h"
|
||||
#include "asffile.h"
|
||||
@@ -51,6 +52,9 @@
|
||||
#include "s3mfile.h"
|
||||
#include "itfile.h"
|
||||
#include "xmfile.h"
|
||||
#include "dsffile.h"
|
||||
#include "dsdifffile.h"
|
||||
#include "ebmlmatroskafile.h"
|
||||
|
||||
using namespace TagLib;
|
||||
|
||||
@@ -59,49 +63,14 @@ namespace
|
||||
typedef List<const FileRef::FileTypeResolver *> ResolverList;
|
||||
ResolverList fileTypeResolvers;
|
||||
|
||||
// Templatized internal functions. T should be String or IOStream*.
|
||||
// Detect the file type by user-defined resolvers.
|
||||
|
||||
template <typename T>
|
||||
FileName toFileName(T arg)
|
||||
{
|
||||
debug("FileRef::toFileName<T>(): This version should never be called.");
|
||||
return FileName(L"");
|
||||
}
|
||||
|
||||
template <>
|
||||
FileName toFileName<IOStream *>(IOStream *arg)
|
||||
{
|
||||
return arg->name();
|
||||
}
|
||||
|
||||
template <>
|
||||
FileName toFileName<FileName>(FileName arg)
|
||||
{
|
||||
return arg;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
File *resolveFileType(T arg, bool readProperties,
|
||||
AudioProperties::ReadStyle style)
|
||||
{
|
||||
debug("FileRef::resolveFileType<T>(): This version should never be called.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
template <>
|
||||
File *resolveFileType<IOStream *>(IOStream *arg, bool readProperties,
|
||||
AudioProperties::ReadStyle style)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
template <>
|
||||
File *resolveFileType<FileName>(FileName arg, bool readProperties,
|
||||
AudioProperties::ReadStyle style)
|
||||
File *detectByResolvers(FileName fileName, bool readAudioProperties,
|
||||
AudioProperties::ReadStyle audioPropertiesStyle)
|
||||
{
|
||||
ResolverList::ConstIterator it = fileTypeResolvers.begin();
|
||||
for(; it != fileTypeResolvers.end(); ++it) {
|
||||
File *file = (*it)->createFile(arg, readProperties, style);
|
||||
File *file = (*it)->createFile(fileName, readAudioProperties, audioPropertiesStyle);
|
||||
if(file)
|
||||
return file;
|
||||
}
|
||||
@@ -109,23 +78,20 @@ namespace
|
||||
return 0;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
File* createInternal(T arg, bool readAudioProperties,
|
||||
AudioProperties::ReadStyle audioPropertiesStyle)
|
||||
{
|
||||
File *file = resolveFileType(arg, readAudioProperties, audioPropertiesStyle);
|
||||
if(file)
|
||||
return file;
|
||||
// Detect the file type based on the file extension.
|
||||
|
||||
File* detectByExtension(IOStream *stream, bool readAudioProperties,
|
||||
AudioProperties::ReadStyle audioPropertiesStyle)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
const String s = toFileName(arg).toString();
|
||||
const String s(stream->name().wstr());
|
||||
#else
|
||||
const String s(toFileName(arg));
|
||||
const String s(stream->name());
|
||||
#endif
|
||||
|
||||
String ext;
|
||||
const int pos = s.rfind(".");
|
||||
if(pos != -1)
|
||||
const size_t pos = s.rfind(".");
|
||||
if(pos != String::npos())
|
||||
ext = s.substr(pos + 1).upper();
|
||||
|
||||
// If this list is updated, the method defaultFileExtensions() should also be
|
||||
@@ -135,66 +101,129 @@ namespace
|
||||
if(ext.isEmpty())
|
||||
return 0;
|
||||
|
||||
// .oga can be any audio in the Ogg container. So leave it to content-based detection.
|
||||
|
||||
if(ext == "MP3")
|
||||
return new MPEG::File(arg, ID3v2::FrameFactory::instance(), readAudioProperties, audioPropertiesStyle);
|
||||
return new MPEG::File(stream, ID3v2::FrameFactory::instance(), readAudioProperties, audioPropertiesStyle);
|
||||
if(ext == "OGG")
|
||||
return new Ogg::Vorbis::File(arg, readAudioProperties, audioPropertiesStyle);
|
||||
if(ext == "OGA") {
|
||||
/* .oga can be any audio in the Ogg container. First try FLAC, then Vorbis. */
|
||||
File *file = new Ogg::FLAC::File(arg, readAudioProperties, audioPropertiesStyle);
|
||||
if(file->isValid())
|
||||
return file;
|
||||
delete file;
|
||||
return new Ogg::Vorbis::File(arg, readAudioProperties, audioPropertiesStyle);
|
||||
}
|
||||
return new Ogg::Vorbis::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
if(ext == "FLAC")
|
||||
return new FLAC::File(arg, ID3v2::FrameFactory::instance(), readAudioProperties, audioPropertiesStyle);
|
||||
return new FLAC::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
if(ext == "MPC")
|
||||
return new MPC::File(arg, readAudioProperties, audioPropertiesStyle);
|
||||
return new MPC::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
if(ext == "WV")
|
||||
return new WavPack::File(arg, readAudioProperties, audioPropertiesStyle);
|
||||
return new WavPack::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
if(ext == "SPX")
|
||||
return new Ogg::Speex::File(arg, readAudioProperties, audioPropertiesStyle);
|
||||
return new Ogg::Speex::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
if(ext == "OPUS")
|
||||
return new Ogg::Opus::File(arg, readAudioProperties, audioPropertiesStyle);
|
||||
return new Ogg::Opus::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
if(ext == "TTA")
|
||||
return new TrueAudio::File(arg, readAudioProperties, audioPropertiesStyle);
|
||||
return new TrueAudio::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
if(ext == "M4A" || ext == "M4R" || ext == "M4B" || ext == "M4P" || ext == "MP4" || ext == "3G2" || ext == "M4V")
|
||||
return new MP4::File(arg, readAudioProperties, audioPropertiesStyle);
|
||||
return new MP4::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
if(ext == "WMA" || ext == "ASF")
|
||||
return new ASF::File(arg, readAudioProperties, audioPropertiesStyle);
|
||||
return new ASF::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
if(ext == "AIF" || ext == "AIFF" || ext == "AFC" || ext == "AIFC")
|
||||
return new RIFF::AIFF::File(arg, readAudioProperties, audioPropertiesStyle);
|
||||
return new RIFF::AIFF::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
if(ext == "WAV")
|
||||
return new RIFF::WAV::File(arg, readAudioProperties, audioPropertiesStyle);
|
||||
return new RIFF::WAV::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
if(ext == "APE")
|
||||
return new APE::File(arg, readAudioProperties, audioPropertiesStyle);
|
||||
return new APE::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
// module, nst and wow are possible but uncommon extensions
|
||||
if(ext == "MOD" || ext == "MODULE" || ext == "NST" || ext == "WOW")
|
||||
return new Mod::File(arg, readAudioProperties, audioPropertiesStyle);
|
||||
return new Mod::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
if(ext == "S3M")
|
||||
return new S3M::File(arg, readAudioProperties, audioPropertiesStyle);
|
||||
return new S3M::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
if(ext == "IT")
|
||||
return new IT::File(arg, readAudioProperties, audioPropertiesStyle);
|
||||
return new IT::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
if(ext == "XM")
|
||||
return new XM::File(arg, readAudioProperties, audioPropertiesStyle);
|
||||
return new XM::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
if(ext == "DFF" || ext == "DSDIFF")
|
||||
return new DSDIFF::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
if(ext == "DSF")
|
||||
return new DSF::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
if (ext == "MKA" || ext == "MKV") {
|
||||
return new EBML::Matroska::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
class FileRef::FileRefPrivate : public RefCounter
|
||||
{
|
||||
public:
|
||||
FileRefPrivate(File *f) :
|
||||
RefCounter(),
|
||||
file(f) {}
|
||||
// Detect the file type based on the actual content of the stream.
|
||||
|
||||
~FileRefPrivate() {
|
||||
delete file;
|
||||
File *detectByContent(IOStream *stream, bool readAudioProperties,
|
||||
AudioProperties::ReadStyle audioPropertiesStyle)
|
||||
{
|
||||
File *file = 0;
|
||||
|
||||
if(MPEG::File::isSupported(stream))
|
||||
file = new MPEG::File(stream, ID3v2::FrameFactory::instance(), readAudioProperties, audioPropertiesStyle);
|
||||
else if(Ogg::Vorbis::File::isSupported(stream))
|
||||
file = new Ogg::Vorbis::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
else if(Ogg::FLAC::File::isSupported(stream))
|
||||
file = new Ogg::FLAC::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
else if(FLAC::File::isSupported(stream))
|
||||
file = new FLAC::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
else if(MPC::File::isSupported(stream))
|
||||
file = new MPC::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
else if(WavPack::File::isSupported(stream))
|
||||
file = new WavPack::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
else if(Ogg::Speex::File::isSupported(stream))
|
||||
file = new Ogg::Speex::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
else if(Ogg::Opus::File::isSupported(stream))
|
||||
file = new Ogg::Opus::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
else if(TrueAudio::File::isSupported(stream))
|
||||
file = new TrueAudio::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
else if(MP4::File::isSupported(stream))
|
||||
file = new MP4::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
else if(ASF::File::isSupported(stream))
|
||||
file = new ASF::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
else if(RIFF::AIFF::File::isSupported(stream))
|
||||
file = new RIFF::AIFF::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
else if(RIFF::WAV::File::isSupported(stream))
|
||||
file = new RIFF::WAV::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
else if(APE::File::isSupported(stream))
|
||||
file = new APE::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
else if(DSDIFF::File::isSupported(stream))
|
||||
file = new DSDIFF::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
else if(DSF::File::isSupported(stream))
|
||||
file = new DSF::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
|
||||
// isSupported() only does a quick check, so double check the file here.
|
||||
|
||||
if(file) {
|
||||
if(file->isValid())
|
||||
return file;
|
||||
else
|
||||
delete file;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
File *file;
|
||||
struct FileRefData
|
||||
{
|
||||
FileRefData() :
|
||||
file(0),
|
||||
stream(0) {}
|
||||
|
||||
~FileRefData() {
|
||||
delete file;
|
||||
delete stream;
|
||||
}
|
||||
|
||||
File *file;
|
||||
IOStream *stream;
|
||||
};
|
||||
}
|
||||
|
||||
class FileRef::FileRefPrivate
|
||||
{
|
||||
public:
|
||||
FileRefPrivate() :
|
||||
data(new FileRefData()) {}
|
||||
|
||||
SHARED_PTR<FileRefData> data;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -202,36 +231,37 @@ public:
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
FileRef::FileRef() :
|
||||
d(new FileRefPrivate(0))
|
||||
d(new FileRefPrivate())
|
||||
{
|
||||
}
|
||||
|
||||
FileRef::FileRef(FileName fileName, bool readAudioProperties,
|
||||
AudioProperties::ReadStyle audioPropertiesStyle) :
|
||||
d(new FileRefPrivate(createInternal(fileName, readAudioProperties, audioPropertiesStyle)))
|
||||
d(new FileRefPrivate())
|
||||
{
|
||||
parse(fileName, readAudioProperties, audioPropertiesStyle);
|
||||
}
|
||||
|
||||
FileRef::FileRef(IOStream* stream, bool readAudioProperties, AudioProperties::ReadStyle audioPropertiesStyle) :
|
||||
d(new FileRefPrivate(createInternal(stream, readAudioProperties, audioPropertiesStyle)))
|
||||
d(new FileRefPrivate())
|
||||
{
|
||||
parse(stream, readAudioProperties, audioPropertiesStyle);
|
||||
}
|
||||
|
||||
FileRef::FileRef(File *file) :
|
||||
d(new FileRefPrivate(file))
|
||||
d(new FileRefPrivate())
|
||||
{
|
||||
d->data->file = file;
|
||||
}
|
||||
|
||||
FileRef::FileRef(const FileRef &ref) :
|
||||
d(ref.d)
|
||||
d(new FileRefPrivate(*ref.d))
|
||||
{
|
||||
d->ref();
|
||||
}
|
||||
|
||||
FileRef::~FileRef()
|
||||
{
|
||||
if(d->deref())
|
||||
delete d;
|
||||
delete d;
|
||||
}
|
||||
|
||||
Tag *FileRef::tag() const
|
||||
@@ -240,7 +270,37 @@ Tag *FileRef::tag() const
|
||||
debug("FileRef::tag() - Called without a valid file.");
|
||||
return 0;
|
||||
}
|
||||
return d->file->tag();
|
||||
return d->data->file->tag();
|
||||
}
|
||||
|
||||
PropertyMap FileRef::properties() const
|
||||
{
|
||||
if(isNull()) {
|
||||
debug("FileRef::properties() - Called without a valid file.");
|
||||
return PropertyMap();
|
||||
}
|
||||
|
||||
return d->data->file->properties();
|
||||
}
|
||||
|
||||
void FileRef::removeUnsupportedProperties(const StringList& properties)
|
||||
{
|
||||
if(isNull()) {
|
||||
debug("FileRef::removeUnsupportedProperties() - Called without a valid file.");
|
||||
return;
|
||||
}
|
||||
|
||||
d->data->file->removeUnsupportedProperties(properties);
|
||||
}
|
||||
|
||||
PropertyMap FileRef::setProperties(const PropertyMap &properties)
|
||||
{
|
||||
if(isNull()) {
|
||||
debug("FileRef::setProperties() - Called without a valid file.");
|
||||
return PropertyMap();
|
||||
}
|
||||
|
||||
return d->data->file->setProperties(properties);
|
||||
}
|
||||
|
||||
AudioProperties *FileRef::audioProperties() const
|
||||
@@ -249,12 +309,12 @@ AudioProperties *FileRef::audioProperties() const
|
||||
debug("FileRef::audioProperties() - Called without a valid file.");
|
||||
return 0;
|
||||
}
|
||||
return d->file->audioProperties();
|
||||
return d->data->file->audioProperties();
|
||||
}
|
||||
|
||||
File *FileRef::file() const
|
||||
{
|
||||
return d->file;
|
||||
return d->data->file;
|
||||
}
|
||||
|
||||
bool FileRef::save()
|
||||
@@ -263,7 +323,7 @@ bool FileRef::save()
|
||||
debug("FileRef::save() - Called without a valid file.");
|
||||
return false;
|
||||
}
|
||||
return d->file->save();
|
||||
return d->data->file->save();
|
||||
}
|
||||
|
||||
const FileRef::FileTypeResolver *FileRef::addFileTypeResolver(const FileRef::FileTypeResolver *resolver) // static
|
||||
@@ -304,13 +364,23 @@ StringList FileRef::defaultFileExtensions()
|
||||
l.append("s3m");
|
||||
l.append("it");
|
||||
l.append("xm");
|
||||
l.append("dsf");
|
||||
l.append("dff");
|
||||
l.append("dsdiff"); // alias for "dff"
|
||||
l.append("mka");
|
||||
l.append("mkv");
|
||||
|
||||
return l;
|
||||
}
|
||||
|
||||
bool FileRef::isValid() const
|
||||
{
|
||||
return (d->data->file && d->data->file->isValid());
|
||||
}
|
||||
|
||||
bool FileRef::isNull() const
|
||||
{
|
||||
return (!d->file || !d->file->isValid());
|
||||
return !isValid();
|
||||
}
|
||||
|
||||
FileRef &FileRef::operator=(const FileRef &ref)
|
||||
@@ -328,16 +398,58 @@ void FileRef::swap(FileRef &ref)
|
||||
|
||||
bool FileRef::operator==(const FileRef &ref) const
|
||||
{
|
||||
return (ref.d->file == d->file);
|
||||
return (ref.d->data == d->data);
|
||||
}
|
||||
|
||||
bool FileRef::operator!=(const FileRef &ref) const
|
||||
{
|
||||
return (ref.d->file != d->file);
|
||||
return (ref.d->data != d->data);
|
||||
}
|
||||
|
||||
File *FileRef::create(FileName fileName, bool readAudioProperties,
|
||||
AudioProperties::ReadStyle audioPropertiesStyle) // static
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// private members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void FileRef::parse(FileName fileName, bool readAudioProperties,
|
||||
AudioProperties::ReadStyle audioPropertiesStyle)
|
||||
{
|
||||
return createInternal(fileName, readAudioProperties, audioPropertiesStyle);
|
||||
// Try user-defined resolvers.
|
||||
|
||||
d->data->file = detectByResolvers(fileName, readAudioProperties, audioPropertiesStyle);
|
||||
if(d->data->file)
|
||||
return;
|
||||
|
||||
// Try to resolve file types based on the file extension.
|
||||
|
||||
d->data->stream = new FileStream(fileName);
|
||||
d->data->file = detectByExtension(d->data->stream, readAudioProperties, audioPropertiesStyle);
|
||||
if(d->data->file)
|
||||
return;
|
||||
|
||||
// At last, try to resolve file types based on the actual content.
|
||||
|
||||
d->data->file = detectByContent(d->data->stream, readAudioProperties, audioPropertiesStyle);
|
||||
if(d->data->file)
|
||||
return;
|
||||
|
||||
// Stream have to be closed here if failed to resolve file types.
|
||||
|
||||
delete d->data->stream;
|
||||
d->data->stream = 0;
|
||||
}
|
||||
|
||||
void FileRef::parse(IOStream *stream, bool readAudioProperties,
|
||||
AudioProperties::ReadStyle audioPropertiesStyle)
|
||||
{
|
||||
// User-defined resolvers won't work with a stream.
|
||||
|
||||
// Try to resolve file types based on the file extension.
|
||||
|
||||
d->data->file = detectByExtension(stream, readAudioProperties, audioPropertiesStyle);
|
||||
if(d->data->file)
|
||||
return;
|
||||
|
||||
// At last, try to resolve file types based on the actual content of the file.
|
||||
|
||||
d->data->file = detectByContent(stream, readAudioProperties, audioPropertiesStyle);
|
||||
}
|
||||
|
||||
@@ -26,10 +26,10 @@
|
||||
#ifndef TAGLIB_FILEREF_H
|
||||
#define TAGLIB_FILEREF_H
|
||||
|
||||
#include "taglib_export.h"
|
||||
#include "tfile.h"
|
||||
#include "tstringlist.h"
|
||||
|
||||
#include "taglib_export.h"
|
||||
#include "tpropertymap.h"
|
||||
#include "audioproperties.h"
|
||||
|
||||
namespace TagLib {
|
||||
@@ -91,8 +91,9 @@ namespace TagLib {
|
||||
|
||||
class TAGLIB_EXPORT FileTypeResolver
|
||||
{
|
||||
TAGLIB_IGNORE_MISSING_DESTRUCTOR
|
||||
public:
|
||||
virtual ~FileTypeResolver() {}
|
||||
|
||||
/*!
|
||||
* This method must be overridden to provide an additional file type
|
||||
* resolver. If the resolver is able to determine the file type it should
|
||||
@@ -173,6 +174,41 @@ namespace TagLib {
|
||||
*/
|
||||
Tag *tag() const;
|
||||
|
||||
/*!
|
||||
* Exports the tags of the file as dictionary mapping (human readable) tag
|
||||
* names (uppercase Strings) to StringLists of tag values. Calls the according
|
||||
* specialization in the File subclasses.
|
||||
* For each metadata object of the file that could not be parsed into the PropertyMap
|
||||
* format, the returend map's unsupportedData() list will contain one entry identifying
|
||||
* that object (e.g. the frame type for ID3v2 tags). Use removeUnsupportedProperties()
|
||||
* to remove (a subset of) them.
|
||||
* For files that contain more than one tag (e.g. an MP3 with both an ID3v1 and an ID3v2
|
||||
* tag) only the most "modern" one will be exported (ID3v2 in this case).
|
||||
*/
|
||||
PropertyMap properties() const;
|
||||
|
||||
/*!
|
||||
* Removes unsupported properties, or a subset of them, from the file's metadata.
|
||||
* The parameter \a properties must contain only entries from
|
||||
* properties().unsupportedData().
|
||||
*/
|
||||
void removeUnsupportedProperties(const StringList& properties);
|
||||
|
||||
/*!
|
||||
* Sets the tags of this File to those specified in \a properties. Calls the
|
||||
* according specialization method in the subclasses of File to do the translation
|
||||
* into the format-specific details.
|
||||
* If some value(s) could not be written imported to the specific metadata format,
|
||||
* the returned PropertyMap will contain those value(s). Otherwise it will be empty,
|
||||
* indicating that no problems occured.
|
||||
* With file types that support several tag formats (for instance, MP3 files can have
|
||||
* ID3v1, ID3v2, and APEv2 tags), this function will create the most appropriate one
|
||||
* (ID3v2 for MP3 files). Older formats will be updated as well, if they exist, but won't
|
||||
* be taken into account for the return value of this function.
|
||||
* See the documentation of the subclass implementations for detailed descriptions.
|
||||
*/
|
||||
PropertyMap setProperties(const PropertyMap &properties);
|
||||
|
||||
/*!
|
||||
* Returns the audio properties for this FileRef. If no audio properties
|
||||
* were read then this will returns a null pointer.
|
||||
@@ -233,8 +269,17 @@ namespace TagLib {
|
||||
*/
|
||||
static StringList defaultFileExtensions();
|
||||
|
||||
/*!
|
||||
* Returns true if the file is open and readable.
|
||||
*
|
||||
* \note Just a negative of isNull().
|
||||
*/
|
||||
bool isValid() const;
|
||||
|
||||
/*!
|
||||
* Returns true if the file (and as such other pointers) are null.
|
||||
*
|
||||
* \note Just a negative of isValid().
|
||||
*/
|
||||
bool isNull() const;
|
||||
|
||||
@@ -259,23 +304,10 @@ namespace TagLib {
|
||||
*/
|
||||
bool operator!=(const FileRef &ref) const;
|
||||
|
||||
/*!
|
||||
* A simple implementation of file type guessing. If \a readAudioProperties
|
||||
* is true then the audio properties will be read using
|
||||
* \a audioPropertiesStyle. If \a readAudioProperties is false then
|
||||
* \a audioPropertiesStyle will be ignored.
|
||||
*
|
||||
* \note You generally shouldn't use this method, but instead the constructor
|
||||
* directly.
|
||||
*
|
||||
* \deprecated
|
||||
*/
|
||||
static File *create(FileName fileName,
|
||||
bool readAudioProperties = true,
|
||||
AudioProperties::ReadStyle audioPropertiesStyle = AudioProperties::Average);
|
||||
|
||||
|
||||
private:
|
||||
void parse(FileName fileName, bool readAudioProperties, AudioProperties::ReadStyle audioPropertiesStyle);
|
||||
void parse(IOStream *stream, bool readAudioProperties, AudioProperties::ReadStyle audioPropertiesStyle);
|
||||
|
||||
class FileRefPrivate;
|
||||
FileRefPrivate *d;
|
||||
};
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
#include <tagunion.h>
|
||||
#include <tpropertymap.h>
|
||||
#include <tagutils.h>
|
||||
#include <tsmartptr.h>
|
||||
|
||||
#include <id3v2header.h>
|
||||
#include <id3v2tag.h>
|
||||
@@ -45,70 +46,82 @@ using namespace TagLib;
|
||||
|
||||
namespace
|
||||
{
|
||||
typedef List<FLAC::MetadataBlock *> BlockList;
|
||||
typedef List<SHARED_PTR<FLAC::MetadataBlock> > BlockList;
|
||||
typedef BlockList::Iterator BlockIterator;
|
||||
typedef BlockList::Iterator BlockConstIterator;
|
||||
|
||||
enum { FlacXiphIndex = 0, FlacID3v2Index = 1, FlacID3v1Index = 2 };
|
||||
|
||||
const long MinPaddingLength = 4096;
|
||||
const long MaxPaddingLegnth = 1024 * 1024;
|
||||
const long long MinPaddingLength = 4096;
|
||||
const long long MaxPaddingLegnth = 1024 * 1024;
|
||||
|
||||
const char LastBlockFlag = '\x80';
|
||||
}
|
||||
|
||||
namespace TagLib
|
||||
{
|
||||
namespace FLAC
|
||||
{
|
||||
// Enables BlockList::find() to take raw pointers.
|
||||
|
||||
bool operator==(SHARED_PTR<MetadataBlock> lhs, MetadataBlock *rhs)
|
||||
{
|
||||
return lhs.get() == rhs;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class FLAC::File::FilePrivate
|
||||
{
|
||||
public:
|
||||
FilePrivate(const ID3v2::FrameFactory *frameFactory = ID3v2::FrameFactory::instance()) :
|
||||
ID3v2FrameFactory(frameFactory),
|
||||
explicit FilePrivate(const ID3v2::FrameFactory *frameFactory) :
|
||||
ID3v2FrameFactory(ID3v2::FrameFactory::instance()),
|
||||
ID3v2Location(-1),
|
||||
ID3v2OriginalSize(0),
|
||||
ID3v1Location(-1),
|
||||
properties(0),
|
||||
flacStart(0),
|
||||
streamStart(0),
|
||||
scanned(false)
|
||||
{
|
||||
blocks.setAutoDelete(true);
|
||||
}
|
||||
|
||||
~FilePrivate()
|
||||
{
|
||||
delete properties;
|
||||
if(frameFactory)
|
||||
ID3v2FrameFactory = frameFactory;
|
||||
}
|
||||
|
||||
const ID3v2::FrameFactory *ID3v2FrameFactory;
|
||||
long ID3v2Location;
|
||||
long ID3v2OriginalSize;
|
||||
long long ID3v2Location;
|
||||
long long ID3v2OriginalSize;
|
||||
|
||||
long ID3v1Location;
|
||||
long long ID3v1Location;
|
||||
|
||||
TagUnion tag;
|
||||
TripleTagUnion tag;
|
||||
|
||||
Properties *properties;
|
||||
SCOPED_PTR<AudioProperties> properties;
|
||||
ByteVector xiphCommentData;
|
||||
BlockList blocks;
|
||||
|
||||
long flacStart;
|
||||
long streamStart;
|
||||
long long flacStart;
|
||||
long long streamStart;
|
||||
bool scanned;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// static members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
bool FLAC::File::isSupported(IOStream *stream)
|
||||
{
|
||||
// A FLAC file has an ID "fLaC" somewhere. An ID3v2 tag may precede.
|
||||
|
||||
const ByteVector buffer = Utils::readHeader(stream, bufferSize(), true);
|
||||
return (buffer.find("fLaC") != ByteVector::npos());
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// public members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
FLAC::File::File(FileName file, bool readProperties, Properties::ReadStyle) :
|
||||
TagLib::File(file),
|
||||
d(new FilePrivate())
|
||||
{
|
||||
if(isOpen())
|
||||
read(readProperties);
|
||||
}
|
||||
|
||||
FLAC::File::File(FileName file, ID3v2::FrameFactory *frameFactory,
|
||||
bool readProperties, Properties::ReadStyle) :
|
||||
FLAC::File::File(FileName file, bool readProperties, AudioProperties::ReadStyle,
|
||||
ID3v2::FrameFactory *frameFactory) :
|
||||
TagLib::File(file),
|
||||
d(new FilePrivate(frameFactory))
|
||||
{
|
||||
@@ -116,8 +129,8 @@ FLAC::File::File(FileName file, ID3v2::FrameFactory *frameFactory,
|
||||
read(readProperties);
|
||||
}
|
||||
|
||||
FLAC::File::File(IOStream *stream, ID3v2::FrameFactory *frameFactory,
|
||||
bool readProperties, Properties::ReadStyle) :
|
||||
FLAC::File::File(IOStream *stream, bool readProperties, AudioProperties::ReadStyle,
|
||||
ID3v2::FrameFactory *frameFactory) :
|
||||
TagLib::File(stream),
|
||||
d(new FilePrivate(frameFactory))
|
||||
{
|
||||
@@ -135,24 +148,14 @@ TagLib::Tag *FLAC::File::tag() const
|
||||
return &d->tag;
|
||||
}
|
||||
|
||||
PropertyMap FLAC::File::properties() const
|
||||
{
|
||||
return d->tag.properties();
|
||||
}
|
||||
|
||||
void FLAC::File::removeUnsupportedProperties(const StringList &unsupported)
|
||||
{
|
||||
d->tag.removeUnsupportedProperties(unsupported);
|
||||
}
|
||||
|
||||
PropertyMap FLAC::File::setProperties(const PropertyMap &properties)
|
||||
{
|
||||
return xiphComment(true)->setProperties(properties);
|
||||
}
|
||||
|
||||
FLAC::Properties *FLAC::File::audioProperties() const
|
||||
FLAC::AudioProperties *FLAC::File::audioProperties() const
|
||||
{
|
||||
return d->properties;
|
||||
return d->properties.get();
|
||||
}
|
||||
|
||||
bool FLAC::File::save()
|
||||
@@ -168,8 +171,8 @@ bool FLAC::File::save()
|
||||
}
|
||||
|
||||
// Create new vorbis comments
|
||||
|
||||
Tag::duplicate(&d->tag, xiphComment(true), false);
|
||||
if(!hasXiphComment())
|
||||
Tag::duplicate(&d->tag, xiphComment(true), false);
|
||||
|
||||
d->xiphCommentData = xiphComment()->render(false);
|
||||
|
||||
@@ -178,30 +181,29 @@ bool FLAC::File::save()
|
||||
for(BlockIterator it = d->blocks.begin(); it != d->blocks.end(); ++it) {
|
||||
if((*it)->code() == MetadataBlock::VorbisComment) {
|
||||
// Set the new Vorbis Comment block
|
||||
delete *it;
|
||||
d->blocks.erase(it);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
d->blocks.append(new UnknownMetadataBlock(MetadataBlock::VorbisComment, d->xiphCommentData));
|
||||
d->blocks.append(SHARED_PTR<MetadataBlock>(
|
||||
new UnknownMetadataBlock(MetadataBlock::VorbisComment, d->xiphCommentData)));
|
||||
|
||||
// Render data for the metadata blocks
|
||||
|
||||
ByteVector data;
|
||||
for(BlockConstIterator it = d->blocks.begin(); it != d->blocks.end(); ++it) {
|
||||
ByteVector blockData = (*it)->render();
|
||||
ByteVector blockHeader = ByteVector::fromUInt(blockData.size());
|
||||
ByteVector blockHeader = ByteVector::fromUInt32BE(blockData.size());
|
||||
blockHeader[0] = (*it)->code();
|
||||
data.append(blockHeader);
|
||||
data.append(blockData);
|
||||
}
|
||||
|
||||
// Compute the amount of padding, and append that to data.
|
||||
// TODO: Should be calculated in offset_t in taglib2.
|
||||
|
||||
long originalLength = d->streamStart - d->flacStart;
|
||||
long paddingLength = originalLength - data.size() - 4;
|
||||
long long originalLength = d->streamStart - d->flacStart;
|
||||
long long paddingLength = originalLength - data.size() - 4;
|
||||
|
||||
if(paddingLength <= 0) {
|
||||
paddingLength = MinPaddingLength;
|
||||
@@ -209,7 +211,7 @@ bool FLAC::File::save()
|
||||
else {
|
||||
// Padding won't increase beyond 1% of the file size or 1MB.
|
||||
|
||||
long threshold = length() / 100;
|
||||
long long threshold = length() / 100;
|
||||
threshold = std::max(threshold, MinPaddingLength);
|
||||
threshold = std::min(threshold, MaxPaddingLegnth);
|
||||
|
||||
@@ -217,14 +219,14 @@ bool FLAC::File::save()
|
||||
paddingLength = MinPaddingLength;
|
||||
}
|
||||
|
||||
ByteVector paddingHeader = ByteVector::fromUInt(paddingLength);
|
||||
ByteVector paddingHeader = ByteVector::fromUInt32BE(static_cast<unsigned int>(paddingLength));
|
||||
paddingHeader[0] = static_cast<char>(MetadataBlock::Padding | LastBlockFlag);
|
||||
data.append(paddingHeader);
|
||||
data.resize(static_cast<unsigned int>(data.size() + paddingLength));
|
||||
data.resize(static_cast<size_t>(data.size() + paddingLength));
|
||||
|
||||
// Write the data to the file
|
||||
|
||||
insert(data, d->flacStart, originalLength);
|
||||
insert(data, d->flacStart, static_cast<size_t>(originalLength));
|
||||
|
||||
d->streamStart += (static_cast<long>(data.size()) - originalLength);
|
||||
|
||||
@@ -241,7 +243,7 @@ bool FLAC::File::save()
|
||||
d->ID3v2Location = 0;
|
||||
|
||||
data = ID3v2Tag()->render();
|
||||
insert(data, d->ID3v2Location, d->ID3v2OriginalSize);
|
||||
insert(data, d->ID3v2Location, static_cast<size_t>(d->ID3v2OriginalSize));
|
||||
|
||||
d->flacStart += (static_cast<long>(data.size()) - d->ID3v2OriginalSize);
|
||||
d->streamStart += (static_cast<long>(data.size()) - d->ID3v2OriginalSize);
|
||||
@@ -256,7 +258,7 @@ bool FLAC::File::save()
|
||||
// ID3v2 tag is empty. Remove the old one.
|
||||
|
||||
if(d->ID3v2Location >= 0) {
|
||||
removeBlock(d->ID3v2Location, d->ID3v2OriginalSize);
|
||||
removeBlock(d->ID3v2Location, static_cast<size_t>(d->ID3v2OriginalSize));
|
||||
|
||||
d->flacStart -= d->ID3v2OriginalSize;
|
||||
d->streamStart -= d->ID3v2OriginalSize;
|
||||
@@ -298,11 +300,7 @@ bool FLAC::File::save()
|
||||
|
||||
ID3v2::Tag *FLAC::File::ID3v2Tag(bool create)
|
||||
{
|
||||
if(!create || d->tag[FlacID3v2Index])
|
||||
return static_cast<ID3v2::Tag *>(d->tag[FlacID3v2Index]);
|
||||
|
||||
d->tag.set(FlacID3v2Index, new ID3v2::Tag);
|
||||
return static_cast<ID3v2::Tag *>(d->tag[FlacID3v2Index]);
|
||||
return d->tag.access<ID3v2::Tag>(FlacID3v2Index, create);
|
||||
}
|
||||
|
||||
ID3v1::Tag *FLAC::File::ID3v1Tag(bool create)
|
||||
@@ -315,28 +313,11 @@ Ogg::XiphComment *FLAC::File::xiphComment(bool create)
|
||||
return d->tag.access<Ogg::XiphComment>(FlacXiphIndex, create);
|
||||
}
|
||||
|
||||
void FLAC::File::setID3v2FrameFactory(const ID3v2::FrameFactory *factory)
|
||||
{
|
||||
d->ID3v2FrameFactory = factory;
|
||||
}
|
||||
|
||||
ByteVector FLAC::File::streamInfoData()
|
||||
{
|
||||
debug("FLAC::File::streamInfoData() -- This function is obsolete. Returning an empty ByteVector.");
|
||||
return ByteVector();
|
||||
}
|
||||
|
||||
long FLAC::File::streamLength()
|
||||
{
|
||||
debug("FLAC::File::streamLength() -- This function is obsolete. Returning zero.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
List<FLAC::Picture *> FLAC::File::pictureList()
|
||||
{
|
||||
List<Picture *> pictures;
|
||||
for(BlockConstIterator it = d->blocks.begin(); it != d->blocks.end(); ++it) {
|
||||
Picture *picture = dynamic_cast<Picture *>(*it);
|
||||
Picture *picture = dynamic_cast<Picture *>(it->get());
|
||||
if(picture) {
|
||||
pictures.append(picture);
|
||||
}
|
||||
@@ -346,24 +327,20 @@ List<FLAC::Picture *> FLAC::File::pictureList()
|
||||
|
||||
void FLAC::File::addPicture(Picture *picture)
|
||||
{
|
||||
d->blocks.append(picture);
|
||||
d->blocks.append(SHARED_PTR<Picture>(picture));
|
||||
}
|
||||
|
||||
void FLAC::File::removePicture(Picture *picture, bool del)
|
||||
void FLAC::File::removePicture(Picture *picture)
|
||||
{
|
||||
BlockIterator it = d->blocks.find(picture);
|
||||
if(it != d->blocks.end())
|
||||
d->blocks.erase(it);
|
||||
|
||||
if(del)
|
||||
delete picture;
|
||||
}
|
||||
|
||||
void FLAC::File::removePictures()
|
||||
{
|
||||
for(BlockIterator it = d->blocks.begin(); it != d->blocks.end(); ) {
|
||||
if(dynamic_cast<Picture *>(*it)) {
|
||||
delete *it;
|
||||
if(dynamic_cast<Picture *>(it->get())) {
|
||||
it = d->blocks.erase(it);
|
||||
}
|
||||
else {
|
||||
@@ -441,14 +418,14 @@ void FLAC::File::read(bool readProperties)
|
||||
|
||||
const ByteVector infoData = d->blocks.front()->render();
|
||||
|
||||
long streamLength;
|
||||
long long streamLength;
|
||||
|
||||
if(d->ID3v1Location >= 0)
|
||||
streamLength = d->ID3v1Location - d->streamStart;
|
||||
else
|
||||
streamLength = length() - d->streamStart;
|
||||
|
||||
d->properties = new Properties(infoData, streamLength);
|
||||
d->properties.reset(new AudioProperties(infoData, streamLength));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -462,7 +439,7 @@ void FLAC::File::scan()
|
||||
if(!isValid())
|
||||
return;
|
||||
|
||||
long nextBlockOffset;
|
||||
long long nextBlockOffset;
|
||||
|
||||
if(d->ID3v2Location >= 0)
|
||||
nextBlockOffset = find("fLaC", d->ID3v2Location + d->ID3v2OriginalSize);
|
||||
@@ -497,7 +474,7 @@ void FLAC::File::scan()
|
||||
|
||||
const char blockType = header[0] & ~LastBlockFlag;
|
||||
const bool isLastBlock = (header[0] & LastBlockFlag) != 0;
|
||||
const unsigned int blockLength = header.toUInt(1U, 3U);
|
||||
const size_t blockLength = header.toUInt24BE(1);
|
||||
|
||||
// First block should be the stream_info metadata
|
||||
|
||||
@@ -507,7 +484,9 @@ void FLAC::File::scan()
|
||||
return;
|
||||
}
|
||||
|
||||
if(blockLength == 0 && blockType != MetadataBlock::Padding) {
|
||||
if(blockLength == 0
|
||||
&& blockType != MetadataBlock::Padding && blockType != MetadataBlock::SeekTable)
|
||||
{
|
||||
debug("FLAC::File::scan() -- Zero-sized metadata block found");
|
||||
setValid(false);
|
||||
return;
|
||||
@@ -520,33 +499,32 @@ void FLAC::File::scan()
|
||||
return;
|
||||
}
|
||||
|
||||
MetadataBlock *block = 0;
|
||||
SHARED_PTR<MetadataBlock> block;
|
||||
|
||||
// Found the vorbis-comment
|
||||
if(blockType == MetadataBlock::VorbisComment) {
|
||||
if(d->xiphCommentData.isEmpty()) {
|
||||
d->xiphCommentData = data;
|
||||
block = new UnknownMetadataBlock(MetadataBlock::VorbisComment, data);
|
||||
block.reset(new UnknownMetadataBlock(MetadataBlock::VorbisComment, data));
|
||||
}
|
||||
else {
|
||||
debug("FLAC::File::scan() -- multiple Vorbis Comment blocks found, discarding");
|
||||
}
|
||||
}
|
||||
else if(blockType == MetadataBlock::Picture) {
|
||||
FLAC::Picture *picture = new FLAC::Picture();
|
||||
SHARED_PTR<FLAC::Picture> picture(new FLAC::Picture());
|
||||
if(picture->parse(data)) {
|
||||
block = picture;
|
||||
}
|
||||
else {
|
||||
debug("FLAC::File::scan() -- invalid picture found, discarding");
|
||||
delete picture;
|
||||
}
|
||||
}
|
||||
else if(blockType == MetadataBlock::Padding) {
|
||||
// Skip all padding blocks.
|
||||
}
|
||||
else {
|
||||
block = new UnknownMetadataBlock(blockType, data);
|
||||
block.reset(new UnknownMetadataBlock(blockType, data));
|
||||
}
|
||||
|
||||
if(block)
|
||||
|
||||
@@ -83,18 +83,6 @@ namespace TagLib {
|
||||
AllTags = 0xffff
|
||||
};
|
||||
|
||||
/*!
|
||||
* Constructs a FLAC file from \a file. If \a readProperties is true the
|
||||
* file's audio properties will also be read.
|
||||
*
|
||||
* \note In the current implementation, \a propertiesStyle is ignored.
|
||||
*
|
||||
* \deprecated This constructor will be dropped in favor of the one below
|
||||
* in a future version.
|
||||
*/
|
||||
File(FileName file, bool readProperties = true,
|
||||
Properties::ReadStyle propertiesStyle = Properties::Average);
|
||||
|
||||
/*!
|
||||
* Constructs an FLAC file from \a file. If \a readProperties is true the
|
||||
* file's audio properties will also be read.
|
||||
@@ -104,27 +92,27 @@ namespace TagLib {
|
||||
*
|
||||
* \note In the current implementation, \a propertiesStyle is ignored.
|
||||
*/
|
||||
// BIC: merge with the above constructor
|
||||
File(FileName file, ID3v2::FrameFactory *frameFactory,
|
||||
File(FileName file,
|
||||
bool readProperties = true,
|
||||
Properties::ReadStyle propertiesStyle = Properties::Average);
|
||||
AudioProperties::ReadStyle propertiesStyle = AudioProperties::Average,
|
||||
ID3v2::FrameFactory *frameFactory = 0);
|
||||
|
||||
/*!
|
||||
* Constructs a FLAC file from \a stream. If \a readProperties is true the
|
||||
* Constructs a FLAC file from \a file. If \a readProperties is true the
|
||||
* file's audio properties will also be read.
|
||||
*
|
||||
* \note TagLib will *not* take ownership of the stream, the caller is
|
||||
* responsible for deleting it after the File object.
|
||||
*
|
||||
* If this file contains and ID3v2 tag the frames will be created using
|
||||
* \a frameFactory.
|
||||
*
|
||||
* \note In the current implementation, \a propertiesStyle is ignored.
|
||||
*
|
||||
* \note TagLib will *not* take ownership of the stream, the caller is
|
||||
* responsible for deleting it after the File object.
|
||||
*/
|
||||
// BIC: merge with the above constructor
|
||||
File(IOStream *stream, ID3v2::FrameFactory *frameFactory,
|
||||
File(IOStream *stream,
|
||||
bool readProperties = true,
|
||||
Properties::ReadStyle propertiesStyle = Properties::Average);
|
||||
AudioProperties::ReadStyle propertiesStyle = AudioProperties::Average,
|
||||
ID3v2::FrameFactory *frameFactory = 0);
|
||||
|
||||
/*!
|
||||
* Destroys this instance of the File.
|
||||
@@ -141,16 +129,6 @@ namespace TagLib {
|
||||
*/
|
||||
virtual TagLib::Tag *tag() const;
|
||||
|
||||
/*!
|
||||
* Implements the unified property interface -- export function.
|
||||
* If the file contains more than one tag (e.g. XiphComment and ID3v1),
|
||||
* only the first one (in the order XiphComment, ID3v2, ID3v1) will be
|
||||
* converted to the PropertyMap.
|
||||
*/
|
||||
PropertyMap properties() const;
|
||||
|
||||
void removeUnsupportedProperties(const StringList &);
|
||||
|
||||
/*!
|
||||
* Implements the unified property interface -- import function.
|
||||
* This always creates a Xiph comment, if none exists. The return value
|
||||
@@ -158,13 +136,13 @@ namespace TagLib {
|
||||
* Ignores any changes to ID3v1 or ID3v2 comments since they are not allowed
|
||||
* in the FLAC specification.
|
||||
*/
|
||||
PropertyMap setProperties(const PropertyMap &);
|
||||
virtual PropertyMap setProperties(const PropertyMap &);
|
||||
|
||||
/*!
|
||||
* Returns the FLAC::Properties for this file. If no audio properties
|
||||
* were read then this will return a null pointer.
|
||||
*/
|
||||
virtual Properties *audioProperties() const;
|
||||
virtual AudioProperties *audioProperties() const;
|
||||
|
||||
/*!
|
||||
* Save the file. This will primarily save the XiphComment, but
|
||||
@@ -232,42 +210,15 @@ namespace TagLib {
|
||||
*/
|
||||
Ogg::XiphComment *xiphComment(bool create = false);
|
||||
|
||||
/*!
|
||||
* Set the ID3v2::FrameFactory to something other than the default. This
|
||||
* can be used to specify the way that ID3v2 frames will be interpreted
|
||||
* when
|
||||
*
|
||||
* \see ID3v2FrameFactory
|
||||
* \deprecated This value should be passed in via the constructor
|
||||
*/
|
||||
void setID3v2FrameFactory(const ID3v2::FrameFactory *factory);
|
||||
|
||||
/*!
|
||||
* Returns the block of data used by FLAC::Properties for parsing the
|
||||
* stream properties.
|
||||
*
|
||||
* \deprecated Always returns an empty vector.
|
||||
*/
|
||||
ByteVector streamInfoData(); // BIC: remove
|
||||
|
||||
/*!
|
||||
* Returns the length of the audio-stream, used by FLAC::Properties for
|
||||
* calculating the bitrate.
|
||||
*
|
||||
* \deprecated Always returns zero.
|
||||
*/
|
||||
long streamLength(); // BIC: remove
|
||||
|
||||
/*!
|
||||
* Returns a list of pictures attached to the FLAC file.
|
||||
*/
|
||||
List<Picture *> pictureList();
|
||||
|
||||
/*!
|
||||
* Removes an attached picture. If \a del is true the picture's memory
|
||||
* will be freed; if it is false, it must be deleted by the user.
|
||||
* Removes an attached picture. The picture's memory will be freed.
|
||||
*/
|
||||
void removePicture(Picture *picture, bool del = true);
|
||||
void removePicture(Picture *picture);
|
||||
|
||||
/*!
|
||||
* Remove all attached images.
|
||||
@@ -318,6 +269,15 @@ namespace TagLib {
|
||||
*/
|
||||
bool hasID3v2Tag() const;
|
||||
|
||||
/*!
|
||||
* Returns whether or not the given \a stream can be opened as a FLAC
|
||||
* file.
|
||||
*
|
||||
* \note This method is designed to do a quick check. The result may
|
||||
* not necessarily be correct.
|
||||
*/
|
||||
static bool isSupported(IOStream *stream);
|
||||
|
||||
private:
|
||||
File(const File &);
|
||||
File &operator=(const File &);
|
||||
|
||||
@@ -50,14 +50,14 @@ public:
|
||||
ByteVector data;
|
||||
};
|
||||
|
||||
FLAC::Picture::Picture()
|
||||
FLAC::Picture::Picture() :
|
||||
d(new PicturePrivate())
|
||||
{
|
||||
d = new PicturePrivate;
|
||||
}
|
||||
|
||||
FLAC::Picture::Picture(const ByteVector &data)
|
||||
FLAC::Picture::Picture(const ByteVector &data) :
|
||||
d(new PicturePrivate())
|
||||
{
|
||||
d = new PicturePrivate;
|
||||
parse(data);
|
||||
}
|
||||
|
||||
@@ -78,10 +78,10 @@ bool FLAC::Picture::parse(const ByteVector &data)
|
||||
return false;
|
||||
}
|
||||
|
||||
unsigned int pos = 0;
|
||||
d->type = FLAC::Picture::Type(data.toUInt(pos));
|
||||
size_t pos = 0;
|
||||
d->type = FLAC::Picture::Type(data.toUInt32BE(pos));
|
||||
pos += 4;
|
||||
unsigned int mimeTypeLength = data.toUInt(pos);
|
||||
const unsigned int mimeTypeLength = data.toUInt32BE(pos);
|
||||
pos += 4;
|
||||
if(pos + mimeTypeLength + 24 > data.size()) {
|
||||
debug("Invalid picture block.");
|
||||
@@ -89,7 +89,7 @@ bool FLAC::Picture::parse(const ByteVector &data)
|
||||
}
|
||||
d->mimeType = String(data.mid(pos, mimeTypeLength), String::UTF8);
|
||||
pos += mimeTypeLength;
|
||||
unsigned int descriptionLength = data.toUInt(pos);
|
||||
const unsigned int descriptionLength = data.toUInt32BE(pos);
|
||||
pos += 4;
|
||||
if(pos + descriptionLength + 20 > data.size()) {
|
||||
debug("Invalid picture block.");
|
||||
@@ -97,15 +97,15 @@ bool FLAC::Picture::parse(const ByteVector &data)
|
||||
}
|
||||
d->description = String(data.mid(pos, descriptionLength), String::UTF8);
|
||||
pos += descriptionLength;
|
||||
d->width = data.toUInt(pos);
|
||||
d->width = data.toUInt32BE(pos);
|
||||
pos += 4;
|
||||
d->height = data.toUInt(pos);
|
||||
d->height = data.toUInt32BE(pos);
|
||||
pos += 4;
|
||||
d->colorDepth = data.toUInt(pos);
|
||||
d->colorDepth = data.toUInt32BE(pos);
|
||||
pos += 4;
|
||||
d->numColors = data.toUInt(pos);
|
||||
d->numColors = data.toUInt32BE(pos);
|
||||
pos += 4;
|
||||
unsigned int dataLength = data.toUInt(pos);
|
||||
const unsigned int dataLength = data.toUInt32BE(pos);
|
||||
pos += 4;
|
||||
if(pos + dataLength > data.size()) {
|
||||
debug("Invalid picture block.");
|
||||
@@ -119,18 +119,18 @@ bool FLAC::Picture::parse(const ByteVector &data)
|
||||
ByteVector FLAC::Picture::render() const
|
||||
{
|
||||
ByteVector result;
|
||||
result.append(ByteVector::fromUInt(d->type));
|
||||
result.append(ByteVector::fromUInt32BE(d->type));
|
||||
ByteVector mimeTypeData = d->mimeType.data(String::UTF8);
|
||||
result.append(ByteVector::fromUInt(mimeTypeData.size()));
|
||||
result.append(ByteVector::fromUInt32BE(mimeTypeData.size()));
|
||||
result.append(mimeTypeData);
|
||||
ByteVector descriptionData = d->description.data(String::UTF8);
|
||||
result.append(ByteVector::fromUInt(descriptionData.size()));
|
||||
result.append(ByteVector::fromUInt32BE(descriptionData.size()));
|
||||
result.append(descriptionData);
|
||||
result.append(ByteVector::fromUInt(d->width));
|
||||
result.append(ByteVector::fromUInt(d->height));
|
||||
result.append(ByteVector::fromUInt(d->colorDepth));
|
||||
result.append(ByteVector::fromUInt(d->numColors));
|
||||
result.append(ByteVector::fromUInt(d->data.size()));
|
||||
result.append(ByteVector::fromUInt32BE(d->width));
|
||||
result.append(ByteVector::fromUInt32BE(d->height));
|
||||
result.append(ByteVector::fromUInt32BE(d->colorDepth));
|
||||
result.append(ByteVector::fromUInt32BE(d->numColors));
|
||||
result.append(ByteVector::fromUInt32BE(d->data.size()));
|
||||
result.append(d->data);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -89,7 +89,7 @@ namespace TagLib {
|
||||
};
|
||||
|
||||
Picture();
|
||||
Picture(const ByteVector &data);
|
||||
explicit Picture(const ByteVector &data);
|
||||
~Picture();
|
||||
|
||||
/*!
|
||||
@@ -199,8 +199,6 @@ namespace TagLib {
|
||||
PicturePrivate *d;
|
||||
};
|
||||
|
||||
typedef List<Picture> PictureList;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
|
||||
using namespace TagLib;
|
||||
|
||||
class FLAC::Properties::PropertiesPrivate
|
||||
class FLAC::AudioProperties::PropertiesPrivate
|
||||
{
|
||||
public:
|
||||
PropertiesPrivate() :
|
||||
@@ -55,71 +55,64 @@ public:
|
||||
// public members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
FLAC::Properties::Properties(ByteVector data, long streamLength, ReadStyle style) :
|
||||
AudioProperties(style),
|
||||
FLAC::AudioProperties::AudioProperties(const ByteVector &data, long long streamLength, ReadStyle) :
|
||||
TagLib::AudioProperties(),
|
||||
d(new PropertiesPrivate())
|
||||
{
|
||||
read(data, streamLength);
|
||||
}
|
||||
|
||||
FLAC::Properties::Properties(File *, ReadStyle style) :
|
||||
AudioProperties(style),
|
||||
d(new PropertiesPrivate())
|
||||
{
|
||||
debug("FLAC::Properties::Properties() - This constructor is no longer used.");
|
||||
}
|
||||
|
||||
FLAC::Properties::~Properties()
|
||||
FLAC::AudioProperties::~AudioProperties()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
|
||||
int FLAC::Properties::length() const
|
||||
int FLAC::AudioProperties::length() const
|
||||
{
|
||||
return lengthInSeconds();
|
||||
}
|
||||
|
||||
int FLAC::Properties::lengthInSeconds() const
|
||||
int FLAC::AudioProperties::lengthInSeconds() const
|
||||
{
|
||||
return d->length / 1000;
|
||||
}
|
||||
|
||||
int FLAC::Properties::lengthInMilliseconds() const
|
||||
int FLAC::AudioProperties::lengthInMilliseconds() const
|
||||
{
|
||||
return d->length;
|
||||
}
|
||||
|
||||
int FLAC::Properties::bitrate() const
|
||||
int FLAC::AudioProperties::bitrate() const
|
||||
{
|
||||
return d->bitrate;
|
||||
}
|
||||
|
||||
int FLAC::Properties::sampleRate() const
|
||||
int FLAC::AudioProperties::sampleRate() const
|
||||
{
|
||||
return d->sampleRate;
|
||||
}
|
||||
|
||||
int FLAC::Properties::bitsPerSample() const
|
||||
int FLAC::AudioProperties::bitsPerSample() const
|
||||
{
|
||||
return d->bitsPerSample;
|
||||
}
|
||||
|
||||
int FLAC::Properties::sampleWidth() const
|
||||
int FLAC::AudioProperties::sampleWidth() const
|
||||
{
|
||||
return bitsPerSample();
|
||||
}
|
||||
|
||||
int FLAC::Properties::channels() const
|
||||
int FLAC::AudioProperties::channels() const
|
||||
{
|
||||
return d->channels;
|
||||
}
|
||||
|
||||
unsigned long long FLAC::Properties::sampleFrames() const
|
||||
unsigned long long FLAC::AudioProperties::sampleFrames() const
|
||||
{
|
||||
return d->sampleFrames;
|
||||
}
|
||||
|
||||
ByteVector FLAC::Properties::signature() const
|
||||
ByteVector FLAC::AudioProperties::signature() const
|
||||
{
|
||||
return d->signature;
|
||||
}
|
||||
@@ -128,14 +121,14 @@ ByteVector FLAC::Properties::signature() const
|
||||
// private members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void FLAC::Properties::read(const ByteVector &data, long streamLength)
|
||||
void FLAC::AudioProperties::read(const ByteVector &data, long long streamLength)
|
||||
{
|
||||
if(data.size() < 18) {
|
||||
debug("FLAC::Properties::read() - FLAC properties must contain at least 18 bytes.");
|
||||
debug("FLAC::AudioProperties::read() - FLAC properties must contain at least 18 bytes.");
|
||||
return;
|
||||
}
|
||||
|
||||
unsigned int pos = 0;
|
||||
size_t pos = 0;
|
||||
|
||||
// Minimum block size (in samples)
|
||||
pos += 2;
|
||||
@@ -149,7 +142,7 @@ void FLAC::Properties::read(const ByteVector &data, long streamLength)
|
||||
// Maximum frame size (in bytes)
|
||||
pos += 3;
|
||||
|
||||
const unsigned int flags = data.toUInt(pos, true);
|
||||
const unsigned int flags = data.toUInt32BE(pos);
|
||||
pos += 4;
|
||||
|
||||
d->sampleRate = flags >> 12;
|
||||
@@ -160,7 +153,7 @@ void FLAC::Properties::read(const ByteVector &data, long streamLength)
|
||||
// stream length in samples. (Audio files measured in days)
|
||||
|
||||
const unsigned long long hi = flags & 0xf;
|
||||
const unsigned long long lo = data.toUInt(pos, true);
|
||||
const unsigned long long lo = data.toUInt32BE(pos);
|
||||
pos += 4;
|
||||
|
||||
d->sampleFrames = (hi << 32) | lo;
|
||||
|
||||
@@ -42,27 +42,19 @@ namespace TagLib {
|
||||
* API.
|
||||
*/
|
||||
|
||||
class TAGLIB_EXPORT Properties : public AudioProperties
|
||||
class TAGLIB_EXPORT AudioProperties : public TagLib::AudioProperties
|
||||
{
|
||||
public:
|
||||
/*!
|
||||
* Create an instance of FLAC::Properties with the data read from the
|
||||
* ByteVector \a data.
|
||||
* Creates an instance of FLAC::AudioProperties with the data read from
|
||||
* the ByteVector \a data.
|
||||
*/
|
||||
// BIC: switch to const reference
|
||||
Properties(ByteVector data, long streamLength, ReadStyle style = Average);
|
||||
AudioProperties(const ByteVector &data, long long streamLength, ReadStyle style = Average);
|
||||
|
||||
/*!
|
||||
* Create an instance of FLAC::Properties with the data read from the
|
||||
* FLAC::File \a file.
|
||||
* Destroys this FLAC::AudioProperties instance.
|
||||
*/
|
||||
// BIC: remove
|
||||
Properties(File *file, ReadStyle style = Average);
|
||||
|
||||
/*!
|
||||
* Destroys this FLAC::Properties instance.
|
||||
*/
|
||||
virtual ~Properties();
|
||||
virtual ~AudioProperties();
|
||||
|
||||
/*!
|
||||
* Returns the length of the file in seconds. The length is rounded down to
|
||||
@@ -80,16 +72,14 @@ namespace TagLib {
|
||||
*
|
||||
* \see lengthInMilliseconds()
|
||||
*/
|
||||
// BIC: make virtual
|
||||
int lengthInSeconds() const;
|
||||
virtual int lengthInSeconds() const;
|
||||
|
||||
/*!
|
||||
* Returns the length of the file in milliseconds.
|
||||
*
|
||||
* \see lengthInSeconds()
|
||||
*/
|
||||
// BIC: make virtual
|
||||
int lengthInMilliseconds() const;
|
||||
virtual int lengthInMilliseconds() const;
|
||||
|
||||
/*!
|
||||
* Returns the average bit rate of the file in kb/s.
|
||||
@@ -134,10 +124,7 @@ namespace TagLib {
|
||||
ByteVector signature() const;
|
||||
|
||||
private:
|
||||
Properties(const Properties &);
|
||||
Properties &operator=(const Properties &);
|
||||
|
||||
void read(const ByteVector &data, long streamLength);
|
||||
void read(const ByteVector &data, long long streamLength);
|
||||
|
||||
class PropertiesPrivate;
|
||||
PropertiesPrivate *d;
|
||||
|
||||
@@ -39,11 +39,10 @@ public:
|
||||
ByteVector data;
|
||||
};
|
||||
|
||||
FLAC::UnknownMetadataBlock::UnknownMetadataBlock(int code, const ByteVector &data)
|
||||
FLAC::UnknownMetadataBlock::UnknownMetadataBlock(int code, const ByteVector &data) :
|
||||
d(new UnknownMetadataBlockPrivate())
|
||||
{
|
||||
d = new UnknownMetadataBlockPrivate;
|
||||
d->code = code;
|
||||
//debug(String(data.toHex()));
|
||||
d->data = data;
|
||||
}
|
||||
|
||||
|
||||
@@ -36,13 +36,13 @@ using namespace IT;
|
||||
class IT::File::FilePrivate
|
||||
{
|
||||
public:
|
||||
FilePrivate(AudioProperties::ReadStyle propertiesStyle)
|
||||
explicit FilePrivate(AudioProperties::ReadStyle propertiesStyle)
|
||||
: tag(), properties(propertiesStyle)
|
||||
{
|
||||
}
|
||||
|
||||
Mod::Tag tag;
|
||||
IT::Properties properties;
|
||||
IT::AudioProperties properties;
|
||||
};
|
||||
|
||||
IT::File::File(FileName file, bool readProperties,
|
||||
@@ -73,17 +73,7 @@ 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
|
||||
IT::AudioProperties *IT::File::audioProperties() const
|
||||
{
|
||||
return &d->properties;
|
||||
}
|
||||
@@ -114,7 +104,7 @@ bool IT::File::save()
|
||||
StringList lines = d->tag.comment().split("\n");
|
||||
for(unsigned short i = 0; i < instrumentCount; ++ i) {
|
||||
seek(192L + length + ((long)i << 2));
|
||||
unsigned long instrumentOffset = 0;
|
||||
unsigned int instrumentOffset = 0;
|
||||
if(!readU32L(instrumentOffset))
|
||||
return false;
|
||||
|
||||
@@ -129,7 +119,7 @@ bool IT::File::save()
|
||||
|
||||
for(unsigned short i = 0; i < sampleCount; ++ i) {
|
||||
seek(192L + length + ((long)instrumentCount << 2) + ((long)i << 2));
|
||||
unsigned long sampleOffset = 0;
|
||||
unsigned int sampleOffset = 0;
|
||||
if(!readU32L(sampleOffset))
|
||||
return false;
|
||||
|
||||
@@ -156,14 +146,14 @@ bool IT::File::save()
|
||||
|
||||
unsigned short special = 0;
|
||||
unsigned short messageLength = 0;
|
||||
unsigned long messageOffset = 0;
|
||||
unsigned int messageOffset = 0;
|
||||
|
||||
seek(46);
|
||||
if(!readU16L(special))
|
||||
return false;
|
||||
|
||||
unsigned long fileSize = File::length();
|
||||
if(special & Properties::MessageAttached) {
|
||||
unsigned int fileSize = static_cast<unsigned int>(File::length());
|
||||
if(special & AudioProperties::MessageAttached) {
|
||||
seek(54);
|
||||
if(!readU16L(messageLength) || !readU32L(messageOffset))
|
||||
return false;
|
||||
@@ -181,7 +171,7 @@ bool IT::File::save()
|
||||
if(messageOffset + messageLength >= fileSize) {
|
||||
// append new message
|
||||
seek(54);
|
||||
writeU16L(message.size());
|
||||
writeU16L(static_cast<unsigned short>(message.size()));
|
||||
writeU32L(messageOffset);
|
||||
seek(messageOffset);
|
||||
writeBlock(message);
|
||||
@@ -233,14 +223,14 @@ void IT::File::read(bool)
|
||||
// sample/instrument names are abused as comments so
|
||||
// I just add all together.
|
||||
String message;
|
||||
if(special & Properties::MessageAttached) {
|
||||
if(special & AudioProperties::MessageAttached) {
|
||||
READ_U16L_AS(messageLength);
|
||||
READ_U32L_AS(messageOffset);
|
||||
seek(messageOffset);
|
||||
ByteVector messageBytes = readBlock(messageLength);
|
||||
READ_ASSERT(messageBytes.size() == messageLength);
|
||||
int index = messageBytes.find((char) 0);
|
||||
if(index > -1)
|
||||
const size_t index = messageBytes.find((char) 0);
|
||||
if(index != ByteVector::npos())
|
||||
messageBytes.resize(index, 0);
|
||||
messageBytes.replace('\r', '\n');
|
||||
message = messageBytes;
|
||||
|
||||
@@ -67,23 +67,11 @@ namespace TagLib {
|
||||
|
||||
Mod::Tag *tag() const;
|
||||
|
||||
/*!
|
||||
* Forwards to Mod::Tag::properties().
|
||||
* BIC: will be removed once File::toDict() is made virtual
|
||||
*/
|
||||
PropertyMap properties() const;
|
||||
|
||||
/*!
|
||||
* Forwards to Mod::Tag::setProperties().
|
||||
* BIC: will be removed once File::setProperties() is made virtual
|
||||
*/
|
||||
PropertyMap setProperties(const PropertyMap &);
|
||||
|
||||
/*!
|
||||
* Returns the IT::Properties for this file. If no audio properties
|
||||
* were read then this will return a null pointer.
|
||||
*/
|
||||
IT::Properties *audioProperties() const;
|
||||
IT::AudioProperties *audioProperties() const;
|
||||
|
||||
/*!
|
||||
* Save the file.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/***************************************************************************
|
||||
/***************************************************************************
|
||||
copyright :(C) 2011 by Mathias Panzenböck
|
||||
email : grosser.meister.morti@gmx.net
|
||||
***************************************************************************/
|
||||
@@ -29,7 +29,7 @@
|
||||
using namespace TagLib;
|
||||
using namespace IT;
|
||||
|
||||
class IT::Properties::PropertiesPrivate
|
||||
class IT::AudioProperties::PropertiesPrivate
|
||||
{
|
||||
public:
|
||||
PropertiesPrivate() :
|
||||
@@ -47,9 +47,7 @@ public:
|
||||
tempo(0),
|
||||
bpmSpeed(0),
|
||||
panningSeparation(0),
|
||||
pitchWheelDepth(0)
|
||||
{
|
||||
}
|
||||
pitchWheelDepth(0) {}
|
||||
|
||||
int channels;
|
||||
unsigned short lengthInPatterns;
|
||||
@@ -68,193 +66,201 @@ public:
|
||||
unsigned char pitchWheelDepth;
|
||||
};
|
||||
|
||||
IT::Properties::Properties(AudioProperties::ReadStyle propertiesStyle) :
|
||||
AudioProperties(propertiesStyle),
|
||||
d(new PropertiesPrivate)
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// public members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
IT::AudioProperties::AudioProperties(AudioProperties::ReadStyle) :
|
||||
TagLib::AudioProperties(),
|
||||
d(new PropertiesPrivate())
|
||||
{
|
||||
}
|
||||
|
||||
IT::Properties::~Properties()
|
||||
IT::AudioProperties::~AudioProperties()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
|
||||
int IT::Properties::length() const
|
||||
int IT::AudioProperties::length() const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int IT::Properties::lengthInSeconds() const
|
||||
int IT::AudioProperties::lengthInSeconds() const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int IT::Properties::lengthInMilliseconds() const
|
||||
int IT::AudioProperties::lengthInMilliseconds() const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int IT::Properties::bitrate() const
|
||||
int IT::AudioProperties::bitrate() const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int IT::Properties::sampleRate() const
|
||||
int IT::AudioProperties::sampleRate() const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int IT::Properties::channels() const
|
||||
int IT::AudioProperties::channels() const
|
||||
{
|
||||
return d->channels;
|
||||
}
|
||||
|
||||
unsigned short IT::Properties::lengthInPatterns() const
|
||||
unsigned short IT::AudioProperties::lengthInPatterns() const
|
||||
{
|
||||
return d->lengthInPatterns;
|
||||
}
|
||||
|
||||
bool IT::Properties::stereo() const
|
||||
bool IT::AudioProperties::stereo() const
|
||||
{
|
||||
return d->flags & Stereo;
|
||||
}
|
||||
|
||||
unsigned short IT::Properties::instrumentCount() const
|
||||
unsigned short IT::AudioProperties::instrumentCount() const
|
||||
{
|
||||
return d->instrumentCount;
|
||||
}
|
||||
|
||||
unsigned short IT::Properties::sampleCount() const
|
||||
unsigned short IT::AudioProperties::sampleCount() const
|
||||
{
|
||||
return d->sampleCount;
|
||||
}
|
||||
|
||||
unsigned short IT::Properties::patternCount() const
|
||||
unsigned short IT::AudioProperties::patternCount() const
|
||||
{
|
||||
return d->patternCount;
|
||||
}
|
||||
|
||||
unsigned short IT::Properties::version() const
|
||||
unsigned short IT::AudioProperties::version() const
|
||||
{
|
||||
return d->version;
|
||||
}
|
||||
|
||||
unsigned short IT::Properties::compatibleVersion() const
|
||||
unsigned short IT::AudioProperties::compatibleVersion() const
|
||||
{
|
||||
return d->compatibleVersion;
|
||||
}
|
||||
|
||||
unsigned short IT::Properties::flags() const
|
||||
unsigned short IT::AudioProperties::flags() const
|
||||
{
|
||||
return d->flags;
|
||||
}
|
||||
|
||||
unsigned short IT::Properties::special() const
|
||||
unsigned short IT::AudioProperties::special() const
|
||||
{
|
||||
return d->special;
|
||||
}
|
||||
|
||||
unsigned char IT::Properties::globalVolume() const
|
||||
unsigned char IT::AudioProperties::globalVolume() const
|
||||
{
|
||||
return d->globalVolume;
|
||||
}
|
||||
|
||||
unsigned char IT::Properties::mixVolume() const
|
||||
unsigned char IT::AudioProperties::mixVolume() const
|
||||
{
|
||||
return d->mixVolume;
|
||||
}
|
||||
|
||||
unsigned char IT::Properties::tempo() const
|
||||
unsigned char IT::AudioProperties::tempo() const
|
||||
{
|
||||
return d->tempo;
|
||||
}
|
||||
|
||||
unsigned char IT::Properties::bpmSpeed() const
|
||||
unsigned char IT::AudioProperties::bpmSpeed() const
|
||||
{
|
||||
return d->bpmSpeed;
|
||||
}
|
||||
|
||||
unsigned char IT::Properties::panningSeparation() const
|
||||
unsigned char IT::AudioProperties::panningSeparation() const
|
||||
{
|
||||
return d->panningSeparation;
|
||||
}
|
||||
|
||||
unsigned char IT::Properties::pitchWheelDepth() const
|
||||
unsigned char IT::AudioProperties::pitchWheelDepth() const
|
||||
{
|
||||
return d->pitchWheelDepth;
|
||||
}
|
||||
|
||||
void IT::Properties::setChannels(int channels)
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// private members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void IT::AudioProperties::setChannels(int channels)
|
||||
{
|
||||
d->channels = channels;
|
||||
}
|
||||
|
||||
void IT::Properties::setLengthInPatterns(unsigned short lengthInPatterns)
|
||||
void IT::AudioProperties::setLengthInPatterns(unsigned short lengthInPatterns)
|
||||
{
|
||||
d->lengthInPatterns = lengthInPatterns;
|
||||
}
|
||||
|
||||
void IT::Properties::setInstrumentCount(unsigned short instrumentCount)
|
||||
void IT::AudioProperties::setInstrumentCount(unsigned short instrumentCount)
|
||||
{
|
||||
d->instrumentCount = instrumentCount;
|
||||
}
|
||||
|
||||
void IT::Properties::setSampleCount(unsigned short sampleCount)
|
||||
void IT::AudioProperties::setSampleCount(unsigned short sampleCount)
|
||||
{
|
||||
d->sampleCount = sampleCount;
|
||||
}
|
||||
|
||||
void IT::Properties::setPatternCount(unsigned short patternCount)
|
||||
void IT::AudioProperties::setPatternCount(unsigned short patternCount)
|
||||
{
|
||||
d->patternCount = patternCount;
|
||||
}
|
||||
|
||||
void IT::Properties::setFlags(unsigned short flags)
|
||||
void IT::AudioProperties::setFlags(unsigned short flags)
|
||||
{
|
||||
d->flags = flags;
|
||||
}
|
||||
|
||||
void IT::Properties::setSpecial(unsigned short special)
|
||||
void IT::AudioProperties::setSpecial(unsigned short special)
|
||||
{
|
||||
d->special = special;
|
||||
}
|
||||
|
||||
void IT::Properties::setCompatibleVersion(unsigned short compatibleVersion)
|
||||
void IT::AudioProperties::setCompatibleVersion(unsigned short compatibleVersion)
|
||||
{
|
||||
d->compatibleVersion = compatibleVersion;
|
||||
}
|
||||
|
||||
void IT::Properties::setVersion(unsigned short version)
|
||||
void IT::AudioProperties::setVersion(unsigned short version)
|
||||
{
|
||||
d->version = version;
|
||||
}
|
||||
|
||||
void IT::Properties::setGlobalVolume(unsigned char globalVolume)
|
||||
void IT::AudioProperties::setGlobalVolume(unsigned char globalVolume)
|
||||
{
|
||||
d->globalVolume = globalVolume;
|
||||
}
|
||||
|
||||
void IT::Properties::setMixVolume(unsigned char mixVolume)
|
||||
void IT::AudioProperties::setMixVolume(unsigned char mixVolume)
|
||||
{
|
||||
d->mixVolume = mixVolume;
|
||||
}
|
||||
|
||||
void IT::Properties::setTempo(unsigned char tempo)
|
||||
void IT::AudioProperties::setTempo(unsigned char tempo)
|
||||
{
|
||||
d->tempo = tempo;
|
||||
}
|
||||
|
||||
void IT::Properties::setBpmSpeed(unsigned char bpmSpeed)
|
||||
void IT::AudioProperties::setBpmSpeed(unsigned char bpmSpeed)
|
||||
{
|
||||
d->bpmSpeed = bpmSpeed;
|
||||
}
|
||||
|
||||
void IT::Properties::setPanningSeparation(unsigned char panningSeparation)
|
||||
void IT::AudioProperties::setPanningSeparation(unsigned char panningSeparation)
|
||||
{
|
||||
d->panningSeparation = panningSeparation;
|
||||
}
|
||||
|
||||
void IT::Properties::setPitchWheelDepth(unsigned char pitchWheelDepth)
|
||||
void IT::AudioProperties::setPitchWheelDepth(unsigned char pitchWheelDepth)
|
||||
{
|
||||
d->pitchWheelDepth = pitchWheelDepth;
|
||||
}
|
||||
|
||||
@@ -30,9 +30,15 @@
|
||||
#include "audioproperties.h"
|
||||
|
||||
namespace TagLib {
|
||||
|
||||
namespace IT {
|
||||
class TAGLIB_EXPORT Properties : public AudioProperties {
|
||||
|
||||
class File;
|
||||
|
||||
class TAGLIB_EXPORT AudioProperties : public TagLib::AudioProperties
|
||||
{
|
||||
friend class File;
|
||||
|
||||
public:
|
||||
/*! Flag bits. */
|
||||
enum {
|
||||
@@ -52,8 +58,8 @@ namespace TagLib {
|
||||
MidiConfEmbedded = 8
|
||||
};
|
||||
|
||||
Properties(AudioProperties::ReadStyle propertiesStyle);
|
||||
virtual ~Properties();
|
||||
explicit AudioProperties(AudioProperties::ReadStyle propertiesStyle);
|
||||
virtual ~AudioProperties();
|
||||
|
||||
int length() const;
|
||||
int lengthInSeconds() const;
|
||||
@@ -78,6 +84,7 @@ namespace TagLib {
|
||||
unsigned char panningSeparation() const;
|
||||
unsigned char pitchWheelDepth() const;
|
||||
|
||||
private:
|
||||
void setChannels(int channels);
|
||||
void setLengthInPatterns(unsigned short lengthInPatterns);
|
||||
void setInstrumentCount(unsigned short instrumentCount);
|
||||
@@ -94,10 +101,6 @@ namespace TagLib {
|
||||
void setPanningSeparation(unsigned char panningSeparation);
|
||||
void setPitchWheelDepth (unsigned char pitchWheelDepth);
|
||||
|
||||
private:
|
||||
Properties(const Properties&);
|
||||
Properties &operator=(const Properties&);
|
||||
|
||||
class PropertiesPrivate;
|
||||
PropertiesPrivate *d;
|
||||
};
|
||||
|
||||
@@ -36,13 +36,13 @@ using namespace Mod;
|
||||
class Mod::File::FilePrivate
|
||||
{
|
||||
public:
|
||||
FilePrivate(AudioProperties::ReadStyle propertiesStyle)
|
||||
explicit FilePrivate(AudioProperties::ReadStyle propertiesStyle)
|
||||
: properties(propertiesStyle)
|
||||
{
|
||||
}
|
||||
|
||||
Mod::Tag tag;
|
||||
Mod::Properties properties;
|
||||
Mod::AudioProperties properties;
|
||||
};
|
||||
|
||||
Mod::File::File(FileName file, bool readProperties,
|
||||
@@ -73,21 +73,11 @@ Mod::Tag *Mod::File::tag() const
|
||||
return &d->tag;
|
||||
}
|
||||
|
||||
Mod::Properties *Mod::File::audioProperties() const
|
||||
Mod::AudioProperties *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()) {
|
||||
@@ -97,13 +87,13 @@ bool Mod::File::save()
|
||||
seek(0);
|
||||
writeString(d->tag.title(), 20);
|
||||
StringList lines = d->tag.comment().split("\n");
|
||||
unsigned int n = std::min(lines.size(), d->properties.instrumentCount());
|
||||
for(unsigned int i = 0; i < n; ++ i) {
|
||||
size_t n = std::min<size_t>(lines.size(), d->properties.instrumentCount());
|
||||
for(size_t i = 0; i < n; ++ i) {
|
||||
writeString(lines[i], 22);
|
||||
seek(8, Current);
|
||||
}
|
||||
|
||||
for(unsigned int i = n; i < d->properties.instrumentCount(); ++ i) {
|
||||
for(size_t i = n; i < d->properties.instrumentCount(); ++ i) {
|
||||
writeString(String(), 22);
|
||||
seek(8, Current);
|
||||
}
|
||||
|
||||
@@ -72,22 +72,11 @@ namespace TagLib {
|
||||
|
||||
Mod::Tag *tag() const;
|
||||
|
||||
/*!
|
||||
* Implements the unified property interface -- export function.
|
||||
* Forwards to Mod::Tag::properties().
|
||||
*/
|
||||
PropertyMap properties() const;
|
||||
|
||||
/*!
|
||||
* Implements the unified property interface -- import function.
|
||||
* Forwards to Mod::Tag::setProperties().
|
||||
*/
|
||||
PropertyMap setProperties(const PropertyMap &);
|
||||
/*!
|
||||
* Returns the Mod::Properties for this file. If no audio properties
|
||||
* were read then this will return a null pointer.
|
||||
*/
|
||||
Mod::Properties *audioProperties() const;
|
||||
Mod::AudioProperties *audioProperties() const;
|
||||
|
||||
/*!
|
||||
* Save the file.
|
||||
|
||||
@@ -38,20 +38,19 @@ Mod::FileBase::FileBase(IOStream *stream) : TagLib::File(stream)
|
||||
{
|
||||
}
|
||||
|
||||
void Mod::FileBase::writeString(const String &s, unsigned long size, char padding)
|
||||
void Mod::FileBase::writeString(const String &s, unsigned int size, char padding)
|
||||
{
|
||||
ByteVector data(s.data(String::Latin1));
|
||||
data.resize(size, padding);
|
||||
writeBlock(data);
|
||||
}
|
||||
|
||||
bool Mod::FileBase::readString(String &s, unsigned long size)
|
||||
bool Mod::FileBase::readString(String &s, unsigned int size)
|
||||
{
|
||||
ByteVector data(readBlock(size));
|
||||
if(data.size() < size) return false;
|
||||
int index = data.find((char) 0);
|
||||
if(index > -1)
|
||||
{
|
||||
const size_t index = data.find((char) 0);
|
||||
if(index != ByteVector::npos()) {
|
||||
data.resize(index);
|
||||
}
|
||||
data.replace('\xff', ' ');
|
||||
@@ -68,22 +67,22 @@ void Mod::FileBase::writeByte(unsigned char byte)
|
||||
|
||||
void Mod::FileBase::writeU16L(unsigned short number)
|
||||
{
|
||||
writeBlock(ByteVector::fromShort(number, false));
|
||||
writeBlock(ByteVector::fromUInt16LE(number));
|
||||
}
|
||||
|
||||
void Mod::FileBase::writeU32L(unsigned long number)
|
||||
void Mod::FileBase::writeU32L(unsigned int number)
|
||||
{
|
||||
writeBlock(ByteVector::fromUInt(number, false));
|
||||
writeBlock(ByteVector::fromUInt32LE(number));
|
||||
}
|
||||
|
||||
void Mod::FileBase::writeU16B(unsigned short number)
|
||||
{
|
||||
writeBlock(ByteVector::fromShort(number, true));
|
||||
writeBlock(ByteVector::fromUInt16BE(number));
|
||||
}
|
||||
|
||||
void Mod::FileBase::writeU32B(unsigned long number)
|
||||
void Mod::FileBase::writeU32B(unsigned int number)
|
||||
{
|
||||
writeBlock(ByteVector::fromUInt(number, true));
|
||||
writeBlock(ByteVector::fromUInt32BE(number));
|
||||
}
|
||||
|
||||
bool Mod::FileBase::readByte(unsigned char &byte)
|
||||
@@ -98,14 +97,14 @@ bool Mod::FileBase::readU16L(unsigned short &number)
|
||||
{
|
||||
ByteVector data(readBlock(2));
|
||||
if(data.size() < 2) return false;
|
||||
number = data.toUShort(false);
|
||||
number = data.toUInt16LE(0);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Mod::FileBase::readU32L(unsigned long &number) {
|
||||
bool Mod::FileBase::readU32L(unsigned int &number) {
|
||||
ByteVector data(readBlock(4));
|
||||
if(data.size() < 4) return false;
|
||||
number = data.toUInt(false);
|
||||
number = data.toUInt32LE(0);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -113,13 +112,13 @@ bool Mod::FileBase::readU16B(unsigned short &number)
|
||||
{
|
||||
ByteVector data(readBlock(2));
|
||||
if(data.size() < 2) return false;
|
||||
number = data.toUShort(true);
|
||||
number = data.toUInt16BE(0);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Mod::FileBase::readU32B(unsigned long &number) {
|
||||
bool Mod::FileBase::readU32B(unsigned int &number) {
|
||||
ByteVector data(readBlock(4));
|
||||
if(data.size() < 4) return false;
|
||||
number = data.toUInt(true);
|
||||
number = data.toUInt32BE(0);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -41,22 +41,22 @@ namespace TagLib {
|
||||
class TAGLIB_EXPORT FileBase : public TagLib::File
|
||||
{
|
||||
protected:
|
||||
FileBase(FileName file);
|
||||
FileBase(IOStream *stream);
|
||||
explicit FileBase(FileName file);
|
||||
explicit FileBase(IOStream *stream);
|
||||
|
||||
void writeString(const String &s, unsigned long size, char padding = 0);
|
||||
void writeString(const String &s, unsigned int size, char padding = 0);
|
||||
void writeByte(unsigned char byte);
|
||||
void writeU16L(unsigned short number);
|
||||
void writeU32L(unsigned long number);
|
||||
void writeU32L(unsigned int number);
|
||||
void writeU16B(unsigned short number);
|
||||
void writeU32B(unsigned long number);
|
||||
void writeU32B(unsigned int number);
|
||||
|
||||
bool readString(String &s, unsigned long size);
|
||||
bool readString(String &s, unsigned int size);
|
||||
bool readByte(unsigned char &byte);
|
||||
bool readU16L(unsigned short &number);
|
||||
bool readU32L(unsigned long &number);
|
||||
bool readU32L(unsigned int &number);
|
||||
bool readU16B(unsigned short &number);
|
||||
bool readU32B(unsigned long &number);
|
||||
bool readU32B(unsigned int &number);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -39,9 +39,9 @@
|
||||
|
||||
#define READ_BYTE(setter) READ(setter,unsigned char,readByte)
|
||||
#define READ_U16L(setter) READ(setter,unsigned short,readU16L)
|
||||
#define READ_U32L(setter) READ(setter,unsigned long,readU32L)
|
||||
#define READ_U32L(setter) READ(setter,unsigned int,readU32L)
|
||||
#define READ_U16B(setter) READ(setter,unsigned short,readU16B)
|
||||
#define READ_U32B(setter) READ(setter,unsigned long,readU32B)
|
||||
#define READ_U32B(setter) READ(setter,unsigned int,readU32B)
|
||||
|
||||
#define READ_STRING(setter,size) \
|
||||
{ \
|
||||
@@ -56,9 +56,9 @@
|
||||
|
||||
#define READ_BYTE_AS(name) READ_AS(unsigned char,name,readByte)
|
||||
#define READ_U16L_AS(name) READ_AS(unsigned short,name,readU16L)
|
||||
#define READ_U32L_AS(name) READ_AS(unsigned long,name,readU32L)
|
||||
#define READ_U32L_AS(name) READ_AS(unsigned int,name,readU32L)
|
||||
#define READ_U16B_AS(name) READ_AS(unsigned short,name,readU16B)
|
||||
#define READ_U32B_AS(name) READ_AS(unsigned long,name,readU32B)
|
||||
#define READ_U32B_AS(name) READ_AS(unsigned int,name,readU32B)
|
||||
|
||||
#define READ_STRING_AS(name,size) \
|
||||
String name; \
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/***************************************************************************
|
||||
/***************************************************************************
|
||||
copyright : (C) 2011 by Mathias Panzenböck
|
||||
email : grosser.meister.morti@gmx.net
|
||||
***************************************************************************/
|
||||
@@ -29,83 +29,89 @@
|
||||
using namespace TagLib;
|
||||
using namespace Mod;
|
||||
|
||||
class Mod::Properties::PropertiesPrivate
|
||||
class Mod::AudioProperties::PropertiesPrivate
|
||||
{
|
||||
public:
|
||||
PropertiesPrivate() :
|
||||
channels(0),
|
||||
instrumentCount(0),
|
||||
lengthInPatterns(0)
|
||||
{
|
||||
}
|
||||
lengthInPatterns(0) {}
|
||||
|
||||
int channels;
|
||||
unsigned int instrumentCount;
|
||||
unsigned char lengthInPatterns;
|
||||
};
|
||||
|
||||
Mod::Properties::Properties(AudioProperties::ReadStyle propertiesStyle) :
|
||||
AudioProperties(propertiesStyle),
|
||||
d(new PropertiesPrivate)
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// public members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
Mod::AudioProperties::AudioProperties(AudioProperties::ReadStyle) :
|
||||
TagLib::AudioProperties(),
|
||||
d(new PropertiesPrivate())
|
||||
{
|
||||
}
|
||||
|
||||
Mod::Properties::~Properties()
|
||||
Mod::AudioProperties::~AudioProperties()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
|
||||
int Mod::Properties::length() const
|
||||
int Mod::AudioProperties::length() const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Mod::Properties::lengthInSeconds() const
|
||||
int Mod::AudioProperties::lengthInSeconds() const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Mod::Properties::lengthInMilliseconds() const
|
||||
int Mod::AudioProperties::lengthInMilliseconds() const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Mod::Properties::bitrate() const
|
||||
int Mod::AudioProperties::bitrate() const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Mod::Properties::sampleRate() const
|
||||
int Mod::AudioProperties::sampleRate() const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Mod::Properties::channels() const
|
||||
int Mod::AudioProperties::channels() const
|
||||
{
|
||||
return d->channels;
|
||||
}
|
||||
|
||||
unsigned int Mod::Properties::instrumentCount() const
|
||||
unsigned int Mod::AudioProperties::instrumentCount() const
|
||||
{
|
||||
return d->instrumentCount;
|
||||
}
|
||||
|
||||
unsigned char Mod::Properties::lengthInPatterns() const
|
||||
unsigned char Mod::AudioProperties::lengthInPatterns() const
|
||||
{
|
||||
return d->lengthInPatterns;
|
||||
}
|
||||
|
||||
void Mod::Properties::setChannels(int channels)
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// private members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void Mod::AudioProperties::setChannels(int channels)
|
||||
{
|
||||
d->channels = channels;
|
||||
}
|
||||
|
||||
void Mod::Properties::setInstrumentCount(unsigned int instrumentCount)
|
||||
void Mod::AudioProperties::setInstrumentCount(unsigned int instrumentCount)
|
||||
{
|
||||
d->instrumentCount = instrumentCount;
|
||||
}
|
||||
|
||||
void Mod::Properties::setLengthInPatterns(unsigned char lengthInPatterns)
|
||||
void Mod::AudioProperties::setLengthInPatterns(unsigned char lengthInPatterns)
|
||||
{
|
||||
d->lengthInPatterns = lengthInPatterns;
|
||||
}
|
||||
|
||||
@@ -33,11 +33,15 @@ namespace TagLib {
|
||||
|
||||
namespace Mod {
|
||||
|
||||
class TAGLIB_EXPORT Properties : public AudioProperties
|
||||
class File;
|
||||
|
||||
class TAGLIB_EXPORT AudioProperties : public TagLib::AudioProperties
|
||||
{
|
||||
friend class File;
|
||||
|
||||
public:
|
||||
Properties(AudioProperties::ReadStyle propertiesStyle);
|
||||
virtual ~Properties();
|
||||
explicit AudioProperties(AudioProperties::ReadStyle propertiesStyle);
|
||||
virtual ~AudioProperties();
|
||||
|
||||
int length() const;
|
||||
int lengthInSeconds() const;
|
||||
@@ -49,17 +53,12 @@ namespace TagLib {
|
||||
unsigned int instrumentCount() const;
|
||||
unsigned char lengthInPatterns() const;
|
||||
|
||||
private:
|
||||
void setChannels(int channels);
|
||||
|
||||
void setInstrumentCount(unsigned int sampleCount);
|
||||
void setLengthInPatterns(unsigned char lengthInPatterns);
|
||||
|
||||
private:
|
||||
friend class File;
|
||||
|
||||
Properties(const Properties&);
|
||||
Properties &operator=(const Properties&);
|
||||
|
||||
class PropertiesPrivate;
|
||||
PropertiesPrivate *d;
|
||||
};
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
#include "modtag.h"
|
||||
#include "tstringlist.h"
|
||||
#include "tpropertymap.h"
|
||||
#include "tpicturemap.h"
|
||||
|
||||
using namespace TagLib;
|
||||
using namespace Mod;
|
||||
@@ -43,9 +44,10 @@ public:
|
||||
String trackerName;
|
||||
};
|
||||
|
||||
Mod::Tag::Tag() : TagLib::Tag()
|
||||
Mod::Tag::Tag() :
|
||||
TagLib::Tag(),
|
||||
d(new TagPrivate())
|
||||
{
|
||||
d = new TagPrivate;
|
||||
}
|
||||
|
||||
Mod::Tag::~Tag()
|
||||
@@ -88,6 +90,11 @@ unsigned int Mod::Tag::track() const
|
||||
return 0;
|
||||
}
|
||||
|
||||
TagLib::PictureMap Mod::Tag::pictures() const
|
||||
{
|
||||
return PictureMap();
|
||||
}
|
||||
|
||||
String Mod::Tag::trackerName() const
|
||||
{
|
||||
return d->trackerName;
|
||||
@@ -123,6 +130,10 @@ void Mod::Tag::setTrack(unsigned int)
|
||||
{
|
||||
}
|
||||
|
||||
void Mod::Tag::setPictures(const PictureMap &l)
|
||||
{
|
||||
}
|
||||
|
||||
void Mod::Tag::setTrackerName(const String &trackerName)
|
||||
{
|
||||
d->trackerName = trackerName;
|
||||
|
||||
@@ -88,6 +88,8 @@ namespace TagLib {
|
||||
*/
|
||||
virtual unsigned int track() const;
|
||||
|
||||
PictureMap pictures() const;
|
||||
|
||||
/*!
|
||||
* Returns the name of the tracker used to create/edit the module file.
|
||||
* Only XM files store this tag to the file as such, for other formats
|
||||
@@ -151,6 +153,8 @@ namespace TagLib {
|
||||
*/
|
||||
virtual void setTrack(unsigned int track);
|
||||
|
||||
void setPictures(const PictureMap &l);
|
||||
|
||||
/*!
|
||||
* Sets the tracker name to \a trackerName. If \a trackerName is
|
||||
* String::null then this value will be cleared.
|
||||
|
||||
@@ -23,18 +23,17 @@
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#include <climits>
|
||||
|
||||
#include <tdebug.h>
|
||||
#include <tstring.h>
|
||||
|
||||
#include "mp4atom.h"
|
||||
|
||||
using namespace TagLib;
|
||||
|
||||
const char *MP4::Atom::containers[11] = {
|
||||
"moov", "udta", "mdia", "meta", "ilst",
|
||||
"stbl", "minf", "moof", "traf", "trak",
|
||||
"stsd"
|
||||
"moov", "udta", "mdia", "meta", "ilst",
|
||||
"stbl", "minf", "moof", "traf", "trak",
|
||||
"stsd"
|
||||
};
|
||||
|
||||
MP4::Atom::Atom(File *file)
|
||||
@@ -52,26 +51,17 @@ MP4::Atom::Atom(File *file)
|
||||
return;
|
||||
}
|
||||
|
||||
length = header.toUInt();
|
||||
length = header.toUInt32BE(0);
|
||||
|
||||
if(length == 1) {
|
||||
const long long longLength = file->readBlock(8).toLongLong();
|
||||
if(sizeof(long) == sizeof(long long)) {
|
||||
length = static_cast<long>(longLength);
|
||||
}
|
||||
else {
|
||||
if(longLength <= LONG_MAX) {
|
||||
// The atom has a 64-bit length, but it's actually a 31-bit value
|
||||
length = static_cast<long>(longLength);
|
||||
}
|
||||
else {
|
||||
debug("MP4: 64-bit atoms are not supported");
|
||||
length = 0;
|
||||
file->seek(0, File::End);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if(length == 0) {
|
||||
// The last atom which extends to the end of the file.
|
||||
length = file->length() - offset;
|
||||
}
|
||||
else if(length == 1) {
|
||||
// The atom has a 64-bit length.
|
||||
length = file->readBlock(8).toInt64BE(0);
|
||||
}
|
||||
|
||||
if(length < 8) {
|
||||
debug("MP4: Invalid atom size");
|
||||
length = 0;
|
||||
@@ -155,7 +145,7 @@ MP4::Atoms::Atoms(File *file)
|
||||
atoms.setAutoDelete(true);
|
||||
|
||||
file->seek(0, File::End);
|
||||
long end = file->tell();
|
||||
long long end = file->tell();
|
||||
file->seek(0);
|
||||
while(file->tell() + 8 <= end) {
|
||||
MP4::Atom *atom = new MP4::Atom(file);
|
||||
|
||||
@@ -77,13 +77,13 @@ namespace TagLib {
|
||||
class Atom
|
||||
{
|
||||
public:
|
||||
Atom(File *file);
|
||||
explicit Atom(File *file);
|
||||
~Atom();
|
||||
Atom *find(const char *name1, const char *name2 = 0, const char *name3 = 0, const char *name4 = 0);
|
||||
bool path(AtomList &path, const char *name1, const char *name2 = 0, const char *name3 = 0);
|
||||
AtomList findall(const char *name, bool recursive = false);
|
||||
long offset;
|
||||
long length;
|
||||
long long offset;
|
||||
long long length;
|
||||
TagLib::ByteVector name;
|
||||
AtomList children;
|
||||
private:
|
||||
@@ -95,7 +95,7 @@ namespace TagLib {
|
||||
class Atoms
|
||||
{
|
||||
public:
|
||||
Atoms(File *file);
|
||||
explicit Atoms(File *file);
|
||||
~Atoms();
|
||||
Atom *find(const char *name1, const char *name2 = 0, const char *name3 = 0, const char *name4 = 0);
|
||||
AtomList path(const char *name1, const char *name2 = 0, const char *name3 = 0, const char *name4 = 0);
|
||||
|
||||
@@ -23,22 +23,33 @@
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#include <taglib.h>
|
||||
#include <tdebug.h>
|
||||
#include "trefcounter.h"
|
||||
#include "taglib.h"
|
||||
#include "tdebug.h"
|
||||
#include "tsmartptr.h"
|
||||
#include "mp4coverart.h"
|
||||
|
||||
using namespace TagLib;
|
||||
|
||||
class MP4::CoverArt::CoverArtPrivate : public RefCounter
|
||||
namespace
|
||||
{
|
||||
struct CoverArtData
|
||||
{
|
||||
MP4::CoverArt::Format format;
|
||||
ByteVector data;
|
||||
};
|
||||
}
|
||||
|
||||
class MP4::CoverArt::CoverArtPrivate
|
||||
{
|
||||
public:
|
||||
CoverArtPrivate() :
|
||||
RefCounter(),
|
||||
format(MP4::CoverArt::JPEG) {}
|
||||
CoverArtPrivate(Format f, const ByteVector &v) :
|
||||
data(new CoverArtData())
|
||||
{
|
||||
data->format = f;
|
||||
data->data = v;
|
||||
}
|
||||
|
||||
Format format;
|
||||
ByteVector data;
|
||||
SHARED_PTR<CoverArtData> data;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -46,16 +57,13 @@ public:
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
MP4::CoverArt::CoverArt(Format format, const ByteVector &data) :
|
||||
d(new CoverArtPrivate())
|
||||
d(new CoverArtPrivate(format, data))
|
||||
{
|
||||
d->format = format;
|
||||
d->data = data;
|
||||
}
|
||||
|
||||
MP4::CoverArt::CoverArt(const CoverArt &item) :
|
||||
d(item.d)
|
||||
d(new CoverArtPrivate(*item.d))
|
||||
{
|
||||
d->ref();
|
||||
}
|
||||
|
||||
MP4::CoverArt &
|
||||
@@ -75,19 +83,17 @@ MP4::CoverArt::swap(CoverArt &item)
|
||||
|
||||
MP4::CoverArt::~CoverArt()
|
||||
{
|
||||
if(d->deref()) {
|
||||
delete d;
|
||||
}
|
||||
delete d;
|
||||
}
|
||||
|
||||
MP4::CoverArt::Format
|
||||
MP4::CoverArt::format() const
|
||||
{
|
||||
return d->format;
|
||||
return d->data->format;
|
||||
}
|
||||
|
||||
ByteVector
|
||||
MP4::CoverArt::data() const
|
||||
{
|
||||
return d->data;
|
||||
return d->data->data;
|
||||
}
|
||||
|
||||
@@ -76,9 +76,7 @@ namespace TagLib {
|
||||
};
|
||||
|
||||
typedef List<CoverArt> CoverArtList;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -26,6 +26,8 @@
|
||||
#include <tdebug.h>
|
||||
#include <tstring.h>
|
||||
#include <tpropertymap.h>
|
||||
#include <tagutils.h>
|
||||
|
||||
#include "mp4atom.h"
|
||||
#include "mp4tag.h"
|
||||
#include "mp4file.h"
|
||||
@@ -64,11 +66,27 @@ public:
|
||||
delete properties;
|
||||
}
|
||||
|
||||
MP4::Tag *tag;
|
||||
MP4::Atoms *atoms;
|
||||
MP4::Properties *properties;
|
||||
MP4::Tag *tag;
|
||||
MP4::Atoms *atoms;
|
||||
MP4::AudioProperties *properties;
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// static members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
bool MP4::File::isSupported(IOStream *stream)
|
||||
{
|
||||
// An MP4 file has to have an "ftyp" box first.
|
||||
|
||||
const ByteVector id = Utils::readHeader(stream, 8, false);
|
||||
return id.containsAt("ftyp", 4);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// public members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
MP4::File::File(FileName file, bool readProperties, AudioProperties::ReadStyle) :
|
||||
TagLib::File(file),
|
||||
d(new FilePrivate())
|
||||
@@ -96,22 +114,7 @@ MP4::File::tag() const
|
||||
return d->tag;
|
||||
}
|
||||
|
||||
PropertyMap MP4::File::properties() const
|
||||
{
|
||||
return d->tag->properties();
|
||||
}
|
||||
|
||||
void MP4::File::removeUnsupportedProperties(const StringList &properties)
|
||||
{
|
||||
d->tag->removeUnsupportedProperties(properties);
|
||||
}
|
||||
|
||||
PropertyMap MP4::File::setProperties(const PropertyMap &properties)
|
||||
{
|
||||
return d->tag->setProperties(properties);
|
||||
}
|
||||
|
||||
MP4::Properties *
|
||||
MP4::AudioProperties *
|
||||
MP4::File::audioProperties() const
|
||||
{
|
||||
return d->properties;
|
||||
@@ -137,7 +140,7 @@ MP4::File::read(bool readProperties)
|
||||
|
||||
d->tag = new Tag(this, d->atoms);
|
||||
if(readProperties) {
|
||||
d->properties = new Properties(this, d->atoms);
|
||||
d->properties = new AudioProperties(this, d->atoms);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -55,19 +55,19 @@ namespace TagLib {
|
||||
* \note In the current implementation, \a propertiesStyle is ignored.
|
||||
*/
|
||||
File(FileName file, bool readProperties = true,
|
||||
Properties::ReadStyle audioPropertiesStyle = Properties::Average);
|
||||
AudioProperties::ReadStyle audioPropertiesStyle = AudioProperties::Average);
|
||||
|
||||
/*!
|
||||
* Constructs an MP4 file from \a stream. If \a readProperties is true the
|
||||
* Constructs an MP4 file from \a file. If \a readProperties is true the
|
||||
* file's audio properties will also be read.
|
||||
*
|
||||
* \note In the current implementation, \a propertiesStyle is ignored.
|
||||
*
|
||||
* \note TagLib will *not* take ownership of the stream, the caller is
|
||||
* responsible for deleting it after the File object.
|
||||
*
|
||||
* \note In the current implementation, \a propertiesStyle is ignored.
|
||||
*/
|
||||
File(IOStream *stream, bool readProperties = true,
|
||||
Properties::ReadStyle audioPropertiesStyle = Properties::Average);
|
||||
AudioProperties::ReadStyle audioPropertiesStyle = AudioProperties::Average);
|
||||
|
||||
/*!
|
||||
* Destroys this instance of the File.
|
||||
@@ -86,26 +86,10 @@ namespace TagLib {
|
||||
*/
|
||||
Tag *tag() const;
|
||||
|
||||
/*!
|
||||
* Implements the unified property interface -- export function.
|
||||
*/
|
||||
PropertyMap properties() const;
|
||||
|
||||
/*!
|
||||
* Removes unsupported properties. Forwards to the actual Tag's
|
||||
* removeUnsupportedProperties() function.
|
||||
*/
|
||||
void removeUnsupportedProperties(const StringList &properties);
|
||||
|
||||
/*!
|
||||
* Implements the unified property interface -- import function.
|
||||
*/
|
||||
PropertyMap setProperties(const PropertyMap &);
|
||||
|
||||
/*!
|
||||
* Returns the MP4 audio properties for this file.
|
||||
*/
|
||||
Properties *audioProperties() const;
|
||||
AudioProperties *audioProperties() const;
|
||||
|
||||
/*!
|
||||
* Save the file.
|
||||
@@ -120,6 +104,15 @@ namespace TagLib {
|
||||
*/
|
||||
bool hasMP4Tag() const;
|
||||
|
||||
/*!
|
||||
* Returns whether or not the given \a stream can be opened as an ASF
|
||||
* file.
|
||||
*
|
||||
* \note This method is designed to do a quick check. The result may
|
||||
* not necessarily be correct.
|
||||
*/
|
||||
static bool isSupported(IOStream *stream);
|
||||
|
||||
private:
|
||||
void read(bool readProperties);
|
||||
|
||||
|
||||
@@ -23,46 +23,58 @@
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#include <taglib.h>
|
||||
#include <tdebug.h>
|
||||
#include "trefcounter.h"
|
||||
#include "taglib.h"
|
||||
#include "tdebug.h"
|
||||
#include "tsmartptr.h"
|
||||
#include "mp4item.h"
|
||||
#include "tutils.h"
|
||||
|
||||
using namespace TagLib;
|
||||
|
||||
class MP4::Item::ItemPrivate : public RefCounter
|
||||
namespace
|
||||
{
|
||||
struct ItemData
|
||||
{
|
||||
bool valid;
|
||||
MP4::AtomDataType atomDataType;
|
||||
MP4::Item::ItemType type;
|
||||
union {
|
||||
bool m_bool;
|
||||
int m_int;
|
||||
MP4::Item::IntPair m_intPair;
|
||||
unsigned char m_byte;
|
||||
unsigned int m_uint;
|
||||
long long m_longlong;
|
||||
};
|
||||
StringList m_stringList;
|
||||
ByteVectorList m_byteVectorList;
|
||||
MP4::CoverArtList m_coverArtList;
|
||||
};
|
||||
}
|
||||
|
||||
class MP4::Item::ItemPrivate
|
||||
{
|
||||
public:
|
||||
ItemPrivate() :
|
||||
RefCounter(),
|
||||
valid(true),
|
||||
atomDataType(TypeUndefined) {}
|
||||
data(new ItemData())
|
||||
{
|
||||
data->valid = true;
|
||||
data->atomDataType = MP4::TypeUndefined;
|
||||
data->type = MP4::Item::TypeUndefined;
|
||||
}
|
||||
|
||||
bool valid;
|
||||
AtomDataType atomDataType;
|
||||
union {
|
||||
bool m_bool;
|
||||
int m_int;
|
||||
IntPair m_intPair;
|
||||
unsigned char m_byte;
|
||||
unsigned int m_uint;
|
||||
long long m_longlong;
|
||||
};
|
||||
StringList m_stringList;
|
||||
ByteVectorList m_byteVectorList;
|
||||
MP4::CoverArtList m_coverArtList;
|
||||
SHARED_PTR<ItemData> data;
|
||||
};
|
||||
|
||||
MP4::Item::Item() :
|
||||
d(new ItemPrivate())
|
||||
{
|
||||
d->valid = false;
|
||||
d->data->valid = false;
|
||||
}
|
||||
|
||||
MP4::Item::Item(const Item &item) :
|
||||
d(item.d)
|
||||
d(new ItemPrivate(*item.d))
|
||||
{
|
||||
d->ref();
|
||||
}
|
||||
|
||||
MP4::Item &
|
||||
@@ -82,131 +94,176 @@ MP4::Item::swap(Item &item)
|
||||
|
||||
MP4::Item::~Item()
|
||||
{
|
||||
if(d->deref())
|
||||
delete d;
|
||||
delete d;
|
||||
}
|
||||
|
||||
MP4::Item::Item(bool value) :
|
||||
d(new ItemPrivate())
|
||||
{
|
||||
d->m_bool = value;
|
||||
d->data->m_bool = value;
|
||||
d->data->type = TypeBool;
|
||||
}
|
||||
|
||||
MP4::Item::Item(int value) :
|
||||
d(new ItemPrivate())
|
||||
{
|
||||
d->m_int = value;
|
||||
d->data->m_int = value;
|
||||
d->data->type = TypeInt;
|
||||
}
|
||||
|
||||
MP4::Item::Item(unsigned char value) :
|
||||
d(new ItemPrivate())
|
||||
{
|
||||
d->m_byte = value;
|
||||
d->data->m_byte = value;
|
||||
d->data->type = TypeByte;
|
||||
}
|
||||
|
||||
MP4::Item::Item(unsigned int value) :
|
||||
d(new ItemPrivate())
|
||||
{
|
||||
d->m_uint = value;
|
||||
d->data->m_uint = value;
|
||||
d->data->type = TypeUInt;
|
||||
}
|
||||
|
||||
MP4::Item::Item(long long value) :
|
||||
d(new ItemPrivate())
|
||||
{
|
||||
d->m_longlong = value;
|
||||
d->data->m_longlong = value;
|
||||
d->data->type = TypeLongLong;
|
||||
}
|
||||
|
||||
MP4::Item::Item(int value1, int value2) :
|
||||
d(new ItemPrivate())
|
||||
{
|
||||
d->m_intPair.first = value1;
|
||||
d->m_intPair.second = value2;
|
||||
d->data->m_intPair.first = value1;
|
||||
d->data->m_intPair.second = value2;
|
||||
d->data->type = TypeIntPair;
|
||||
}
|
||||
|
||||
MP4::Item::Item(const ByteVectorList &value) :
|
||||
d(new ItemPrivate())
|
||||
{
|
||||
d->m_byteVectorList = value;
|
||||
d->data->m_byteVectorList = value;
|
||||
d->data->type = TypeByteVectorList;
|
||||
}
|
||||
|
||||
MP4::Item::Item(const StringList &value) :
|
||||
d(new ItemPrivate())
|
||||
{
|
||||
d->m_stringList = value;
|
||||
d->data->m_stringList = value;
|
||||
d->data->type = TypeStringList;
|
||||
}
|
||||
|
||||
MP4::Item::Item(const MP4::CoverArtList &value) :
|
||||
d(new ItemPrivate())
|
||||
{
|
||||
d->m_coverArtList = value;
|
||||
d->data->m_coverArtList = value;
|
||||
d->data->type = TypeCoverArtList;
|
||||
}
|
||||
|
||||
void MP4::Item::setAtomDataType(MP4::AtomDataType type)
|
||||
{
|
||||
d->atomDataType = type;
|
||||
d->data->atomDataType = type;
|
||||
}
|
||||
|
||||
MP4::AtomDataType MP4::Item::atomDataType() const
|
||||
{
|
||||
return d->atomDataType;
|
||||
return d->data->atomDataType;
|
||||
}
|
||||
|
||||
bool
|
||||
MP4::Item::toBool() const
|
||||
{
|
||||
return d->m_bool;
|
||||
return d->data->m_bool;
|
||||
}
|
||||
|
||||
int
|
||||
MP4::Item::toInt() const
|
||||
{
|
||||
return d->m_int;
|
||||
return d->data->m_int;
|
||||
}
|
||||
|
||||
unsigned char
|
||||
MP4::Item::toByte() const
|
||||
{
|
||||
return d->m_byte;
|
||||
return d->data->m_byte;
|
||||
}
|
||||
|
||||
unsigned int
|
||||
MP4::Item::toUInt() const
|
||||
{
|
||||
return d->m_uint;
|
||||
return d->data->m_uint;
|
||||
}
|
||||
|
||||
long long
|
||||
MP4::Item::toLongLong() const
|
||||
{
|
||||
return d->m_longlong;
|
||||
return d->data->m_longlong;
|
||||
}
|
||||
|
||||
MP4::Item::IntPair
|
||||
MP4::Item::toIntPair() const
|
||||
{
|
||||
return d->m_intPair;
|
||||
return d->data->m_intPair;
|
||||
}
|
||||
|
||||
StringList
|
||||
MP4::Item::toStringList() const
|
||||
{
|
||||
return d->m_stringList;
|
||||
return d->data->m_stringList;
|
||||
}
|
||||
|
||||
ByteVectorList
|
||||
MP4::Item::toByteVectorList() const
|
||||
{
|
||||
return d->m_byteVectorList;
|
||||
return d->data->m_byteVectorList;
|
||||
}
|
||||
|
||||
MP4::CoverArtList
|
||||
MP4::Item::toCoverArtList() const
|
||||
{
|
||||
return d->m_coverArtList;
|
||||
return d->data->m_coverArtList;
|
||||
}
|
||||
|
||||
bool
|
||||
MP4::Item::isValid() const
|
||||
{
|
||||
return d->valid;
|
||||
return d->data->valid;
|
||||
}
|
||||
|
||||
String
|
||||
MP4::Item::toString() const
|
||||
{
|
||||
StringList desc;
|
||||
switch (d->data->type) {
|
||||
case TypeBool:
|
||||
return d->data->m_bool ? "true" : "false";
|
||||
case TypeInt:
|
||||
return Utils::formatString("%d", d->data->m_int);
|
||||
case TypeIntPair:
|
||||
return Utils::formatString("%d/%d", d->data->m_intPair.first, d->data->m_intPair.second);
|
||||
case TypeByte:
|
||||
return Utils::formatString("%d", d->data->m_byte);
|
||||
case TypeUInt:
|
||||
return Utils::formatString("%u", d->data->m_uint);
|
||||
case TypeLongLong:
|
||||
return Utils::formatString("%lld", d->data->m_longlong);
|
||||
case TypeStringList:
|
||||
return d->data->m_stringList.toString(" / ");
|
||||
case TypeByteVectorList:
|
||||
for(size_t i = 0; i < d->data->m_byteVectorList.size(); i++) {
|
||||
desc.append(Utils::formatString(
|
||||
"[%d bytes of data]", static_cast<int>(d->data->m_byteVectorList[i].size())));
|
||||
}
|
||||
return desc.toString(", ");
|
||||
case TypeCoverArtList:
|
||||
for(size_t i = 0; i < d->data->m_coverArtList.size(); i++) {
|
||||
desc.append(Utils::formatString(
|
||||
"[%d bytes of data]", static_cast<int>(d->data->m_coverArtList[i].data().size())));
|
||||
}
|
||||
return desc.toString(", ");
|
||||
case TypeUndefined:
|
||||
return "[unknown]";
|
||||
}
|
||||
return String();
|
||||
}
|
||||
|
||||
@@ -41,6 +41,19 @@ namespace TagLib {
|
||||
int first, second;
|
||||
};
|
||||
|
||||
enum ItemType {
|
||||
TypeUndefined = 0,
|
||||
TypeBool,
|
||||
TypeInt,
|
||||
TypeIntPair,
|
||||
TypeByte,
|
||||
TypeUInt,
|
||||
TypeLongLong,
|
||||
TypeStringList,
|
||||
TypeByteVectorList,
|
||||
TypeCoverArtList,
|
||||
};
|
||||
|
||||
Item();
|
||||
Item(const Item &item);
|
||||
|
||||
@@ -79,8 +92,12 @@ namespace TagLib {
|
||||
ByteVectorList toByteVectorList() const;
|
||||
CoverArtList toCoverArtList() const;
|
||||
|
||||
ItemType type() const;
|
||||
|
||||
bool isValid() const;
|
||||
|
||||
String toString() const;
|
||||
|
||||
private:
|
||||
class ItemPrivate;
|
||||
ItemPrivate *d;
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
|
||||
using namespace TagLib;
|
||||
|
||||
class MP4::Properties::PropertiesPrivate
|
||||
class MP4::AudioProperties::PropertiesPrivate
|
||||
{
|
||||
public:
|
||||
PropertiesPrivate() :
|
||||
@@ -41,7 +41,7 @@ public:
|
||||
channels(0),
|
||||
bitsPerSample(0),
|
||||
encrypted(false),
|
||||
codec(MP4::Properties::Unknown) {}
|
||||
codec(MP4::AudioProperties::Unknown) {}
|
||||
|
||||
int length;
|
||||
int bitrate;
|
||||
@@ -56,78 +56,98 @@ public:
|
||||
// public members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
MP4::Properties::Properties(File *file, MP4::Atoms *atoms, ReadStyle style) :
|
||||
AudioProperties(style),
|
||||
MP4::AudioProperties::AudioProperties(File *file, MP4::Atoms *atoms, ReadStyle) :
|
||||
TagLib::AudioProperties(),
|
||||
d(new PropertiesPrivate())
|
||||
{
|
||||
read(file, atoms);
|
||||
}
|
||||
|
||||
MP4::Properties::~Properties()
|
||||
MP4::AudioProperties::~AudioProperties()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
|
||||
int
|
||||
MP4::Properties::channels() const
|
||||
MP4::AudioProperties::channels() const
|
||||
{
|
||||
return d->channels;
|
||||
}
|
||||
|
||||
int
|
||||
MP4::Properties::sampleRate() const
|
||||
MP4::AudioProperties::sampleRate() const
|
||||
{
|
||||
return d->sampleRate;
|
||||
}
|
||||
|
||||
int
|
||||
MP4::Properties::length() const
|
||||
MP4::AudioProperties::length() const
|
||||
{
|
||||
return lengthInSeconds();
|
||||
}
|
||||
|
||||
int
|
||||
MP4::Properties::lengthInSeconds() const
|
||||
MP4::AudioProperties::lengthInSeconds() const
|
||||
{
|
||||
return d->length / 1000;
|
||||
}
|
||||
|
||||
int
|
||||
MP4::Properties::lengthInMilliseconds() const
|
||||
MP4::AudioProperties::lengthInMilliseconds() const
|
||||
{
|
||||
return d->length;
|
||||
}
|
||||
|
||||
int
|
||||
MP4::Properties::bitrate() const
|
||||
MP4::AudioProperties::bitrate() const
|
||||
{
|
||||
return d->bitrate;
|
||||
}
|
||||
|
||||
int
|
||||
MP4::Properties::bitsPerSample() const
|
||||
MP4::AudioProperties::bitsPerSample() const
|
||||
{
|
||||
return d->bitsPerSample;
|
||||
}
|
||||
|
||||
bool
|
||||
MP4::Properties::isEncrypted() const
|
||||
MP4::AudioProperties::isEncrypted() const
|
||||
{
|
||||
return d->encrypted;
|
||||
}
|
||||
|
||||
MP4::Properties::Codec
|
||||
MP4::Properties::codec() const
|
||||
MP4::AudioProperties::Codec
|
||||
MP4::AudioProperties::codec() const
|
||||
{
|
||||
return d->codec;
|
||||
}
|
||||
|
||||
String
|
||||
MP4::AudioProperties::toString() const
|
||||
{
|
||||
String format;
|
||||
if(d->codec == AAC) {
|
||||
format = "AAC";
|
||||
}
|
||||
else if(d->codec == ALAC) {
|
||||
format = "ALAC";
|
||||
}
|
||||
else {
|
||||
format = "Unknown";
|
||||
}
|
||||
StringList desc;
|
||||
desc.append("MPEG-4 audio (" + format + ")");
|
||||
desc.append(String::number(length()) + " seconds");
|
||||
desc.append(String::number(bitrate()) + " kbps");
|
||||
return desc.toString(", ");
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// private members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void
|
||||
MP4::Properties::read(File *file, Atoms *atoms)
|
||||
MP4::AudioProperties::read(File *file, Atoms *atoms)
|
||||
{
|
||||
MP4::Atom *moov = atoms->find("moov");
|
||||
if(!moov) {
|
||||
@@ -147,7 +167,7 @@ MP4::Properties::read(File *file, Atoms *atoms)
|
||||
return;
|
||||
}
|
||||
file->seek(hdlr->offset);
|
||||
data = file->readBlock(hdlr->length);
|
||||
data = file->readBlock(static_cast<size_t>(hdlr->length));
|
||||
if(data.containsAt("soun", 16)) {
|
||||
break;
|
||||
}
|
||||
@@ -165,7 +185,7 @@ MP4::Properties::read(File *file, Atoms *atoms)
|
||||
}
|
||||
|
||||
file->seek(mdhd->offset);
|
||||
data = file->readBlock(mdhd->length);
|
||||
data = file->readBlock(static_cast<size_t>(mdhd->length));
|
||||
|
||||
const unsigned int version = data[8];
|
||||
long long unit;
|
||||
@@ -175,16 +195,16 @@ MP4::Properties::read(File *file, Atoms *atoms)
|
||||
debug("MP4: Atom 'trak.mdia.mdhd' is smaller than expected");
|
||||
return;
|
||||
}
|
||||
unit = data.toLongLong(28U);
|
||||
length = data.toLongLong(36U);
|
||||
unit = data.toUInt32BE(28);
|
||||
length = data.toInt64BE(32);
|
||||
}
|
||||
else {
|
||||
if(data.size() < 24 + 4) {
|
||||
if(data.size() < 24 + 8) {
|
||||
debug("MP4: Atom 'trak.mdia.mdhd' is smaller than expected");
|
||||
return;
|
||||
}
|
||||
unit = data.toUInt(20U);
|
||||
length = data.toUInt(24U);
|
||||
unit = data.toUInt32BE(20);
|
||||
length = data.toUInt32BE(24);
|
||||
}
|
||||
if(unit > 0 && length > 0)
|
||||
d->length = static_cast<int>(length * 1000.0 / unit + 0.5);
|
||||
@@ -195,12 +215,12 @@ MP4::Properties::read(File *file, Atoms *atoms)
|
||||
}
|
||||
|
||||
file->seek(atom->offset);
|
||||
data = file->readBlock(atom->length);
|
||||
data = file->readBlock(static_cast<size_t>(atom->length));
|
||||
if(data.containsAt("mp4a", 20)) {
|
||||
d->codec = AAC;
|
||||
d->channels = data.toShort(40U);
|
||||
d->bitsPerSample = data.toShort(42U);
|
||||
d->sampleRate = data.toUInt(46U);
|
||||
d->channels = data.toUInt16BE(40);
|
||||
d->bitsPerSample = data.toUInt16BE(42);
|
||||
d->sampleRate = data.toUInt32BE(46);
|
||||
if(data.containsAt("esds", 56) && data[64] == 0x03) {
|
||||
unsigned int pos = 65;
|
||||
if(data.containsAt("\x80\x80\x80", pos)) {
|
||||
@@ -213,7 +233,7 @@ MP4::Properties::read(File *file, Atoms *atoms)
|
||||
pos += 3;
|
||||
}
|
||||
pos += 10;
|
||||
d->bitrate = static_cast<int>((data.toUInt(pos) + 500) / 1000.0 + 0.5);
|
||||
d->bitrate = static_cast<int>((data.toUInt32BE(pos) + 500) / 1000.0 + 0.5);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -222,8 +242,8 @@ MP4::Properties::read(File *file, Atoms *atoms)
|
||||
d->codec = ALAC;
|
||||
d->bitsPerSample = data.at(69);
|
||||
d->channels = data.at(73);
|
||||
d->bitrate = static_cast<int>(data.toUInt(80U) / 1000.0 + 0.5);
|
||||
d->sampleRate = data.toUInt(84U);
|
||||
d->bitrate = static_cast<int>(data.toUInt32BE(80) / 1000.0 + 0.5);
|
||||
d->sampleRate = data.toUInt32BE(84);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ namespace TagLib {
|
||||
class File;
|
||||
|
||||
//! An implementation of MP4 audio properties
|
||||
class TAGLIB_EXPORT Properties : public AudioProperties
|
||||
class TAGLIB_EXPORT AudioProperties : public TagLib::AudioProperties
|
||||
{
|
||||
public:
|
||||
enum Codec {
|
||||
@@ -46,8 +46,8 @@ namespace TagLib {
|
||||
ALAC
|
||||
};
|
||||
|
||||
Properties(File *file, Atoms *atoms, ReadStyle style = Average);
|
||||
virtual ~Properties();
|
||||
AudioProperties(File *file, Atoms *atoms, ReadStyle style = Average);
|
||||
virtual ~AudioProperties();
|
||||
|
||||
/*!
|
||||
* Returns the length of the file in seconds. The length is rounded down to
|
||||
@@ -65,16 +65,14 @@ namespace TagLib {
|
||||
*
|
||||
* \see lengthInMilliseconds()
|
||||
*/
|
||||
// BIC: make virtual
|
||||
int lengthInSeconds() const;
|
||||
virtual int lengthInSeconds() const;
|
||||
|
||||
/*!
|
||||
* Returns the length of the file in milliseconds.
|
||||
*
|
||||
* \see lengthInSeconds()
|
||||
*/
|
||||
// BIC: make virtual
|
||||
int lengthInMilliseconds() const;
|
||||
virtual int lengthInMilliseconds() const;
|
||||
|
||||
/*!
|
||||
* Returns the average bit rate of the file in kb/s.
|
||||
@@ -106,6 +104,8 @@ namespace TagLib {
|
||||
*/
|
||||
Codec codec() const;
|
||||
|
||||
String toString() const;
|
||||
|
||||
private:
|
||||
void read(File *file, Atoms *atoms);
|
||||
|
||||
|
||||
@@ -22,9 +22,10 @@
|
||||
* License Version 1.1. You may obtain a copy of the License at *
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#include <cstring>
|
||||
#include <tdebug.h>
|
||||
#include <tstring.h>
|
||||
#include <tpicturemap.h>
|
||||
#include <tpropertymap.h>
|
||||
#include "mp4atom.h"
|
||||
#include "mp4tag.h"
|
||||
@@ -71,14 +72,15 @@ MP4::Tag::Tag(TagLib::File *file, MP4::Atoms *atoms) :
|
||||
parseIntPair(atom);
|
||||
}
|
||||
else if(atom->name == "cpil" || atom->name == "pgap" || atom->name == "pcst" ||
|
||||
atom->name == "hdvd") {
|
||||
atom->name == "hdvd" || atom->name == "shwm") {
|
||||
parseBool(atom);
|
||||
}
|
||||
else if(atom->name == "tmpo") {
|
||||
else if(atom->name == "tmpo" || atom->name == "rate" || atom->name == "\251mvi" || atom->name == "\251mvc") {
|
||||
parseInt(atom);
|
||||
}
|
||||
else if(atom->name == "tvsn" || atom->name == "tves" || atom->name == "cnID" ||
|
||||
atom->name == "sfID" || atom->name == "atID" || atom->name == "geID") {
|
||||
atom->name == "sfID" || atom->name == "atID" || atom->name == "geID" ||
|
||||
atom->name == "cmID") {
|
||||
parseUInt(atom);
|
||||
}
|
||||
else if(atom->name == "plID") {
|
||||
@@ -93,6 +95,9 @@ MP4::Tag::Tag(TagLib::File *file, MP4::Atoms *atoms) :
|
||||
else if(atom->name == "covr") {
|
||||
parseCovr(atom);
|
||||
}
|
||||
else if(atom->name == "purl" || atom->name == "egid") {
|
||||
parseText(atom, -1);
|
||||
}
|
||||
else {
|
||||
parseText(atom);
|
||||
}
|
||||
@@ -108,18 +113,18 @@ MP4::AtomDataList
|
||||
MP4::Tag::parseData2(const MP4::Atom *atom, int expectedFlags, bool freeForm)
|
||||
{
|
||||
AtomDataList result;
|
||||
ByteVector data = d->file->readBlock(atom->length - 8);
|
||||
ByteVector data = d->file->readBlock(static_cast<size_t>(atom->length - 8));
|
||||
int i = 0;
|
||||
unsigned int pos = 0;
|
||||
size_t pos = 0;
|
||||
while(pos < data.size()) {
|
||||
const int length = static_cast<int>(data.toUInt(pos));
|
||||
const int length = static_cast<int>(data.toUInt32BE(pos));
|
||||
if(length < 12) {
|
||||
debug("MP4: Too short atom");
|
||||
return result;
|
||||
}
|
||||
|
||||
const ByteVector name = data.mid(pos + 4, 4);
|
||||
const int flags = static_cast<int>(data.toUInt(pos + 8));
|
||||
const int flags = static_cast<int>(data.toUInt32BE(pos + 8));
|
||||
if(freeForm && i < 2) {
|
||||
if(i == 0 && name != "mean") {
|
||||
debug("MP4: Unexpected atom \"" + name + "\", expecting \"mean\"");
|
||||
@@ -162,7 +167,7 @@ MP4::Tag::parseInt(const MP4::Atom *atom)
|
||||
{
|
||||
ByteVectorList data = parseData(atom);
|
||||
if(!data.isEmpty()) {
|
||||
addItem(atom->name, (int)data[0].toShort());
|
||||
addItem(atom->name, (int)data[0].toInt16BE(0));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -171,7 +176,7 @@ MP4::Tag::parseUInt(const MP4::Atom *atom)
|
||||
{
|
||||
ByteVectorList data = parseData(atom);
|
||||
if(!data.isEmpty()) {
|
||||
addItem(atom->name, data[0].toUInt());
|
||||
addItem(atom->name, data[0].toUInt32BE(0));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -180,7 +185,7 @@ MP4::Tag::parseLongLong(const MP4::Atom *atom)
|
||||
{
|
||||
ByteVectorList data = parseData(atom);
|
||||
if(!data.isEmpty()) {
|
||||
addItem(atom->name, data[0].toLongLong());
|
||||
addItem(atom->name, data[0].toInt64BE(0));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -198,7 +203,7 @@ MP4::Tag::parseGnre(const MP4::Atom *atom)
|
||||
{
|
||||
ByteVectorList data = parseData(atom);
|
||||
if(!data.isEmpty()) {
|
||||
int idx = (int)data[0].toShort();
|
||||
int idx = (int)data[0].toInt16BE(0);
|
||||
if(idx > 0) {
|
||||
addItem("\251gen", StringList(ID3v1::genre(idx - 1)));
|
||||
}
|
||||
@@ -210,8 +215,8 @@ MP4::Tag::parseIntPair(const MP4::Atom *atom)
|
||||
{
|
||||
ByteVectorList data = parseData(atom);
|
||||
if(!data.isEmpty()) {
|
||||
const int a = data[0].toShort(2U);
|
||||
const int b = data[0].toShort(4U);
|
||||
const int a = data[0].toInt16BE(2);
|
||||
const int b = data[0].toInt16BE(4);
|
||||
addItem(atom->name, MP4::Item(a, b));
|
||||
}
|
||||
}
|
||||
@@ -284,17 +289,17 @@ void
|
||||
MP4::Tag::parseCovr(const MP4::Atom *atom)
|
||||
{
|
||||
MP4::CoverArtList value;
|
||||
ByteVector data = d->file->readBlock(atom->length - 8);
|
||||
ByteVector data = d->file->readBlock(static_cast<size_t>(atom->length - 8));
|
||||
unsigned int pos = 0;
|
||||
while(pos < data.size()) {
|
||||
const int length = static_cast<int>(data.toUInt(pos));
|
||||
const int length = static_cast<int>(data.toUInt32BE(pos));
|
||||
if(length < 12) {
|
||||
debug("MP4: Too short atom");
|
||||
break;;
|
||||
}
|
||||
|
||||
const ByteVector name = data.mid(pos + 4, 4);
|
||||
const int flags = static_cast<int>(data.toUInt(pos + 8));
|
||||
const int flags = static_cast<int>(data.toUInt32BE(pos + 8));
|
||||
if(name != "data") {
|
||||
debug("MP4: Unexpected atom \"" + name + "\", expecting \"data\"");
|
||||
break;
|
||||
@@ -317,7 +322,7 @@ ByteVector
|
||||
MP4::Tag::padIlst(const ByteVector &data, int length) const
|
||||
{
|
||||
if(length == -1) {
|
||||
length = ((data.size() + 1023) & ~1023) - data.size();
|
||||
length = static_cast<int>(((data.size() + 1023) & ~1023) - data.size());
|
||||
}
|
||||
return renderAtom("free", ByteVector(length, '\1'));
|
||||
}
|
||||
@@ -325,7 +330,7 @@ MP4::Tag::padIlst(const ByteVector &data, int length) const
|
||||
ByteVector
|
||||
MP4::Tag::renderAtom(const ByteVector &name, const ByteVector &data) const
|
||||
{
|
||||
return ByteVector::fromUInt(data.size() + 8) + name + data;
|
||||
return ByteVector::fromUInt32BE(data.size() + 8) + name + data;
|
||||
}
|
||||
|
||||
ByteVector
|
||||
@@ -333,7 +338,7 @@ MP4::Tag::renderData(const ByteVector &name, int flags, const ByteVectorList &da
|
||||
{
|
||||
ByteVector result;
|
||||
for(ByteVectorList::ConstIterator it = data.begin(); it != data.end(); ++it) {
|
||||
result.append(renderAtom("data", ByteVector::fromUInt(flags) + ByteVector(4, '\0') + *it));
|
||||
result.append(renderAtom("data", ByteVector::fromUInt32BE(flags) + ByteVector(4, '\0') + *it));
|
||||
}
|
||||
return renderAtom(name, result);
|
||||
}
|
||||
@@ -350,7 +355,7 @@ ByteVector
|
||||
MP4::Tag::renderInt(const ByteVector &name, const MP4::Item &item) const
|
||||
{
|
||||
ByteVectorList data;
|
||||
data.append(ByteVector::fromShort(item.toInt()));
|
||||
data.append(ByteVector::fromUInt16BE(item.toInt()));
|
||||
return renderData(name, TypeInteger, data);
|
||||
}
|
||||
|
||||
@@ -358,7 +363,7 @@ ByteVector
|
||||
MP4::Tag::renderUInt(const ByteVector &name, const MP4::Item &item) const
|
||||
{
|
||||
ByteVectorList data;
|
||||
data.append(ByteVector::fromUInt(item.toUInt()));
|
||||
data.append(ByteVector::fromUInt32BE(item.toUInt()));
|
||||
return renderData(name, TypeInteger, data);
|
||||
}
|
||||
|
||||
@@ -366,7 +371,7 @@ ByteVector
|
||||
MP4::Tag::renderLongLong(const ByteVector &name, const MP4::Item &item) const
|
||||
{
|
||||
ByteVectorList data;
|
||||
data.append(ByteVector::fromLongLong(item.toLongLong()));
|
||||
data.append(ByteVector::fromUInt64BE(item.toLongLong()));
|
||||
return renderData(name, TypeInteger, data);
|
||||
}
|
||||
|
||||
@@ -383,8 +388,8 @@ MP4::Tag::renderIntPair(const ByteVector &name, const MP4::Item &item) const
|
||||
{
|
||||
ByteVectorList data;
|
||||
data.append(ByteVector(2, '\0') +
|
||||
ByteVector::fromShort(item.toIntPair().first) +
|
||||
ByteVector::fromShort(item.toIntPair().second) +
|
||||
ByteVector::fromUInt16BE(item.toIntPair().first) +
|
||||
ByteVector::fromUInt16BE(item.toIntPair().second) +
|
||||
ByteVector(2, '\0'));
|
||||
return renderData(name, TypeImplicit, data);
|
||||
}
|
||||
@@ -394,8 +399,8 @@ MP4::Tag::renderIntPairNoTrailing(const ByteVector &name, const MP4::Item &item)
|
||||
{
|
||||
ByteVectorList data;
|
||||
data.append(ByteVector(2, '\0') +
|
||||
ByteVector::fromShort(item.toIntPair().first) +
|
||||
ByteVector::fromShort(item.toIntPair().second));
|
||||
ByteVector::fromUInt16BE(item.toIntPair().first) +
|
||||
ByteVector::fromUInt16BE(item.toIntPair().second));
|
||||
return renderData(name, TypeImplicit, data);
|
||||
}
|
||||
|
||||
@@ -416,7 +421,7 @@ MP4::Tag::renderCovr(const ByteVector &name, const MP4::Item &item) const
|
||||
ByteVector data;
|
||||
MP4::CoverArtList value = item.toCoverArtList();
|
||||
for(MP4::CoverArtList::ConstIterator it = value.begin(); it != value.end(); ++it) {
|
||||
data.append(renderAtom("data", ByteVector::fromUInt(it->format()) +
|
||||
data.append(renderAtom("data", ByteVector::fromUInt32BE(it->format()) +
|
||||
ByteVector(4, '\0') + it->data()));
|
||||
}
|
||||
return renderAtom(name, data);
|
||||
@@ -431,8 +436,8 @@ MP4::Tag::renderFreeForm(const String &name, const MP4::Item &item) const
|
||||
return ByteVector();
|
||||
}
|
||||
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)));
|
||||
data.append(renderAtom("mean", ByteVector::fromUInt32BE(0) + header[1].data(String::UTF8)));
|
||||
data.append(renderAtom("name", ByteVector::fromUInt32BE(0) + header[2].data(String::UTF8)));
|
||||
AtomDataType type = item.atomDataType();
|
||||
if(type == TypeUndefined) {
|
||||
if(!item.toStringList().isEmpty()) {
|
||||
@@ -445,13 +450,13 @@ MP4::Tag::renderFreeForm(const String &name, const MP4::Item &item) const
|
||||
if(type == TypeUTF8) {
|
||||
StringList value = item.toStringList();
|
||||
for(StringList::ConstIterator it = value.begin(); it != value.end(); ++it) {
|
||||
data.append(renderAtom("data", ByteVector::fromUInt(type) + ByteVector(4, '\0') + it->data(String::UTF8)));
|
||||
data.append(renderAtom("data", ByteVector::fromUInt32BE(type) + ByteVector(4, '\0') + it->data(String::UTF8)));
|
||||
}
|
||||
}
|
||||
else {
|
||||
ByteVectorList value = item.toByteVectorList();
|
||||
for(ByteVectorList::ConstIterator it = value.begin(); it != value.end(); ++it) {
|
||||
data.append(renderAtom("data", ByteVector::fromUInt(type) + ByteVector(4, '\0') + *it));
|
||||
data.append(renderAtom("data", ByteVector::fromUInt32BE(type) + ByteVector(4, '\0') + *it));
|
||||
}
|
||||
}
|
||||
return renderAtom("----", data);
|
||||
@@ -472,14 +477,16 @@ MP4::Tag::save()
|
||||
else if(name == "disk") {
|
||||
data.append(renderIntPairNoTrailing(name.data(String::Latin1), it->second));
|
||||
}
|
||||
else if(name == "cpil" || name == "pgap" || name == "pcst" || name == "hdvd") {
|
||||
else if(name == "cpil" || name == "pgap" || name == "pcst" || name == "hdvd" ||
|
||||
name == "shwm") {
|
||||
data.append(renderBool(name.data(String::Latin1), it->second));
|
||||
}
|
||||
else if(name == "tmpo") {
|
||||
else if(name == "tmpo" || name == "rate" || name == "\251mvi" || name == "\251mvc") {
|
||||
data.append(renderInt(name.data(String::Latin1), it->second));
|
||||
}
|
||||
else if(name == "tvsn" || name == "tves" || name == "cnID" ||
|
||||
name == "sfID" || name == "atID" || name == "geID") {
|
||||
name == "sfID" || name == "atID" || name == "geID" ||
|
||||
name == "cmID") {
|
||||
data.append(renderUInt(name.data(String::Latin1), it->second));
|
||||
}
|
||||
else if(name == "plID") {
|
||||
@@ -491,6 +498,9 @@ MP4::Tag::save()
|
||||
else if(name == "covr") {
|
||||
data.append(renderCovr(name.data(String::Latin1), it->second));
|
||||
}
|
||||
else if(name == "purl" || name == "egid") {
|
||||
data.append(renderText(name.data(String::Latin1), it->second, TypeImplicit));
|
||||
}
|
||||
else if(name.size() == 4){
|
||||
data.append(renderText(name.data(String::Latin1), it->second));
|
||||
}
|
||||
@@ -522,25 +532,25 @@ MP4::Tag::updateParents(const AtomList &path, long delta, int ignore)
|
||||
|
||||
for(AtomList::ConstIterator it = path.begin(); it != itEnd; ++it) {
|
||||
d->file->seek((*it)->offset);
|
||||
long size = d->file->readBlock(4).toUInt();
|
||||
long size = d->file->readBlock(4).toUInt32BE(0);
|
||||
// 64-bit
|
||||
if (size == 1) {
|
||||
d->file->seek(4, File::Current); // Skip name
|
||||
long long longSize = d->file->readBlock(8).toLongLong();
|
||||
long long longSize = d->file->readBlock(8).toInt64BE(0);
|
||||
// Seek the offset of the 64-bit size
|
||||
d->file->seek((*it)->offset + 8);
|
||||
d->file->writeBlock(ByteVector::fromLongLong(longSize + delta));
|
||||
d->file->writeBlock(ByteVector::fromUInt64BE(longSize + delta));
|
||||
}
|
||||
// 32-bit
|
||||
else {
|
||||
d->file->seek((*it)->offset);
|
||||
d->file->writeBlock(ByteVector::fromUInt(size + delta));
|
||||
d->file->writeBlock(ByteVector::fromUInt32BE(size + delta));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
MP4::Tag::updateOffsets(long delta, long offset)
|
||||
MP4::Tag::updateOffsets(long delta, long long offset)
|
||||
{
|
||||
MP4::Atom *moov = d->atoms->find("moov");
|
||||
if(moov) {
|
||||
@@ -551,16 +561,16 @@ MP4::Tag::updateOffsets(long delta, long offset)
|
||||
atom->offset += delta;
|
||||
}
|
||||
d->file->seek(atom->offset + 12);
|
||||
ByteVector data = d->file->readBlock(atom->length - 12);
|
||||
unsigned int count = data.toUInt();
|
||||
ByteVector data = d->file->readBlock(static_cast<size_t>(atom->length - 12));
|
||||
unsigned int count = data.toUInt32BE(0);
|
||||
d->file->seek(atom->offset + 16);
|
||||
unsigned int pos = 4;
|
||||
size_t pos = 4;
|
||||
while(count--) {
|
||||
long o = static_cast<long>(data.toUInt(pos));
|
||||
long o = data.toUInt32BE(pos);
|
||||
if(o > offset) {
|
||||
o += delta;
|
||||
}
|
||||
d->file->writeBlock(ByteVector::fromUInt(o));
|
||||
d->file->writeBlock(ByteVector::fromUInt32BE(o));
|
||||
pos += 4;
|
||||
}
|
||||
}
|
||||
@@ -572,16 +582,16 @@ MP4::Tag::updateOffsets(long delta, long offset)
|
||||
atom->offset += delta;
|
||||
}
|
||||
d->file->seek(atom->offset + 12);
|
||||
ByteVector data = d->file->readBlock(atom->length - 12);
|
||||
unsigned int count = data.toUInt();
|
||||
ByteVector data = d->file->readBlock(static_cast<size_t>(atom->length - 12));
|
||||
unsigned int count = data.toUInt32BE(0);
|
||||
d->file->seek(atom->offset + 16);
|
||||
unsigned int pos = 4;
|
||||
size_t pos = 4;
|
||||
while(count--) {
|
||||
long long o = data.toLongLong(pos);
|
||||
long long o = data.toInt64BE(pos);
|
||||
if(o > offset) {
|
||||
o += delta;
|
||||
}
|
||||
d->file->writeBlock(ByteVector::fromLongLong(o));
|
||||
d->file->writeBlock(ByteVector::fromUInt64BE(o));
|
||||
pos += 8;
|
||||
}
|
||||
}
|
||||
@@ -596,15 +606,15 @@ MP4::Tag::updateOffsets(long delta, long offset)
|
||||
atom->offset += delta;
|
||||
}
|
||||
d->file->seek(atom->offset + 9);
|
||||
ByteVector data = d->file->readBlock(atom->length - 9);
|
||||
const unsigned int flags = data.toUInt(0, 3, true);
|
||||
ByteVector data = d->file->readBlock(static_cast<size_t>(atom->length - 9));
|
||||
const unsigned int flags = data.toUInt24BE(0);
|
||||
if(flags & 1) {
|
||||
long long o = data.toLongLong(7U);
|
||||
long long o = data.toInt64BE(7);
|
||||
if(o > offset) {
|
||||
o += delta;
|
||||
}
|
||||
d->file->seek(atom->offset + 16);
|
||||
d->file->writeBlock(ByteVector::fromLongLong(o));
|
||||
d->file->writeBlock(ByteVector::fromUInt64BE(o));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -624,11 +634,11 @@ MP4::Tag::saveNew(ByteVector data)
|
||||
data = renderAtom("udta", data);
|
||||
}
|
||||
|
||||
long offset = path.back()->offset + 8;
|
||||
long long offset = path.back()->offset + 8;
|
||||
d->file->insert(data, offset, 0);
|
||||
|
||||
updateParents(path, data.size());
|
||||
updateOffsets(data.size(), offset);
|
||||
updateParents(path, static_cast<long>(data.size()));
|
||||
updateOffsets(static_cast<long>(data.size()), offset);
|
||||
|
||||
// Insert the newly created atoms into the tree to keep it up-to-date.
|
||||
|
||||
@@ -642,8 +652,8 @@ MP4::Tag::saveExisting(ByteVector data, const AtomList &path)
|
||||
AtomList::ConstIterator it = path.end();
|
||||
|
||||
MP4::Atom *ilst = *(--it);
|
||||
long offset = ilst->offset;
|
||||
long length = ilst->length;
|
||||
long long offset = ilst->offset;
|
||||
long long length = ilst->length;
|
||||
|
||||
MP4::Atom *meta = *(--it);
|
||||
AtomList::ConstIterator index = meta->children.find(ilst);
|
||||
@@ -668,17 +678,17 @@ MP4::Tag::saveExisting(ByteVector data, const AtomList &path)
|
||||
}
|
||||
}
|
||||
|
||||
long delta = data.size() - length;
|
||||
long delta = static_cast<long>(data.size() - length);
|
||||
if(delta > 0 || (delta < 0 && delta > -8)) {
|
||||
data.append(padIlst(data));
|
||||
delta = data.size() - length;
|
||||
delta = static_cast<long>(data.size() - length);
|
||||
}
|
||||
else if(delta < 0) {
|
||||
data.append(padIlst(data, -delta - 8));
|
||||
data.append(padIlst(data, 0 - delta - 8));
|
||||
delta = 0;
|
||||
}
|
||||
|
||||
d->file->insert(data, offset, length);
|
||||
d->file->insert(data, offset, static_cast<size_t>(length));
|
||||
|
||||
if(delta) {
|
||||
updateParents(path, delta, 1);
|
||||
@@ -742,6 +752,42 @@ MP4::Tag::track() const
|
||||
return 0;
|
||||
}
|
||||
|
||||
PictureMap
|
||||
MP4::Tag::pictures() const
|
||||
{
|
||||
if(!d->items.contains("covr"))
|
||||
return PictureMap();
|
||||
|
||||
CoverArtList list = d->items["covr"].toCoverArtList();
|
||||
if(list.isEmpty())
|
||||
return PictureMap();
|
||||
|
||||
PictureMap map;
|
||||
for(CoverArtList::ConstIterator it = list.begin(); it != list.end(); ++it) {
|
||||
CoverArt art = *it;
|
||||
String mime = "image/";
|
||||
switch(art.format()) {
|
||||
case CoverArt::BMP:
|
||||
mime.append("bmp");
|
||||
break;
|
||||
case CoverArt::JPEG:
|
||||
mime.append("jpeg");
|
||||
break;
|
||||
case CoverArt::GIF:
|
||||
mime.append("gif");
|
||||
break;
|
||||
case CoverArt::PNG:
|
||||
mime.append("png");
|
||||
break;
|
||||
case CoverArt::Unknown:
|
||||
break;
|
||||
}
|
||||
Picture picture(art.data(), Picture::Other, mime);
|
||||
map.insert(picture);
|
||||
}
|
||||
return PictureMap(map);
|
||||
}
|
||||
|
||||
void
|
||||
MP4::Tag::setTitle(const String &value)
|
||||
{
|
||||
@@ -784,6 +830,39 @@ MP4::Tag::setTrack(unsigned int value)
|
||||
d->items["trkn"] = MP4::Item(value, 0);
|
||||
}
|
||||
|
||||
void
|
||||
MP4::Tag::setPictures(const PictureMap &l)
|
||||
{
|
||||
CoverArtList list;
|
||||
for(PictureMap::ConstIterator pictureMapIt = l.begin();
|
||||
pictureMapIt != l.end();
|
||||
++pictureMapIt) {
|
||||
PictureList pictures = pictureMapIt->second;
|
||||
for(PictureList::ConstIterator pictureListIt = pictures.begin();
|
||||
pictureListIt != pictures.end();
|
||||
++pictureListIt) {
|
||||
Picture picture = *pictureListIt;
|
||||
CoverArt::Format format;
|
||||
String mime = picture.mime();
|
||||
if(String("image/") == mime)
|
||||
format = CoverArt::Unknown;
|
||||
else if(String("image/bmp") == mime)
|
||||
format = CoverArt::BMP;
|
||||
else if(String("image/png") == mime)
|
||||
format = CoverArt::PNG;
|
||||
else if(String("image/gif") == mime)
|
||||
format = CoverArt::GIF;
|
||||
else if(String("image/jpeg") == mime)
|
||||
format = CoverArt::JPEG;
|
||||
else
|
||||
format = CoverArt::Unknown;
|
||||
CoverArt art(format, picture.data());
|
||||
list.append(art);
|
||||
}
|
||||
}
|
||||
d->items["covr"] = list;
|
||||
}
|
||||
|
||||
bool MP4::Tag::isEmpty() const
|
||||
{
|
||||
return d->items.isEmpty();
|
||||
@@ -819,6 +898,16 @@ bool MP4::Tag::contains(const String &key) const
|
||||
return d->items.contains(key);
|
||||
}
|
||||
|
||||
String
|
||||
MP4::Tag::toString() const
|
||||
{
|
||||
StringList desc;
|
||||
for(MP4::ItemListMap::Iterator i = d->items.begin(); i != d->items.end(); i++) {
|
||||
desc.append(i->first + "=" + i->second.toString());
|
||||
}
|
||||
return desc.toString("\n");
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
const char *keyTranslation[][2] = {
|
||||
@@ -844,6 +933,11 @@ namespace
|
||||
{ "sonm", "TITLESORT" },
|
||||
{ "soco", "COMPOSERSORT" },
|
||||
{ "sosn", "SHOWSORT" },
|
||||
{ "shwm", "SHOWWORKMOVEMENT" },
|
||||
{ "\251wrk", "WORK" },
|
||||
{ "\251mvn", "MOVEMENTNAME" },
|
||||
{ "\251mvi", "MOVEMENTNUMBER" },
|
||||
{ "\251mvc", "MOVEMENTCOUNT" },
|
||||
{ "----:com.apple.iTunes:MusicBrainz Track Id", "MUSICBRAINZ_TRACKID" },
|
||||
{ "----:com.apple.iTunes:MusicBrainz Artist Id", "MUSICBRAINZ_ARTISTID" },
|
||||
{ "----:com.apple.iTunes:MusicBrainz Album Id", "MUSICBRAINZ_ALBUMID" },
|
||||
@@ -897,10 +991,10 @@ PropertyMap MP4::Tag::properties() const
|
||||
}
|
||||
props[key] = value;
|
||||
}
|
||||
else if(key == "BPM") {
|
||||
else if(key == "BPM" || key == "MOVEMENTNUMBER" || key == "MOVEMENTCOUNT") {
|
||||
props[key] = String::number(it->second.toInt());
|
||||
}
|
||||
else if(key == "COMPILATION") {
|
||||
else if(key == "COMPILATION" || key == "SHOWWORKMOVEMENT") {
|
||||
props[key] = String::number(it->second.toBool());
|
||||
}
|
||||
else {
|
||||
@@ -942,21 +1036,21 @@ PropertyMap MP4::Tag::setProperties(const PropertyMap &props)
|
||||
if(reverseKeyMap.contains(it->first)) {
|
||||
String name = reverseKeyMap[it->first];
|
||||
if((it->first == "TRACKNUMBER" || it->first == "DISCNUMBER") && !it->second.isEmpty()) {
|
||||
int first = 0, second = 0;
|
||||
StringList parts = StringList::split(it->second.front(), "/");
|
||||
if(!parts.isEmpty()) {
|
||||
first = parts[0].toInt();
|
||||
int first = parts[0].toInt();
|
||||
int second = 0;
|
||||
if(parts.size() > 1) {
|
||||
second = parts[1].toInt();
|
||||
}
|
||||
d->items[name] = MP4::Item(first, second);
|
||||
}
|
||||
}
|
||||
else if(it->first == "BPM" && !it->second.isEmpty()) {
|
||||
else if((it->first == "BPM" || it->first == "MOVEMENTNUMBER" || it->first == "MOVEMENTCOUNT") && !it->second.isEmpty()) {
|
||||
int value = it->second.front().toInt();
|
||||
d->items[name] = MP4::Item(value);
|
||||
}
|
||||
else if(it->first == "COMPILATION" && !it->second.isEmpty()) {
|
||||
else if((it->first == "COMPILATION" || it->first == "SHOWWORKMOVEMENT") && !it->second.isEmpty()) {
|
||||
bool value = (it->second.front().toInt() != 0);
|
||||
d->items[name] = MP4::Item(value);
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user