mirror of
https://github.com/taglib/taglib.git
synced 2026-06-07 14:59:24 -04:00
Compare commits
821 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bd4c9cbf97 | ||
|
|
7dc8bfc806 | ||
|
|
1a1ee8b54f | ||
|
|
cd044bfc8f | ||
|
|
7eda31e6ef | ||
|
|
0e6f888d4d | ||
|
|
b07db7510f | ||
|
|
1635d4d563 | ||
|
|
6d0f0ad170 | ||
|
|
eaf7955c63 | ||
|
|
78c489d9cc | ||
|
|
56fa36934e | ||
|
|
0dff3150c1 | ||
|
|
e414987344 | ||
|
|
47184c4447 | ||
|
|
ab0437db0e | ||
|
|
1ee7493abc | ||
|
|
d869f1b3e4 | ||
|
|
2044b31698 | ||
|
|
6e0741bcdc | ||
|
|
e9a671476b | ||
|
|
5b19c7aed3 | ||
|
|
98a9530d23 | ||
|
|
4775c83d8d | ||
|
|
034262c518 | ||
|
|
57c8dbe014 | ||
|
|
bfaf1be6a6 | ||
|
|
c9f7772198 | ||
|
|
549bca3382 | ||
|
|
98961813dc | ||
|
|
9a08678098 | ||
|
|
c1d8159a34 | ||
|
|
fa234fc053 | ||
|
|
b4fe04f8e0 | ||
|
|
c9486731d9 | ||
|
|
ec734bbe08 | ||
|
|
c7babea74a | ||
|
|
8a42e552aa | ||
|
|
28baa03b23 | ||
|
|
10094f66b9 | ||
|
|
aca1d0d41c | ||
|
|
491322d1ba | ||
|
|
8b3f1a459e | ||
|
|
131918333b | ||
|
|
cf2bdce21d | ||
|
|
51d63ab285 | ||
|
|
3f11e0ae2f | ||
|
|
bd06012b02 | ||
|
|
9233ff1f5d | ||
|
|
88219f2493 | ||
|
|
9df243ef74 | ||
|
|
c1bb678695 | ||
|
|
56382e8cd4 | ||
|
|
8bb8fc5fe6 | ||
|
|
8b564baf01 | ||
|
|
3d67b139e4 | ||
|
|
59166f6757 | ||
|
|
b993e70cf4 | ||
|
|
b7dc1ab8ac | ||
|
|
c86a2fce70 | ||
|
|
faddc4aa06 | ||
|
|
16326b2479 | ||
|
|
c083d7ce15 | ||
|
|
9679b08120 | ||
|
|
0e395f4ec4 | ||
|
|
52b245f015 | ||
|
|
2f4c76b52a | ||
|
|
dfa33bec08 | ||
|
|
f202fa25c3 | ||
|
|
70b4ce79fb | ||
|
|
8c63c877ad | ||
|
|
3869aa189f | ||
|
|
a7c0b53f7a | ||
|
|
135c0eb647 | ||
|
|
0db487bf61 | ||
|
|
8d98ebf24b | ||
|
|
11f94903d0 | ||
|
|
dce8e016b9 | ||
|
|
6b5f28d56d | ||
|
|
19cceab211 | ||
|
|
182edcd3f9 | ||
|
|
a3ad2d0aaa | ||
|
|
1d213b8b98 | ||
|
|
b40b834b1b | ||
|
|
6be03b7ae1 | ||
|
|
75d4252480 | ||
|
|
304ab62957 | ||
|
|
89a54863ee | ||
|
|
3f25d3c342 | ||
|
|
2e3a66cc31 | ||
|
|
41077aa57e | ||
|
|
21b08c0dcb | ||
|
|
8a800b8c38 | ||
|
|
c332aa04f2 | ||
|
|
24e0ac7aa4 | ||
|
|
ad1696ade6 | ||
|
|
c13a42021a | ||
|
|
2154078187 | ||
|
|
eaf7ff8b94 | ||
|
|
39d1d68237 | ||
|
|
e275abb8a3 | ||
|
|
967aaf7af2 | ||
|
|
48c4bf9c05 | ||
|
|
bdf50eda99 | ||
|
|
653c2fe9e1 | ||
|
|
6ed0ca28db | ||
|
|
cf5ad66922 | ||
|
|
97a74ca3d8 | ||
|
|
7646184d6b | ||
|
|
2dc1aa4ec9 | ||
|
|
00cfee30b6 | ||
|
|
88cb197111 | ||
|
|
110a253ba3 | ||
|
|
d42e8ed3fe | ||
|
|
37ba5a5cc1 | ||
|
|
23186f24ff | ||
|
|
528b84fbde | ||
|
|
912897cd35 | ||
|
|
54f84cc924 | ||
|
|
47c4e0859c | ||
|
|
524b588a1e | ||
|
|
303b55fb97 | ||
|
|
ecf1d4fa53 | ||
|
|
d2bd56c519 | ||
|
|
040b069957 | ||
|
|
f44ea9b80b | ||
|
|
0ba61343a4 | ||
|
|
cdc87aec10 | ||
|
|
4a86489186 | ||
|
|
85c678f587 | ||
|
|
29e88cfe66 | ||
|
|
128c55bc53 | ||
|
|
cf352ac7f4 | ||
|
|
bbb8221301 | ||
|
|
f69c21c8e6 | ||
|
|
574604765f | ||
|
|
f2d0e664e7 | ||
|
|
185bb7042e | ||
|
|
843a8aac80 | ||
|
|
dcef356e3f | ||
|
|
868f4eef3d | ||
|
|
c0e9428218 | ||
|
|
bec59b4b7b | ||
|
|
bd6c3ba174 | ||
|
|
ee8124ed7a | ||
|
|
965260e3cb | ||
|
|
f903e1ad71 | ||
|
|
a1bdb0171d | ||
|
|
9bcba812af | ||
|
|
271bd05afa | ||
|
|
c54c924333 | ||
|
|
d314bfa06a | ||
|
|
cc7d23cdf5 | ||
|
|
2a1fb27735 | ||
|
|
b5516c9718 | ||
|
|
231772b2ad | ||
|
|
77ab5e9689 | ||
|
|
c2c9e8989c | ||
|
|
63922f2676 | ||
|
|
b273505c28 | ||
|
|
abc6c31890 | ||
|
|
9867bc947e | ||
|
|
e20a53afbb | ||
|
|
ceb142c9bd | ||
|
|
322085f90e | ||
|
|
250dece2ab | ||
|
|
a33cc0635a | ||
|
|
ca8c2e07ec | ||
|
|
bc915f5dc8 | ||
|
|
e5cf30e1e9 | ||
|
|
c840222a39 | ||
|
|
39e712796f | ||
|
|
97203503b0 | ||
|
|
e21640bf10 | ||
|
|
a31356e330 | ||
|
|
9ef9514bfa | ||
|
|
967c0eefed | ||
|
|
abbf880872 | ||
|
|
e49724ae5f | ||
|
|
9427d8f3ba | ||
|
|
f40290dcaf | ||
|
|
983a35f5ae | ||
|
|
4dc6bdcd28 | ||
|
|
c963d1189a | ||
|
|
1ac61ffe19 | ||
|
|
a564d743f8 | ||
|
|
8c4d663393 | ||
|
|
bc5e56d3eb | ||
|
|
8aa7dd81d8 | ||
|
|
15ff32b685 | ||
|
|
084108a908 | ||
|
|
decc0fa50a | ||
|
|
02c7e34681 | ||
|
|
5f079d6992 | ||
|
|
2e90ec0ef0 | ||
|
|
9914519d43 | ||
|
|
f80d11ed2a | ||
|
|
4e7f844ea6 | ||
|
|
50b89ad19a | ||
|
|
0470c2894d | ||
|
|
10721b4b41 | ||
|
|
cebaf9a8e2 | ||
|
|
197d2a684b | ||
|
|
e255e749e8 | ||
|
|
a7eb7735ee | ||
|
|
0de8b45612 | ||
|
|
bdd8ff2af0 | ||
|
|
82964ba176 | ||
|
|
f7887e7235 | ||
|
|
8ab618da18 | ||
|
|
c98fba7cc4 | ||
|
|
52bf85f8ca | ||
|
|
a4bb904a01 | ||
|
|
05486d00bf | ||
|
|
c4a0855f42 | ||
|
|
2cb7973162 | ||
|
|
ff8a9ea831 | ||
|
|
fd66b0d3b6 | ||
|
|
f581615110 | ||
|
|
07eb81e88a | ||
|
|
c6f1e2750e | ||
|
|
a8884c2b17 | ||
|
|
76f00c5a8a | ||
|
|
68ac7c3106 | ||
|
|
38d1d4c21c | ||
|
|
b77e828d4b | ||
|
|
17e299350a | ||
|
|
1d24bd3399 | ||
|
|
4971f8fb03 | ||
|
|
d74689cb93 | ||
|
|
51ae5748cb | ||
|
|
f2eb331696 | ||
|
|
5f6bbb20e7 | ||
|
|
03d03f782e | ||
|
|
1d3e080f04 | ||
|
|
3391bd80c4 | ||
|
|
1644c0dd87 | ||
|
|
85cc41082c | ||
|
|
b5cd4c40e2 | ||
|
|
932d45259c | ||
|
|
844f07d32d | ||
|
|
304ab20174 | ||
|
|
243dd863d7 | ||
|
|
d77e2aee0b | ||
|
|
1e636957ab | ||
|
|
507a42871c | ||
|
|
01348fb619 | ||
|
|
9e0a7f7adb | ||
|
|
340ec9932a | ||
|
|
c8b39449c3 | ||
|
|
bd254654bc | ||
|
|
fbc3f9bbec | ||
|
|
99ad01e12f | ||
|
|
83aa01c6af | ||
|
|
4c14571647 | ||
|
|
f4b476a620 | ||
|
|
f8d78a61f7 | ||
|
|
cf6c68bafc | ||
|
|
f7c24930cd | ||
|
|
310c3bc043 | ||
|
|
e6c03c6de8 | ||
|
|
2c29fbeabb | ||
|
|
4828a3b925 | ||
|
|
794a2a0b2b | ||
|
|
f32d27973f | ||
|
|
f74b166435 | ||
|
|
c28dc78060 | ||
|
|
89fb62cfb1 | ||
|
|
59a2994f4e | ||
|
|
f3bdd416da | ||
|
|
39f86d02e7 | ||
|
|
1a2e1d08ac | ||
|
|
3f3b48353c | ||
|
|
54f5c66b65 | ||
|
|
3749dd7b75 | ||
|
|
d602ae483e | ||
|
|
5374cb1ac4 | ||
|
|
8a461ccedc | ||
|
|
17c2220588 | ||
|
|
271c004b30 | ||
|
|
25d9bd1814 | ||
|
|
b0bb7f8c0f | ||
|
|
4d3ab73d2e | ||
|
|
ae867cbd8c | ||
|
|
563fbaf82a | ||
|
|
e5ad041e42 | ||
|
|
9ca7b0c33a | ||
|
|
a00b3499b4 | ||
|
|
d84e86da9c | ||
|
|
741ed4e4bf | ||
|
|
e4813f4996 | ||
|
|
1332d44ff6 | ||
|
|
3d71ea1ad2 | ||
|
|
02c8995273 | ||
|
|
7e92af6e8b | ||
|
|
7ec1127f3e | ||
|
|
30d839538d | ||
|
|
8f6b6ac055 | ||
|
|
a5f11697f7 | ||
|
|
1721354627 | ||
|
|
ac7e5303a6 | ||
|
|
8ba246cdbe | ||
|
|
6656528f18 | ||
|
|
bad2cea122 | ||
|
|
61d5bcfd5b | ||
|
|
2dd6931eee | ||
|
|
91b00b17b2 | ||
|
|
e116824380 | ||
|
|
2db13ad8cf | ||
|
|
cd767738fc | ||
|
|
43bc11541d | ||
|
|
6806dc4cf2 | ||
|
|
ec9c49b964 | ||
|
|
872cafb0b9 | ||
|
|
47342f6974 | ||
|
|
5763d042a6 | ||
|
|
cd9e6b7502 | ||
|
|
54508df30b | ||
|
|
dcf0331eb1 | ||
|
|
96155f35fa | ||
|
|
ebd3373d6d | ||
|
|
f3ecfa11bb | ||
|
|
7082b9f66b | ||
|
|
de25bc6111 | ||
|
|
074f30e3fa | ||
|
|
f1b40de66b | ||
|
|
0b99cd9bac | ||
|
|
4668cf0f93 | ||
|
|
c05fa78406 | ||
|
|
b8dc105ae3 | ||
|
|
fced0f46e9 | ||
|
|
dc0f667a4c | ||
|
|
085180e9a4 | ||
|
|
ef1312d622 | ||
|
|
86c0428475 | ||
|
|
3c78c4cfc9 | ||
|
|
c8bb6271e5 | ||
|
|
84f7462526 | ||
|
|
2f23892182 | ||
|
|
2918602ad0 | ||
|
|
c146cd7e92 | ||
|
|
b124e621fe | ||
|
|
044fba2921 | ||
|
|
e72a98903f | ||
|
|
79bc9ccf8e | ||
|
|
850a3565a4 | ||
|
|
18d424995f | ||
|
|
d04db70bc6 | ||
|
|
ba7adc2bc2 | ||
|
|
79bb1428c0 | ||
|
|
7470f92a67 | ||
|
|
6455671ece | ||
|
|
660748210f | ||
|
|
1cf176af61 | ||
|
|
5cb589a5b8 | ||
|
|
a4d04e0c40 | ||
|
|
4ab0891646 | ||
|
|
d71398c953 | ||
|
|
bfed3797a0 | ||
|
|
e435372146 | ||
|
|
c2f544c9d1 | ||
|
|
8ca75f03b5 | ||
|
|
036a0317b9 | ||
|
|
8f6fe0b16c | ||
|
|
2c4ae870ec | ||
|
|
d8d56d3937 | ||
|
|
2d90d938fe | ||
|
|
a80093167f | ||
|
|
249f892455 | ||
|
|
cb9f07d9dc | ||
|
|
0b583bafd0 | ||
|
|
4648394841 | ||
|
|
e026d797e0 | ||
|
|
eeb2f5de09 | ||
|
|
1792ee9db8 | ||
|
|
1fb310ec1f | ||
|
|
c8bcd153fe | ||
|
|
662f340f93 | ||
|
|
48d8c0a808 | ||
|
|
5ebd3d5276 | ||
|
|
14af861d24 | ||
|
|
3795f277fb | ||
|
|
6045995e65 | ||
|
|
c2fe93c12b | ||
|
|
ea9202d9ee | ||
|
|
3c657d1a44 | ||
|
|
adf0f76360 | ||
|
|
682ea77c2b | ||
|
|
15c3c756ca | ||
|
|
4801fbb927 | ||
|
|
22de22b701 | ||
|
|
48a1a05a88 | ||
|
|
6000a19f70 | ||
|
|
ff28cf276c | ||
|
|
4891ee729d | ||
|
|
9419dab51b | ||
|
|
45ee18e206 | ||
|
|
bf7ee62dc6 | ||
|
|
f76b1e5429 | ||
|
|
f7b15fad20 | ||
|
|
dd4adf94ce | ||
|
|
d4d8410c08 | ||
|
|
931bb042c3 | ||
|
|
a5d9e49c49 | ||
|
|
179c175a6c | ||
|
|
ba98628919 | ||
|
|
87fc4012f4 | ||
|
|
dd5ab2a08f | ||
|
|
b74ffba4b5 | ||
|
|
4552f2c2eb | ||
|
|
6398796f95 | ||
|
|
2c7ac6d6a9 | ||
|
|
6a61f02f85 | ||
|
|
038b52ae01 | ||
|
|
598ab752bc | ||
|
|
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 | ||
|
|
a19a623d4b | ||
|
|
250c59f783 | ||
|
|
8eda5d5053 | ||
|
|
b5115e3497 | ||
|
|
36ccad2bd4 | ||
|
|
b00a5c1aab | ||
|
|
f6a604f54b | ||
|
|
489e2e6cbb | ||
|
|
9336c82da3 | ||
|
|
cfbaf34597 | ||
|
|
046c98230f | ||
|
|
4381bd75f3 | ||
|
|
6df61cf2af | ||
|
|
e9ef40fe7f | ||
|
|
2786aa7463 | ||
|
|
d3062f3af4 | ||
|
|
7871afec37 | ||
|
|
c9a0754e3b | ||
|
|
6cfb11bb12 | ||
|
|
ad075a56f9 | ||
|
|
f80a7c0d83 | ||
|
|
5e1d9fad31 | ||
|
|
eff28c55bf | ||
|
|
d5b9d7b8a7 | ||
|
|
ce77fbb0e7 | ||
|
|
b98a984b66 | ||
|
|
f9a747dceb | ||
|
|
7b8d576bde | ||
|
|
2651372291 | ||
|
|
499f6db977 | ||
|
|
9d58e9f8e8 | ||
|
|
56cd3695f7 | ||
|
|
d81d894d41 | ||
|
|
e390cbac52 | ||
|
|
253c61e37d | ||
|
|
1848b3bc6f | ||
|
|
aae23f3c07 | ||
|
|
da9df1b2a8 | ||
|
|
70334edd19 | ||
|
|
f5ca097379 | ||
|
|
eb6d058ab9 | ||
|
|
e6a69e24bc | ||
|
|
dcab8ed90e | ||
|
|
f5ce498182 | ||
|
|
ee7fa78011 | ||
|
|
873c917081 | ||
|
|
d3bd8fb7ff | ||
|
|
005441faaa | ||
|
|
935534aa53 | ||
|
|
65a24bbc51 | ||
|
|
711b35cc6e | ||
|
|
d53ca6f736 | ||
|
|
aa5f9bb221 | ||
|
|
d2b3547254 | ||
|
|
06ca9a099d | ||
|
|
8d873e4e3e | ||
|
|
b2fa124451 | ||
|
|
ff5b2dc96f | ||
|
|
757f5ebc96 | ||
|
|
e36a9cabb9 | ||
|
|
606f6c0e74 | ||
|
|
1cc047c953 | ||
|
|
cae4f1b804 | ||
|
|
deffe83fd0 | ||
|
|
597dcde72a | ||
|
|
6a96a6426a | ||
|
|
69c65284a5 | ||
|
|
97aaa0f979 | ||
|
|
1b64bb0cb7 | ||
|
|
0dac721ce2 | ||
|
|
bbeeca6fdb | ||
|
|
7e90313690 | ||
|
|
1d3c95f692 | ||
|
|
8c3801f18d | ||
|
|
c9bdd416ef | ||
|
|
9f28e037fe | ||
|
|
92c070ba9e | ||
|
|
75e3ec73aa | ||
|
|
3e47a036fb | ||
|
|
9b995544e4 | ||
|
|
d8e5077961 | ||
|
|
6422054540 | ||
|
|
1b878102f0 | ||
|
|
0a85f9b227 | ||
|
|
31f3109b47 | ||
|
|
76f8ff388f | ||
|
|
7627ae48ed | ||
|
|
b2a6768704 | ||
|
|
7d270a7e20 | ||
|
|
bf53dc6131 | ||
|
|
ff8b6a91e7 | ||
|
|
1a82419872 | ||
|
|
5a3265d031 | ||
|
|
a27199b772 | ||
|
|
6c27a32de8 | ||
|
|
11abffc0f6 | ||
|
|
2aea23aed2 | ||
|
|
e8ef0e0a4b | ||
|
|
520da50bc5 | ||
|
|
c0a909b709 | ||
|
|
8d09f12847 | ||
|
|
710166e32d | ||
|
|
01054009ac | ||
|
|
7d8aa7b8bd | ||
|
|
98a57744c3 | ||
|
|
8c4b484e67 | ||
|
|
2166d703e0 | ||
|
|
887f3b28e5 | ||
|
|
6a9db50c03 | ||
|
|
07d95e0dc0 | ||
|
|
a9acca5d81 | ||
|
|
dadfe79799 | ||
|
|
552185a8b8 | ||
|
|
46eacaeba4 | ||
|
|
455e827e1e | ||
|
|
c08e0b1357 | ||
|
|
f3016c0892 | ||
|
|
f8f7cb0904 | ||
|
|
d037b8c908 | ||
|
|
c349ba3a31 | ||
|
|
bb006e41d7 | ||
|
|
ab2267f9aa | ||
|
|
73662037eb | ||
|
|
aa339eba87 | ||
|
|
45a3bdb695 | ||
|
|
5a155ef4ce | ||
|
|
184a151d41 | ||
|
|
cc2ccbc20c | ||
|
|
fa46185ca4 | ||
|
|
fffff35ca5 | ||
|
|
31894f47f6 | ||
|
|
c1e3d65b68 | ||
|
|
a8ecbbaef4 | ||
|
|
24575aab23 | ||
|
|
c04b24a2f5 | ||
|
|
013fbbf22c | ||
|
|
5350bc8501 | ||
|
|
92a1a00624 | ||
|
|
8afbf6c92a | ||
|
|
9976155aa9 | ||
|
|
c2cb9ab8b0 | ||
|
|
758b7e39ce | ||
|
|
4ba7bb5a8a | ||
|
|
0b62ba1530 | ||
|
|
fe92f3dffe | ||
|
|
b8e82a7775 | ||
|
|
3dc873b7d0 | ||
|
|
1254534ed4 | ||
|
|
7a5fb7d672 | ||
|
|
5b7ee5bc1a | ||
|
|
815d1583e9 | ||
|
|
6c183a7df8 | ||
|
|
8d5f821b16 | ||
|
|
0611744171 | ||
|
|
c25e0c6ead | ||
|
|
45762d20f1 | ||
|
|
67434aa7b4 | ||
|
|
cb23f91c98 | ||
|
|
37c4d0b11d | ||
|
|
626d13467e | ||
|
|
02eac18d00 | ||
|
|
4064b34eff | ||
|
|
22708a0af6 | ||
|
|
12da0ebd6d | ||
|
|
d13a3e3e16 | ||
|
|
e8b28a0466 | ||
|
|
081d6eaf76 | ||
|
|
ac361c7692 | ||
|
|
d7de3983d9 | ||
|
|
1aca1f64fe | ||
|
|
5a1f44d166 | ||
|
|
130654316b | ||
|
|
5cd139a578 | ||
|
|
dd2ed47703 | ||
|
|
d0238ba82f | ||
|
|
cf5d431d77 | ||
|
|
4ec264b6a1 | ||
|
|
ef92ee223f | ||
|
|
8b6bcf411a | ||
|
|
aa66b0651d | ||
|
|
7c5d1e02de | ||
|
|
6d27a1013d | ||
|
|
50711f4758 | ||
|
|
6998be94f6 | ||
|
|
862952bdcc | ||
|
|
f0b0b736e3 | ||
|
|
546870d83a | ||
|
|
581e58585a | ||
|
|
36b905670c | ||
|
|
42459fe457 | ||
|
|
fc571e5150 | ||
|
|
5480458dfc | ||
|
|
9950fca3c2 | ||
|
|
63e838f831 | ||
|
|
94f28b4fa1 | ||
|
|
680784c7b7 | ||
|
|
5d517127ae | ||
|
|
be9b5cc93a | ||
|
|
847eec23cf | ||
|
|
9dfb3fe452 | ||
|
|
a013c7c4c2 | ||
|
|
62ec63b039 | ||
|
|
52d3122e9e | ||
|
|
24f220224d | ||
|
|
a1ac23530e | ||
|
|
e99910dd74 | ||
|
|
88ad2843f4 | ||
|
|
a0b8683656 | ||
|
|
085a0ef298 | ||
|
|
363c471a2d | ||
|
|
7f8efec4d4 | ||
|
|
dcc0fe553c | ||
|
|
8bdddaabce | ||
|
|
2cb89b85c9 | ||
|
|
e6e9ba6094 | ||
|
|
7b854c5434 | ||
|
|
1529af7a12 | ||
|
|
e475e31e0d | ||
|
|
ea55c8b5c1 | ||
|
|
bc8b60ea46 | ||
|
|
83ffc87598 | ||
|
|
d97292c593 | ||
|
|
060a50ab11 | ||
|
|
6966be4292 | ||
|
|
f38749aacf | ||
|
|
b4b16fd7e0 | ||
|
|
b1c67fc5dc | ||
|
|
90f1f03163 | ||
|
|
101a330d55 | ||
|
|
59088096e6 | ||
|
|
d1ceffd1f0 | ||
|
|
eb6542bc8b | ||
|
|
bccd57a470 | ||
|
|
a6701ddcda | ||
|
|
31d1f11969 | ||
|
|
f9e558eef5 | ||
|
|
fcdf7c2ae2 | ||
|
|
8c424badad | ||
|
|
f1b683b582 | ||
|
|
5e37f0101d | ||
|
|
b541ec8b68 | ||
|
|
1fa677e2ef | ||
|
|
463e8f313a | ||
|
|
8130b30397 | ||
|
|
6b836c6ba7 | ||
|
|
f5a3f2b6c1 | ||
|
|
4b8e39e8f0 | ||
|
|
905bf89e66 | ||
|
|
67cbf2c0a8 | ||
|
|
3f84621d58 | ||
|
|
083eadec60 | ||
|
|
f180445be5 | ||
|
|
cbc279b899 | ||
|
|
e1bcb101f0 | ||
|
|
14f3b6c928 | ||
|
|
b6361ddde5 | ||
|
|
66dd0b8a6f | ||
|
|
94b7828990 | ||
|
|
fa4289e044 | ||
|
|
3612c2cc24 | ||
|
|
477aba9465 | ||
|
|
a640074a21 | ||
|
|
25ffbcb4b9 | ||
|
|
e44de9f37f | ||
|
|
2b7d6fef47 | ||
|
|
ae633105d6 | ||
|
|
6978131d22 | ||
|
|
83a0bc3710 | ||
|
|
6477132301 | ||
|
|
559c6b28c9 | ||
|
|
e9f70a59b2 | ||
|
|
f34d73d319 | ||
|
|
ef3ce1e38a | ||
|
|
8724184d56 | ||
|
|
61dabe61a7 | ||
|
|
de0fc83066 | ||
|
|
c3807e59cd | ||
|
|
b592f78238 | ||
|
|
84e3582332 | ||
|
|
ce1c03faa3 | ||
|
|
94c941928a | ||
|
|
8c6fe45453 | ||
|
|
c4fe65787c | ||
|
|
29b0568dec | ||
|
|
7d0a651f3e | ||
|
|
224f133d65 | ||
|
|
f956c89141 | ||
|
|
6dc8d701a8 | ||
|
|
779f904940 | ||
|
|
4161746d89 | ||
|
|
6d5654028a | ||
|
|
93d55dafd6 | ||
|
|
e1e1b6c60c | ||
|
|
1cc3e4cc57 | ||
|
|
f310b1810d | ||
|
|
e10684312e | ||
|
|
a3564d8c68 | ||
|
|
95ef0e7882 | ||
|
|
13a258d9ed | ||
|
|
72a807def1 | ||
|
|
539d951277 | ||
|
|
21788f4a26 | ||
|
|
74cb6f3fc7 | ||
|
|
e0f1151c5c | ||
|
|
10e1fcd686 | ||
|
|
3bce77a359 | ||
|
|
0ffd2e8ab9 | ||
|
|
7e85d9b202 | ||
|
|
c6443dabc6 | ||
|
|
c5db39fbf4 | ||
|
|
b6469bda1a | ||
|
|
978a5f0a27 | ||
|
|
8203ccf04c | ||
|
|
47860c20f4 | ||
|
|
6e6e11f21a | ||
|
|
288e97ad44 | ||
|
|
07b26ab3e4 | ||
|
|
0f00dfc778 | ||
|
|
b01fecd280 | ||
|
|
69921b8090 | ||
|
|
1caaa8a020 | ||
|
|
4f3844114a | ||
|
|
76de4234a1 | ||
|
|
4bac99e3da | ||
|
|
6b7a2c4cf7 | ||
|
|
1a942627bf | ||
|
|
3128f425b8 | ||
|
|
3f968933f4 | ||
|
|
67f44071cd | ||
|
|
11fbf394a3 | ||
|
|
a25e1e9f90 | ||
|
|
091ab9dee0 | ||
|
|
c353a71ce5 | ||
|
|
762581fe3f | ||
|
|
8f147034d6 | ||
|
|
6775cef651 | ||
|
|
86c7e905ba | ||
|
|
2184aa476f | ||
|
|
3e8eff16b8 | ||
|
|
41a44f4ab2 | ||
|
|
70e471f1d1 | ||
|
|
f836cb4dea | ||
|
|
47bd01ecda | ||
|
|
90ad17b4c5 | ||
|
|
f4c1beb161 | ||
|
|
c963d3ba04 | ||
|
|
c4a02a1799 | ||
|
|
59a1b7a491 | ||
|
|
62bb3c95c8 | ||
|
|
d3351a0012 | ||
|
|
0c14bd0ed0 | ||
|
|
7bbf5a2e79 | ||
|
|
ba5137bf2d | ||
|
|
b3cea6cca7 | ||
|
|
a533b9c665 | ||
|
|
ec8e611909 | ||
|
|
c66e6b27d9 | ||
|
|
1d379fdb2f | ||
|
|
47813c5a7f | ||
|
|
94ff9124c7 | ||
|
|
e750cb491d | ||
|
|
1f7715a5da | ||
|
|
8e7504a66f | ||
|
|
9ad5bb1d62 | ||
|
|
b6adb1f108 | ||
|
|
c7231c58a3 | ||
|
|
c5cf9b93bc | ||
|
|
1bb06b1f7a | ||
|
|
35d5ba4eff | ||
|
|
04bee3faec | ||
|
|
e8c1a11730 | ||
|
|
d8e8ec69fe | ||
|
|
6d0712c8df | ||
|
|
00b2f6a386 | ||
|
|
a3dccdc7a3 | ||
|
|
b698c73690 | ||
|
|
00c6e7ea79 | ||
|
|
b5c0ab58ba | ||
|
|
de17843d8c | ||
|
|
944143a91b |
@@ -8,12 +8,12 @@ root = true
|
||||
insert_final_newline = true
|
||||
|
||||
# 2 space indentation
|
||||
[*.{h,cpp,tcc,cmake}]
|
||||
[*.{h,cpp,tcc,cmake,yml}]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
|
||||
# Trim traling whitespaces
|
||||
[*.{h,cpp,tcc,cmake}]
|
||||
# Trim trailing whitespaces
|
||||
[*.{h,cpp,tcc,cmake,yml}]
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
# UTF-8 without BOM
|
||||
|
||||
6
.github/dependabot.yml
vendored
Normal file
6
.github/dependabot.yml
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
59
.github/workflows/build.yml
vendored
Normal file
59
.github/workflows/build.yml
vendored
Normal file
@@ -0,0 +1,59 @@
|
||||
name: Build
|
||||
|
||||
on: [push, pull_request, workflow_dispatch]
|
||||
|
||||
env:
|
||||
BUILD_TYPE: Release
|
||||
|
||||
jobs:
|
||||
build:
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, macos-latest, windows-latest]
|
||||
include:
|
||||
- os: windows-latest
|
||||
cmake_extra_args: '-DCMAKE_TOOLCHAIN_FILE="$env:VCPKG_INSTALLATION_ROOT/scripts/buildsystems/vcpkg.cmake"'
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Ubuntu
|
||||
run: sudo apt install -y libcppunit-dev libutfcpp-dev zlib1g-dev
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
|
||||
- name: Set up macOS
|
||||
run: |
|
||||
brew update
|
||||
brew install cppunit utf8cpp
|
||||
if: matrix.os == 'macos-latest'
|
||||
|
||||
- name: Set up Windows
|
||||
run: vcpkg install cppunit utfcpp zlib --triplet x64-windows
|
||||
if: matrix.os == 'windows-latest'
|
||||
|
||||
- name: Configure
|
||||
run: >
|
||||
cmake -B${{github.workspace}}/build
|
||||
-DBUILD_SHARED_LIBS=ON -DVISIBILITY_HIDDEN=ON
|
||||
-DBUILD_TESTING=ON -DBUILD_EXAMPLES=ON -DBUILD_BINDINGS=ON
|
||||
-DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}}
|
||||
${{ matrix.cmake_extra_args }}
|
||||
|
||||
- name: Build
|
||||
run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}}
|
||||
|
||||
- name: Test
|
||||
working-directory: ${{github.workspace}}/build
|
||||
run: ctest -C ${{env.BUILD_TYPE}} -V --no-tests=error
|
||||
if: matrix.os != 'windows-latest'
|
||||
|
||||
- name: Test Windows
|
||||
working-directory: ${{github.workspace}}/build
|
||||
run: |
|
||||
$env:Path += ";$PWD\taglib\Release;$PWD\bindings\c\Release"
|
||||
$env:Path += ";$env:VCPKG_INSTALLATION_ROOT\packages\cppunit_x64-windows\bin"
|
||||
$env:Path += ";$env:VCPKG_INSTALLATION_ROOT\packages\utfcpp_x64-windows\bin"
|
||||
$env:Path += ";$env:VCPKG_INSTALLATION_ROOT\packages\zlib_x64-windows\bin"
|
||||
ctest -C ${{env.BUILD_TYPE}} -V --no-tests=error
|
||||
if: matrix.os == 'windows-latest'
|
||||
11
.gitignore
vendored
11
.gitignore
vendored
@@ -3,6 +3,7 @@ cmake_uninstall.cmake
|
||||
Makefile
|
||||
CTestTestfile.cmake
|
||||
CMakeFiles/
|
||||
CMakeLists.txt.user*
|
||||
*.so
|
||||
*.so.*
|
||||
*.dylib
|
||||
@@ -12,6 +13,7 @@ CMakeFiles/
|
||||
*.suo
|
||||
*.user
|
||||
.*
|
||||
!.github/workflows/
|
||||
*~
|
||||
/CMakeCache.txt
|
||||
/Doxyfile
|
||||
@@ -19,8 +21,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 +46,12 @@ CMakeFiles/
|
||||
/taglib/tag.dir/Release
|
||||
/ALL_BUILD.dir
|
||||
/ZERO_CHECK.dir
|
||||
taglib.h.stamp
|
||||
taglib.xcodeproj
|
||||
CMakeScripts
|
||||
/.clang-format
|
||||
/compile_commands.json
|
||||
/build/
|
||||
.clangd
|
||||
.cache
|
||||
.idea
|
||||
|
||||
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
[submodule "3rdparty/utfcpp"]
|
||||
path = 3rdparty/utfcpp
|
||||
url = https://github.com/nemtrif/utfcpp.git
|
||||
12
.travis.yml
12
.travis.yml
@@ -1,12 +0,0 @@
|
||||
language: cpp
|
||||
sudo: false
|
||||
compiler:
|
||||
- gcc
|
||||
- clang
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- libcppunit-dev
|
||||
- zlib1g-dev
|
||||
script: cmake -DBUILD_TESTS=ON -DBUILD_EXAMPLES=ON . && make && make check
|
||||
|
||||
1
3rdparty/utfcpp
vendored
Submodule
1
3rdparty/utfcpp
vendored
Submodule
Submodule 3rdparty/utfcpp added at df857efc5b
10
AUTHORS
10
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,10 @@ 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
|
||||
Urs Fleisch <ufleisch@users.sourceforge.net>
|
||||
Bug fixes, maintainer.
|
||||
|
||||
Please send all patches and questions to taglib-devel@kde.org rather than to
|
||||
individual developers!
|
||||
|
||||
493
CHANGELOG.md
Normal file
493
CHANGELOG.md
Normal file
@@ -0,0 +1,493 @@
|
||||
TagLib 2.0 (Jan 24, 2024)
|
||||
=========================
|
||||
|
||||
* New major version, binary incompatible, but source-compatible with the
|
||||
latest 1.x release if no deprecated features are used.
|
||||
* Requires a C++17 compiler and uses features of C++17.
|
||||
* Major code cleanup, fixed warnings issued by compilers and static analyzers.
|
||||
* Made methods virtual which should have been virtual but could not be
|
||||
changed to keep binary compatibility, remove related workarounds.
|
||||
* Removed deprecated functions:
|
||||
- APE::Item::Item(const String &, const String &)
|
||||
- APE::Item::toStringList(): Use values()
|
||||
- APE::Item::value(): Use binaryData()
|
||||
- ASF::Properties::setLength()
|
||||
- ByteVector::checksum()
|
||||
- ByteVector::isNull(): Use isEmpty()
|
||||
- ByteVector::null
|
||||
- FLAC::File::setID3v2FrameFactory()
|
||||
- FLAC::File::streamInfoData()
|
||||
- FLAC::File::streamLength()
|
||||
- FLAC::Properties::Properties(File *, ReadStyle)
|
||||
- FLAC::Properties::sampleWidth(): Use bitsPerSample()
|
||||
- File::isReadable(): Use system functions
|
||||
- File::isWritable(): Use system functions
|
||||
- FileName::str()
|
||||
- FileRef::create(): Use constructor
|
||||
- MP4::Tag::itemListMap(): Use itemMap()
|
||||
- MPC::File::remove(): Use strip()
|
||||
- MPC::Properties::Properties(const ByteVector &, long, ReadStyle)
|
||||
- MPEG::File::save(int, ...): Use overload
|
||||
- MPEG::File::setID3v2FrameFactory(): Use constructor
|
||||
- MPEG::ID3v2::Frame::Header::Header(const ByteVector &, bool)
|
||||
- MPEG::ID3v2::Frame::Header::frameAlterPreservation(): Use
|
||||
fileAlterPreservation()
|
||||
- MPEG::ID3v2::Frame::Header::setData(const ByteVector &, bool)
|
||||
- MPEG::ID3v2::Frame::Header::size(unsigned int): Use size()
|
||||
- MPEG::ID3v2::Frame::Header::unsycronisation(): use unsynchronisation()
|
||||
- MPEG::ID3v2::Frame::checkEncoding(const StringList &, String::Type): Use
|
||||
checkTextEncoding(const StringList &, String::Type)
|
||||
- MPEG::ID3v2::Frame::headerSize(): Use Header::size()
|
||||
- MPEG::ID3v2::Frame::headerSize(unsigned int): Use
|
||||
Header::size(unsigned int)
|
||||
- MPEG::ID3v2::FrameFactory::createFrame(const ByteVector &, bool)
|
||||
- MPEG::ID3v2::FrameFactory::createFrame(const ByteVector &, unsigned int):
|
||||
Use createFrame(const ByteVector &, const Header *)
|
||||
- MPEG::ID3v2::RelativeVolumeFrame::channelType()
|
||||
- MPEG::ID3v2::RelativeVolumeFrame::peakVolume(): Use peakVolume(ChannelType)
|
||||
- MPEG::ID3v2::RelativeVolumeFrame::setChannelType()
|
||||
- MPEG::ID3v2::RelativeVolumeFrame::setPeakVolume(const PeakVolume &): Use
|
||||
setPeakVolume(const PeakVolume &, ChannelType)
|
||||
- MPEG::ID3v2::RelativeVolumeFrame::setVolumeAdjustment(float): Use
|
||||
setVolumeAdjustment(float, ChannelType)
|
||||
- MPEG::ID3v2::RelativeVolumeFrame::setVolumeAdjustmentIndex(short): Use
|
||||
setVolumeAdjustmentIndex(short, ChannelType)
|
||||
- MPEG::ID3v2::RelativeVolumeFrame::volumeAdjustment(): Use
|
||||
volumeAdjustment(ChannelType)
|
||||
- MPEG::ID3v2::RelativeVolumeFrame::volumeAdjustmentIndex(): Use
|
||||
volumeAdjustmentIndex(ChannelType)
|
||||
- MPEG::ID3v2::Tag::footer()
|
||||
- MPEG::ID3v2::Tag::render(int): Use render(Version)
|
||||
- MPEG::XingHeader::xingHeaderOffset()
|
||||
- Ogg::Page::getCopyWithNewPageSequenceNumber()
|
||||
- Ogg::XiphComment::removeField(): Use removeFields()
|
||||
- PropertyMap::unsupportedData(): Returns now const reference, use
|
||||
addUnsupportedData() to add keys
|
||||
- RIFF::AIFF::Properties::Properties(const ByteVector &, ReadStyle)
|
||||
- RIFF::AIFF::Properties::Properties(const ByteVector &, int, ReadStyle)
|
||||
- RIFF::AIFF::Properties::sampleWidth(): Use bitsPerSample()
|
||||
- RIFF::WAV::File::save(TagTypes, bool, int): Use
|
||||
save(TagTypes, StripTags, Version)
|
||||
- RIFF::WAV::File::tag(): Returns now a TagUnion, use ID3v2Tag() to get an
|
||||
ID3v2::Tag
|
||||
- String::isNull(): Use isEmpty()
|
||||
- String::null
|
||||
- TrueAudio::File::setID3v2FrameFactory(): Use constructor
|
||||
- WavPack::Properties::Properties(const ByteVector &, long, ReadStyle)
|
||||
* Behavioral changes:
|
||||
- The basic tag methods (e.g. genre()) separate multiple values with " / "
|
||||
instead of " ".
|
||||
- The stream operator for String uses UTF-8 instead of ISO-8859-1 encoding.
|
||||
- MP4 property ORIGINALDATE is mapped to "----:com.apple.iTunes:ORIGINALDATE"
|
||||
instead of "----:com.apple.iTunes:originaldate".
|
||||
- MP4 property ENCODEDBY is mapped to "©enc" instead of "©too", which is now
|
||||
mapped to ENCODING.
|
||||
* Unified interface for complex properties like pictures.
|
||||
* Simplified the unified properties interface by providing its methods on
|
||||
FileRef.
|
||||
* C bindings: Support for properties (taglib_property_...) and complex
|
||||
properties like cover art (taglib_complex_property_...), memory I/O streams.
|
||||
* Support for Direct Stream Digital (DSD) stream files (DSF) and interchange
|
||||
file format (DSDIFF, DFF), ADTS (AAC) files.
|
||||
* The runtime version can be queried.
|
||||
* Additional utility functions ByteVector::fromUShort(),
|
||||
ByteVector::fromULongLong(), ByteVector::toULongLong(),
|
||||
ByteVector::toULongLong(), List::sort().
|
||||
* Fixed List::setAutoDelete() affecting implicitly shared copies.
|
||||
* Build system: Direct support for CMake, find_package(TagLib) exports target
|
||||
TagLib::tag.
|
||||
* Build system: Fixed PackageConfig to support both relative and absolute paths.
|
||||
* Build system: utf8cpp is no longer included, it can be provided via a system
|
||||
package or a Git submodule.
|
||||
* ASF: Support additional properties ARTISTWEBPAGE, ENCODING, ENCODINGTIME,
|
||||
FILEWEBPAGE, INITIALKEY, ORIGINALALBUM, ORIGINALARTIST, ORIGINALFILENAME,
|
||||
ORIGINALLYRICIST.
|
||||
* ID3v2: Fixed extensibility of FrameFactory, use it also for WAV and AIFF
|
||||
files.
|
||||
* MP4: Support additional properties OWNER, RELEASEDATE.
|
||||
* MP4: Introduced ItemFactory allowing clients to support new atom types.
|
||||
* MP4: Detect duration from mvhd atom if not present in mdhd atom.
|
||||
* MP4: Fixed type of hdvd atom to be integer instead of boolean.
|
||||
* MP4: Tolerate trailing garbage in M4A files.
|
||||
* MPC: Fixed content check in presence of an ID3v2 tag.
|
||||
* MPEG: Do not scan full file for ID3v2 tag when ReadStyle Fast is used.
|
||||
* RIFF: Support properties ALBUM, ARRANGER, ARTIST, ARTISTWEBPAGE, BPM,
|
||||
COMMENT, COMPOSER, COPYRIGHT, DATE, DISCSUBTITLE, ENCODEDBY, ENCODING,
|
||||
ENCODINGTIME, GENRE, ISRC, LABEL, LANGUAGE, LYRICIST, MEDIA, PERFORMER,
|
||||
RELEASECOUNTRY, REMIXER, TITLE, TRACKNUMBER.
|
||||
* WAV: Fixed crash with files having the "id3 " chunk as the only valid chunk.
|
||||
* Windows: Fixed support for files larger than 2GB.
|
||||
|
||||
TagLib 1.13.1 (Jul 1, 2023)
|
||||
===========================
|
||||
|
||||
* Fixed parsing of TXXX frames without description.
|
||||
* Detect MP4 atoms with invalid length or type.
|
||||
* Do not miss ID3v2 frames when an extended header is present.
|
||||
* Use property "DISCSUBTITLE" for ID3v2 "TSST" frame.
|
||||
* Build system improvements: Use absolute path for macOS dylib install name,
|
||||
support --define-prefix when using pkg-config, fixed minimum required
|
||||
CppUnit version.
|
||||
* Code clean up using clang-tidy.
|
||||
|
||||
TagLib 1.13 (Oct 27, 2022)
|
||||
==========================
|
||||
|
||||
* Added interface StreamTypeResolver to support streams which cannot be
|
||||
fopen()'ed, e.g. network files.
|
||||
* Added MP4::File::strip() to remove meta atom from MP4 file.
|
||||
* Added Map::value() to look up without creating entry.
|
||||
* Use property "WORK" instead of "CONTENTGROUP" for ID3v2 "TIT1" frame,
|
||||
use property "WORK" for ASF "WM/ContentGroupDescription",
|
||||
use property "COMPILATION" for ID3v2 "TCMP" frame.
|
||||
* Build system improvements: option WITH_ZLIB, BUILD_TESTING instead of
|
||||
BUILD_TESTS, GNUInstallDirs, FeatureSummary, tests with BUILD_SHARED_LIBS,
|
||||
cross compilation with Buildroot, systems without HAVE_GCC_ATOMIC, Clang.
|
||||
* Fixed heap-buffer-overflows when handling ASF, APE, FLAC, ID3v2, MP4, MPC
|
||||
tags.
|
||||
* Fixed detection of invalid file by extension when correct type can be
|
||||
detected by contents.
|
||||
* Fixed unnecessary creation of map entries in APE and FLAC tags if looked up
|
||||
tag does not exist.
|
||||
* Fixed parsing of MP4 non-full meta atoms.
|
||||
* Fixed potential ID3v1 false positive in the presence of an APE tag.
|
||||
* Fixed ID3v2 version handling for frames embedded in CHAP or CTOC frames.
|
||||
* Fixed parsing of multiple strings with a single BOM in ID3v2.4.0.
|
||||
* Fixed several smaller issues reported by clang-tidy.
|
||||
|
||||
TagLib 1.12 (Feb 16, 2021)
|
||||
==========================
|
||||
|
||||
* Added support for WinRT.
|
||||
* Added support for Linux on POWER.
|
||||
* Added support for classical music tags of iTunes 12.5.
|
||||
* Added support for file descriptor to FileStream.
|
||||
* Added support for 'cmID', 'purl', 'egid' MP4 atoms.
|
||||
* Added support for 'GRP1' ID3v2 frame.
|
||||
* Added support for extensible WAV subformat.
|
||||
* Enabled FileRef to detect file types based on the stream content.
|
||||
* Dropped support for Windows 9x and NT 4.0 or older.
|
||||
* Check for mandatory header objects in ASF files.
|
||||
* More tolerant handling of RIFF padding, WAV files, broken MPEG streams.
|
||||
* Improved calculation of Ogg, Opus, Speex, WAV, MP4 bitrates.
|
||||
* Improved Windows compatibility by storing FLAC picture after comments.
|
||||
* Fixed numerical genres in ID3v2.3.0 'TCON' frames.
|
||||
* Fixed consistency of API removing MP4 items when empty values are set.
|
||||
* Fixed consistency of API preferring COMM frames with no description.
|
||||
* Fixed OOB read on invalid Ogg FLAC files (CVE-2018-11439).
|
||||
* Fixed handling of empty MPEG files.
|
||||
* Fixed parsing MP4 mdhd timescale.
|
||||
* Fixed reading MP4 atoms with zero length.
|
||||
* Fixed reading FLAC files with zero-sized seektables.
|
||||
* Fixed handling of lowercase field names in Vorbis Comments.
|
||||
* Fixed handling of 'rate' atoms in MP4 files.
|
||||
* Fixed handling of invalid UTF-8 sequences.
|
||||
* Fixed possible file corruptions when saving Ogg files.
|
||||
* Fixed handling of non-audio blocks, sampling rates, DSD audio in WavPack files.
|
||||
* TableOfContentsFrame::toString() improved.
|
||||
* UserTextIdentificationFrame::toString() improved.
|
||||
* Marked FileRef::create() deprecated.
|
||||
* Marked MPEG::File::save() with boolean parameters deprecated,
|
||||
provide overloads with enum parameters.
|
||||
* Several smaller bug fixes and performance improvements.
|
||||
|
||||
TagLib 1.11.1 (Oct 24, 2016)
|
||||
============================
|
||||
|
||||
* Fixed binary incompatible change in TagLib::String.
|
||||
* Fixed reading ID3v2 CTOC frames with a lot of entries.
|
||||
* Fixed seeking ByteVectorStream from the end.
|
||||
|
||||
TagLib 1.11 (Apr 29, 2016)
|
||||
==========================
|
||||
|
||||
1.11:
|
||||
|
||||
* Fixed reading APE items with long keys.
|
||||
* Fixed reading ID3v2 SYLT frames when description is empty.
|
||||
|
||||
1.11 BETA 2:
|
||||
|
||||
* Better handling of PCM WAV files with a 'fact' chunk.
|
||||
* Better handling of corrupted APE tags.
|
||||
* Efficient decoding of unsynchronized ID3v2 frames.
|
||||
* Fixed text encoding when saving certain frames in ID3v2.3 tags.
|
||||
* Fixed updating the size of RIFF files when removing chunks.
|
||||
* Several smaller bug fixes and performance improvements.
|
||||
|
||||
1.11 BETA:
|
||||
|
||||
* New API for creating FileRef from IOStream.
|
||||
* Added support for ID3v2 PCST and WFED frames.
|
||||
* Added support for pictures in XiphComment.
|
||||
* Added String::clear().
|
||||
* Added FLAC::File::strip() for removing non-standard tags.
|
||||
* Added alternative functions to XiphComment::removeField().
|
||||
* Added BUILD_BINDINGS build option.
|
||||
* Added ENABLE_CCACHE build option.
|
||||
* Replaced ENABLE_STATIC build option with BUILD_SHARED_LIBS.
|
||||
* Better handling of duplicate ID3v2 tags in all kinds of files.
|
||||
* Better handling of duplicate tag chunks in WAV files.
|
||||
* Better handling of duplicate tag chunks in AIFF files.
|
||||
* Better handling of duplicate Vorbis comment blocks in FLAC files.
|
||||
* Better handling of broken MPEG audio frames.
|
||||
* Fixed crash when calling File::properties() after strip().
|
||||
* Fixed crash when parsing certain MPEG files.
|
||||
* Fixed crash when saving Ogg files.
|
||||
* Fixed possible file corruptions when saving ASF files.
|
||||
* Fixed possible file corruptions when saving FLAC files.
|
||||
* Fixed possible file corruptions when saving MP4 files.
|
||||
* Fixed possible file corruptions when saving MPEG files.
|
||||
* Fixed possible file corruptions when saving APE files.
|
||||
* Fixed possible file corruptions when saving Musepack files.
|
||||
* Fixed possible file corruptions when saving WavPack files.
|
||||
* Fixed updating the comment field of Vorbis comments.
|
||||
* Fixed reading date and time in ID3v2.3 tags.
|
||||
* Marked ByteVector::null and ByteVector::isNull() deprecated.
|
||||
* Marked String::null and String::isNull() deprecated.
|
||||
* Marked XiphComment::removeField() deprecated.
|
||||
* Marked Ogg::Page::getCopyWithNewPageSequenceNumber() deprecated. It returns null.
|
||||
* Marked custom integer types deprecated.
|
||||
* Many smaller bug fixes and performance improvements.
|
||||
|
||||
TagLib 1.10 (Nov 11, 2015)
|
||||
==========================
|
||||
|
||||
1.10:
|
||||
|
||||
* Added new options to the tagwriter example.
|
||||
* Fixed self-assignment operator in some types.
|
||||
* Fixed extraction of MP4 tag keys with an empty list.
|
||||
|
||||
1.10 BETA:
|
||||
|
||||
* New API for the audio length in milliseconds.
|
||||
* Added support for ID3v2 ETCO and SYLT frames.
|
||||
* Added support for album artist in PropertyMap API of MP4 files.
|
||||
* Added support for embedded frames in ID3v2 CHAP and CTOC frames.
|
||||
* Added support for AIFF-C files.
|
||||
* Better handling of duplicate ID3v2 tags in MPEG files.
|
||||
* Allowed generating taglib.pc on Windows.
|
||||
* Added ZLIB_SOURCE build option.
|
||||
* Fixed backwards-incompatible change in TagLib::String when constructing UTF16 strings.
|
||||
* Fixed crash when parsing certain FLAC files.
|
||||
* Fixed crash when encoding empty strings.
|
||||
* Fixed saving of certain XM files on OS X.
|
||||
* Changed Xiph and APE generic getters to return space-concatenated values.
|
||||
* Fixed possible file corruptions when removing tags from WAV files.
|
||||
* Added support for MP4 files with 64-bit atoms in certain 64-bit environments.
|
||||
* Prevented ID3v2 padding from being too large.
|
||||
* Fixed crash when parsing corrupted APE files.
|
||||
* Fixed crash when parsing corrupted WAV files.
|
||||
* Fixed crash when parsing corrupted Ogg FLAC files.
|
||||
* Fixed crash when parsing corrupted MPEG files.
|
||||
* Fixed saving empty tags in WAV files.
|
||||
* Fixed crash when parsing corrupted Musepack files.
|
||||
* Fixed possible memory leaks when parsing AIFF and WAV files.
|
||||
* Fixed crash when parsing corrupted MP4 files.
|
||||
* Stopped writing empty ID3v2 frames.
|
||||
* Fixed possible file corruptions when saving WMA files.
|
||||
* Added TagLib::MP4::Tag::isEmpty().
|
||||
* Added accessors to manipulate MP4 tags.
|
||||
* Fixed crash when parsing corrupted WavPack files.
|
||||
* Fixed seeking MPEG frames.
|
||||
* Fixed reading FLAC files with zero-sized padding blocks.
|
||||
* Added support for reading the encoder information of WMA files.
|
||||
* Added support for reading the codec of WAV files.
|
||||
* Added support for multi channel WavPack files.
|
||||
* Added support for reading the nominal bitrate of Ogg Speex files.
|
||||
* Added support for VBR headers in MPEG files.
|
||||
* Marked FLAC::File::streamInfoData() deprecated. It returns an empty ByteVector.
|
||||
* Marked FLAC::File::streamLength() deprecated. It returns zero.
|
||||
* Fixed possible file corruptions when adding an ID3v1 tag to FLAC files.
|
||||
* Many smaller bug fixes and performance improvements.
|
||||
|
||||
TagLib 1.9.1 (Oct 8, 2013)
|
||||
==========================
|
||||
|
||||
* Fixed binary incompatible change in TagLib::Map and TagLib::List.
|
||||
* Fixed constructing String from ByteVector.
|
||||
* Fixed compilation on MSVC with the /Zc:wchar_t- option.
|
||||
* Fixed detecting of RIFF files with invalid chunk sizes.
|
||||
* Added TagLib::MP4::Properties::codec().
|
||||
|
||||
TagLib 1.9 (Oct 6, 2013)
|
||||
========================
|
||||
|
||||
* Added support for the Ogg Opus file format.
|
||||
* Added support for INFO tags in WAV files.
|
||||
* Changed FileStream to use Windows file API.
|
||||
* Included taglib-config.cmd script for Windows.
|
||||
* New ID3v1::Tag methods for working directly with genre numbers.
|
||||
* New MPEG::File methods for checking which tags are saved in the file.
|
||||
* Added support for the PropertyMap API to ASF and MP4 files.
|
||||
* Added MusicBrainz identifiers to the PropertyMap API.
|
||||
* Allowed reading of MP4 cover art without an explicitly specified format.
|
||||
* Better parsing of corrupted FLAC files.
|
||||
* Fixed saving of PropertyMap comments without description into ID3v2 tags.
|
||||
* Fixed crash when parsing certain XM files.
|
||||
* Fixed compilation of unit test with clang.
|
||||
* Better handling of files that can't be open or have read-only permissions.
|
||||
* Improved atomic reference counting.
|
||||
* New hookable API for debug messages.
|
||||
* More complete Windows install instructions.
|
||||
* Many smaller bug fixes and performance improvements.
|
||||
|
||||
TagLib 1.8 (Sep 6, 2012)
|
||||
========================
|
||||
|
||||
1.8:
|
||||
|
||||
* Added support for OWNE ID3 frames.
|
||||
* Changed key validation in the new PropertyMap API.
|
||||
* ID3v1::Tag::setStringHandler will no longer delete the previous handler,
|
||||
the caller is responsible for this.
|
||||
* File objects will also no longer delete the passed IOStream objects. It
|
||||
should be done in the caller code after the File object is no longer
|
||||
used.
|
||||
* Added ID3v2::Tag::setLatin1StringHandler for custom handling of
|
||||
latin1-encoded text in ID3v2 frames.
|
||||
* Fixed validation of ID3v2 frame IDs (IDs with '0' were ignored).
|
||||
|
||||
1.8 BETA:
|
||||
|
||||
* New API for accessing tags by name.
|
||||
* New abstract I/O stream layer to allow custom I/O handlers.
|
||||
* Support for writing ID3v2.3 tags.
|
||||
* Support for various module file formats (MOD, S3M, IT, XM).
|
||||
* Support for MP4 and ASF is now enabled by default.
|
||||
* Started using atomic int operations for reference counting.
|
||||
* Added methods for checking if WMA and MP4 files are DRM-protected.
|
||||
* Added taglib_free to the C bindings.
|
||||
* New method to allow removing pictures from FLAC files.
|
||||
* Support for reading audio properties from ALAC and Musepack SV8 files.
|
||||
* Added replay-gain information to Musepack audio properties.
|
||||
* Support for APEv2 binary tags.
|
||||
* Many AudioProperties subclasses now provide information about the total number of samples.
|
||||
* Various small bug fixes.
|
||||
|
||||
TagLib 1.7.2 (Apr 20, 2012)
|
||||
===========================
|
||||
|
||||
* Fixed division by zero while parsing corrupted MP4 files (CVE-2012-2396).
|
||||
* Fixed compilation on Haiku.
|
||||
|
||||
TagLib 1.7.1 (Mar 17, 2012)
|
||||
===========================
|
||||
|
||||
* Improved parsing of corrupted WMA, RIFF and OGG files.
|
||||
* Fixed a memory leak in the WMA parser.
|
||||
* Fixed a memory leak in the FLAC parser.
|
||||
* Fixed a possible division by zero in the APE parser.
|
||||
* Added detection of TTA2 files.
|
||||
* Fixed saving of multiple identically named tags to Vorbis Comments.
|
||||
|
||||
TagLib 1.7 (Mar 11, 2011)
|
||||
=========================
|
||||
|
||||
1.7:
|
||||
|
||||
* Fixed memory leaks in the FLAC file format parser.
|
||||
* Fixed bitrate calculation for WAV files.
|
||||
|
||||
1.7 RC1:
|
||||
|
||||
* Support for reading/writing tags from Monkey's Audio files. (BUG:210404)
|
||||
* Support for reading/writing embedded pictures from WMA files.
|
||||
* Support for reading/writing embedded pictures from FLAC files (BUG:218696).
|
||||
* Implemented APE::Tag::isEmpty() to check for all APE tags, not just the
|
||||
basic ones.
|
||||
* Added reading of WAV audio length. (BUG:116033)
|
||||
* Exposed FLAC MD5 signature of the uncompressed audio stream via
|
||||
FLAC::Properties::signature(). (BUG:160172)
|
||||
* Added function ByteVector::toHex() for hex-encoding of byte vectors.
|
||||
* WavPack reader now tries to get the audio length by finding the final
|
||||
block, if the header doesn't have the information. (BUG:258016)
|
||||
* Fixed a memory leak in the ID3v2.2 PIC frame parser. (BUG:257007)
|
||||
* Fixed writing of RIFF files with even chunk sizes. (BUG:243954)
|
||||
* Fixed compilation on MSVC 2010.
|
||||
* Removed support for building using autoconf/automake.
|
||||
* API docs can be now built using "make docs".
|
||||
|
||||
TagLib 1.6.3 (Apr 17, 2010)
|
||||
===========================
|
||||
|
||||
* Fixed definitions of the TAGLIB_WITH_MP4 and TAGLIB_WITH_ASF macros.
|
||||
* Fixed upgrading of ID3v2.3 genre frame with ID3v1 code 0 (Blues).
|
||||
* New method `int String::toInt(bool *ok)` which can return whether the
|
||||
conversion to a number was successful.
|
||||
* Fixed parsing of incorrectly written lengths in ID3v2 (affects mainly
|
||||
compressed frames). (BUG:231075)
|
||||
|
||||
TagLib 1.6.2 (Apr 9, 2010)
|
||||
==========================
|
||||
|
||||
* Read Vorbis Comments from the first FLAC metadata block, if there are
|
||||
multiple ones. (BUG:211089)
|
||||
* Fixed a memory leak in FileRef's OGA format detection.
|
||||
* Fixed compilation with the Sun Studio compiler. (BUG:215225)
|
||||
* Handle WM/TrackNumber attributes with DWORD content in WMA files.
|
||||
(BUG:218526)
|
||||
* More strict check if something is a valid MP4 file. (BUG:216819)
|
||||
* Correctly save MP4 int-pair atoms with flags set to 0.
|
||||
* Fixed compilation of the test runner on Windows.
|
||||
* Store ASF attributes larger than 64k in the metadata library object.
|
||||
* Ignore trailing non-data atoms when parsing MP4 covr atoms.
|
||||
* Don't upgrade ID3v2.2 frame TDA to TDRC. (BUG:228968)
|
||||
|
||||
TagLib 1.6.1 (Oct 31, 2009)
|
||||
===========================
|
||||
|
||||
* Better detection of the audio codec of .oga files in FileRef.
|
||||
* Fixed saving of Vorbis comments to Ogg FLAC files. TagLib tried to
|
||||
include the Vorbis framing bit, which is only correct for Ogg Vorbis.
|
||||
* Public symbols now have explicitly set visibility to "default" on GCC.
|
||||
* Added missing exports for static ID3v1 functions.
|
||||
* Fixed a typo in taglib_c.pc
|
||||
* Fixed a failing test on ppc64.
|
||||
* Support for binary 'covr' atom in MP4 files. TagLib 1.6 treated them
|
||||
as text atoms, which corrupted them in some cases.
|
||||
* Fixed ID3v1-style genre to string conversion in MP4 files.
|
||||
|
||||
TagLib 1.6 (Sep 13, 2009)
|
||||
=========================
|
||||
|
||||
1.6:
|
||||
|
||||
* New CMake option to build a static version - ENABLE_STATIC.
|
||||
* Added support for disabling dllimport/dllexport on Windows using the
|
||||
TAGLIB_STATIC macro.
|
||||
* Support for parsing the obsolete 'gnre' MP4 atom.
|
||||
* New cpp macros TAGLIB_WITH_MP4 and TAGLIB_WITH_ASF to determine if
|
||||
TagLib was built with MP4/ASF support.
|
||||
|
||||
1.6 RC1:
|
||||
|
||||
* Split Ogg packets larger than 64k into multiple pages. (BUG:171957)
|
||||
* TagLib can now use FLAC padding block. (BUG:107659)
|
||||
* ID3v2.2 frames are now not incorrectly saved. (BUG:176373)
|
||||
* Support for ID3v2.2 PIC frames. (BUG:167786)
|
||||
* Fixed a bug in ByteVectorList::split().
|
||||
* XiphComment::year() now falls back to YEAR if DATE doesn't exist
|
||||
and XiphComment::year() falls back to TRACKNUM if TRACKNUMBER doesn't
|
||||
exist. (BUG:144396)
|
||||
* Improved ID3v2.3 genre parsing. (BUG:188578)
|
||||
* Better checking of corrupted ID3v2 APIC data. (BUG:168382)
|
||||
* Bitrate calculating using the Xing header now uses floating point
|
||||
numbers. (BUG:172556)
|
||||
* New TagLib::String method rfind().
|
||||
* Added support for MP4 file format with iTunes-style metadata [optional].
|
||||
* Added support for ASF (WMA) file format [optional].
|
||||
* Fixed crash when saving a Locator APEv2 tag. (BUG:169810)
|
||||
* Fixed a possible crash in the non-const version of String::operator[]
|
||||
and in String::operator+=. (BUG:169389)
|
||||
* Added support for PRIV ID3v2 frames.
|
||||
* Empty ID3v2 genres are no longer treated as numeric ID3v1 genres.
|
||||
* Added support for the POPM (rating/play count) ID3v2 frame.
|
||||
* Generic RIFF file format support:
|
||||
* Support for AIFF files with ID3v2 tags.
|
||||
* Support for WAV files with ID3v2 tags.
|
||||
* Fixed crash on handling unsupported ID3v2 frames, e.g. on encrypted
|
||||
frames. (BUG:161721)
|
||||
* Fixed overflow while calculating bitrate of FLAC files with a very
|
||||
high bitrate.
|
||||
220
CMakeLists.txt
220
CMakeLists.txt
@@ -1,60 +1,86 @@
|
||||
cmake_minimum_required(VERSION 3.5.0 FATAL_ERROR)
|
||||
|
||||
project(taglib)
|
||||
|
||||
cmake_minimum_required(VERSION 2.8.0 FATAL_ERROR)
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules")
|
||||
|
||||
if(NOT ${CMAKE_VERSION} VERSION_LESS 2.8.12)
|
||||
cmake_policy(SET CMP0022 OLD)
|
||||
include(CTest)
|
||||
include(FeatureSummary)
|
||||
include(GNUInstallDirs)
|
||||
include(CMakePackageConfigHelpers)
|
||||
|
||||
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()
|
||||
|
||||
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules")
|
||||
|
||||
option(ENABLE_STATIC "Make static version of libtag" OFF)
|
||||
if(ENABLE_STATIC)
|
||||
if(NOT BUILD_SHARED_LIBS)
|
||||
add_definitions(-DTAGLIB_STATIC)
|
||||
set(BUILD_SHARED_LIBS OFF)
|
||||
else()
|
||||
set(BUILD_SHARED_LIBS ON)
|
||||
endif()
|
||||
OPTION(ENABLE_STATIC_RUNTIME "Visual Studio, link with runtime statically" OFF)
|
||||
option(ENABLE_STATIC_RUNTIME "Visual Studio, link with runtime statically" OFF)
|
||||
|
||||
option(VISIBILITY_HIDDEN "Build with -fvisibility=hidden" OFF)
|
||||
if(VISIBILITY_HIDDEN)
|
||||
add_definitions (-fvisibility=hidden)
|
||||
option(ENABLE_CCACHE "Use ccache when building libtag" OFF)
|
||||
if(ENABLE_CCACHE)
|
||||
find_program(CCACHE_FOUND ccache)
|
||||
if(CCACHE_FOUND)
|
||||
set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE ccache)
|
||||
set_property(GLOBAL PROPERTY RULE_LAUNCH_LINK ccache)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
option(BUILD_TESTS "Build the test suite" OFF)
|
||||
option(BUILD_EXAMPLES "Build the examples" OFF)
|
||||
option(VISIBILITY_HIDDEN "Build with -fvisibility=hidden" OFF)
|
||||
option(BUILD_EXAMPLES "Build the examples" OFF)
|
||||
option(BUILD_BINDINGS "Build the bindings" ON)
|
||||
|
||||
option(NO_ITUNES_HACKS "Disable workarounds for iTunes bugs" OFF)
|
||||
option(NO_ITUNES_HACKS "Disable workarounds for iTunes bugs" OFF)
|
||||
|
||||
option(PLATFORM_WINRT "Enable WinRT support" OFF)
|
||||
if(PLATFORM_WINRT)
|
||||
add_definitions(-DPLATFORM_WINRT)
|
||||
endif()
|
||||
|
||||
set(TAGLIB_INSTALL_SUFFIX "" CACHE STRING
|
||||
"Suffix added to installed files (include directory, libraries, .pc)")
|
||||
|
||||
add_definitions(-DHAVE_CONFIG_H)
|
||||
set(TESTS_DIR "${CMAKE_CURRENT_SOURCE_DIR}/tests/")
|
||||
|
||||
## the following are directories where stuff will be installed to
|
||||
set(LIB_SUFFIX "" CACHE STRING "Define suffix of directory name (32/64)")
|
||||
set(EXEC_INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}" CACHE PATH "Base directory for executables and libraries" FORCE)
|
||||
set(BIN_INSTALL_DIR "${EXEC_INSTALL_PREFIX}/bin" CACHE PATH "The subdirectory to the binaries prefix (default prefix/bin)" FORCE)
|
||||
set(LIB_INSTALL_DIR "${EXEC_INSTALL_PREFIX}/lib${LIB_SUFFIX}" CACHE PATH "The subdirectory relative to the install prefix where libraries will be installed (default is /lib${LIB_SUFFIX})" FORCE)
|
||||
set(INCLUDE_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/include" CACHE PATH "The subdirectory to the header prefix" FORCE)
|
||||
|
||||
if(APPLE)
|
||||
option(BUILD_FRAMEWORK "Build an OS X framework" OFF)
|
||||
set(FRAMEWORK_INSTALL_DIR "/Library/Frameworks" CACHE STRING "Directory to install frameworks to.")
|
||||
if(CMAKE_C_COMPILER_ID MATCHES "^(GNU|Clang|AppleClang)$")
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall")
|
||||
endif()
|
||||
|
||||
if(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX)
|
||||
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall")
|
||||
if(CMAKE_CXX_COMPILER_ID MATCHES "^(GNU|Clang|AppleClang)$")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall")
|
||||
endif()
|
||||
if (MSVC AND ENABLE_STATIC_RUNTIME)
|
||||
foreach(flag_var CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE CMAKE_CXX_FLAGS_MINSIZEREL CMAKE_CXX_FLAGS_RELWITHDEBINFO)
|
||||
string(REGEX REPLACE "/MD" "/MT" ${flag_var} "${${flag_var}}")
|
||||
endforeach(flag_var)
|
||||
|
||||
if(MSVC)
|
||||
if(ENABLE_STATIC_RUNTIME)
|
||||
foreach(flag_var CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE CMAKE_CXX_FLAGS_MINSIZEREL CMAKE_CXX_FLAGS_RELWITHDEBINFO)
|
||||
string(REGEX REPLACE "/MD" "/MT" ${flag_var} "${${flag_var}}")
|
||||
endforeach(flag_var)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
set(TAGLIB_LIB_MAJOR_VERSION "1")
|
||||
set(TAGLIB_LIB_MINOR_VERSION "10")
|
||||
set(TAGLIB_LIB_PATCH_VERSION "0")
|
||||
# Read version information from file taglib/toolkit/taglib.h into variables
|
||||
# TAGLIB_LIB_MAJOR_VERSION, TAGLIB_LIB_MINOR_VERSION, TAGLIB_LIB_PATCH_VERSION.
|
||||
foreach(version_part MAJOR MINOR PATCH)
|
||||
set(version_var_name "TAGLIB_${version_part}_VERSION")
|
||||
file(STRINGS taglib/toolkit/taglib.h version_line
|
||||
REGEX "^#define +${version_var_name}")
|
||||
if(NOT version_line)
|
||||
message(FATAL_ERROR "${version_var_name} not found in taglib.h")
|
||||
endif()
|
||||
string(REGEX MATCH "${version_var_name} +([^ ]+)" result ${version_line})
|
||||
set(TAGLIB_LIB_${version_part}_VERSION ${CMAKE_MATCH_1})
|
||||
endforeach(version_part)
|
||||
|
||||
# Only used to force cmake rerun when taglib.h changes.
|
||||
configure_file(taglib/toolkit/taglib.h ${CMAKE_CURRENT_BINARY_DIR}/taglib.h.stamp)
|
||||
|
||||
if("${TAGLIB_LIB_PATCH_VERSION}" EQUAL "0")
|
||||
set(TAGLIB_LIB_VERSION_STRING "${TAGLIB_LIB_MAJOR_VERSION}.${TAGLIB_LIB_MINOR_VERSION}")
|
||||
@@ -62,76 +88,110 @@ else()
|
||||
set(TAGLIB_LIB_VERSION_STRING "${TAGLIB_LIB_MAJOR_VERSION}.${TAGLIB_LIB_MINOR_VERSION}.${TAGLIB_LIB_PATCH_VERSION}")
|
||||
endif()
|
||||
|
||||
# 1. If the library source code has changed at all since the last update, then increment revision.
|
||||
# 2. If any interfaces have been added, removed, or changed since the last update, increment current, and set revision to 0.
|
||||
# 3. If any interfaces have been added since the last public release, then increment age.
|
||||
# 4. If any interfaces have been removed since the last public release, then set age to 0.
|
||||
set(TAGLIB_SOVERSION_CURRENT 16)
|
||||
set(TAGLIB_SOVERSION_REVISION 1)
|
||||
set(TAGLIB_SOVERSION_AGE 15)
|
||||
|
||||
math(EXPR TAGLIB_SOVERSION_MAJOR "${TAGLIB_SOVERSION_CURRENT} - ${TAGLIB_SOVERSION_AGE}")
|
||||
math(EXPR TAGLIB_SOVERSION_MINOR "${TAGLIB_SOVERSION_AGE}")
|
||||
math(EXPR TAGLIB_SOVERSION_PATCH "${TAGLIB_SOVERSION_REVISION}")
|
||||
# Major version: increase it if you break ABI compatibility.
|
||||
# Minor version: increase it if you add ABI compatible features.
|
||||
# Patch version: increase it for bug fix releases.
|
||||
set(TAGLIB_SOVERSION_MAJOR 2)
|
||||
set(TAGLIB_SOVERSION_MINOR 0)
|
||||
set(TAGLIB_SOVERSION_PATCH 0)
|
||||
|
||||
include(ConfigureChecks.cmake)
|
||||
|
||||
# Determine whether zlib is installed.
|
||||
option(WITH_ZLIB "Build with ZLIB" ON)
|
||||
|
||||
if(WITH_ZLIB)
|
||||
find_package("ZLIB")
|
||||
set(HAVE_ZLIB ${ZLIB_FOUND})
|
||||
if(ZLIB_FOUND)
|
||||
set(ZLIB_LIBRARIES_FLAGS -lz)
|
||||
if(NOT BUILD_SHARED_LIBS)
|
||||
# When linking TagLib statically, zlib has to be linked explicitly.
|
||||
set(ZLIB_INTERFACE_LINK_LIBRARIES ZLIB::ZLIB)
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(NOT WIN32)
|
||||
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/taglib-config.cmake" "${CMAKE_CURRENT_BINARY_DIR}/taglib-config")
|
||||
install(PROGRAMS "${CMAKE_CURRENT_BINARY_DIR}/taglib-config" DESTINATION "${BIN_INSTALL_DIR}")
|
||||
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/taglib-config.cmake" "${CMAKE_CURRENT_BINARY_DIR}/taglib-config" @ONLY)
|
||||
install(PROGRAMS "${CMAKE_CURRENT_BINARY_DIR}/taglib-config" DESTINATION "${CMAKE_INSTALL_BINDIR}"
|
||||
RENAME "taglib${TAGLIB_INSTALL_SUFFIX}-config")
|
||||
endif()
|
||||
|
||||
if(WIN32)
|
||||
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/taglib-config.cmd.cmake" "${CMAKE_CURRENT_BINARY_DIR}/taglib-config.cmd")
|
||||
install(PROGRAMS "${CMAKE_CURRENT_BINARY_DIR}/taglib-config.cmd" DESTINATION "${BIN_INSTALL_DIR}")
|
||||
install(PROGRAMS "${CMAKE_CURRENT_BINARY_DIR}/taglib-config.cmd" DESTINATION "${CMAKE_INSTALL_BINDIR}"
|
||||
RENAME "taglib${TAGLIB_INSTALL_SUFFIX}-config.cmd")
|
||||
endif()
|
||||
|
||||
if(NOT BUILD_FRAMEWORK)
|
||||
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/taglib.pc.cmake" "${CMAKE_CURRENT_BINARY_DIR}/taglib.pc")
|
||||
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/taglib.pc" DESTINATION "${LIB_INSTALL_DIR}/pkgconfig")
|
||||
endif()
|
||||
|
||||
if(NOT HAVE_ZLIB AND ZLIB_SOURCE)
|
||||
set(HAVE_ZLIB 1)
|
||||
set(HAVE_ZLIB_SOURCE 1)
|
||||
if(IS_ABSOLUTE ${CMAKE_INSTALL_INCLUDEDIR})
|
||||
set(CMAKE_PC_INCLUDEDIR ${CMAKE_INSTALL_INCLUDEDIR})
|
||||
else()
|
||||
set(CMAKE_PC_INCLUDEDIR "\${prefix}/${CMAKE_INSTALL_INCLUDEDIR}")
|
||||
endif()
|
||||
if(IS_ABSOLUTE ${CMAKE_INSTALL_LIBDIR})
|
||||
set(CMAKE_PC_LIBDIR ${CMAKE_INSTALL_LIBDIR})
|
||||
else()
|
||||
set(CMAKE_PC_LIBDIR "\${prefix}/${CMAKE_INSTALL_LIBDIR}")
|
||||
endif()
|
||||
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/taglib.pc.cmake" "${CMAKE_CURRENT_BINARY_DIR}/taglib.pc" @ONLY)
|
||||
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/taglib.pc" DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig"
|
||||
RENAME "taglib${TAGLIB_INSTALL_SUFFIX}.pc")
|
||||
endif()
|
||||
|
||||
include_directories(${CMAKE_CURRENT_BINARY_DIR})
|
||||
configure_file(config.h.cmake "${CMAKE_CURRENT_BINARY_DIR}/config.h")
|
||||
|
||||
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)
|
||||
add_subdirectory(bindings)
|
||||
if(BUILD_TESTS)
|
||||
enable_testing()
|
||||
find_package(utf8cpp QUIET)
|
||||
if(NOT utf8cpp_FOUND)
|
||||
if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/utfcpp/CMakeLists.txt)
|
||||
add_subdirectory("3rdparty/utfcpp")
|
||||
message(STATUS "Using utfcpp from ${utf8cpp_SOURCE_DIR}")
|
||||
else()
|
||||
message(FATAL_ERROR
|
||||
"utfcpp not found. Either install package (probably utfcpp, utf8cpp, or libutfcpp-dev) "
|
||||
"or fetch the git submodule using\n"
|
||||
"git submodule update --init")
|
||||
endif()
|
||||
else()
|
||||
message(STATUS "Using utfcpp ${utf8cpp_VERSION} from ${utf8cpp_CONFIG}")
|
||||
endif()
|
||||
|
||||
add_subdirectory(taglib)
|
||||
|
||||
if(BUILD_BINDINGS)
|
||||
add_subdirectory(bindings)
|
||||
endif()
|
||||
|
||||
if(BUILD_TESTING)
|
||||
find_package(CppUnit)
|
||||
if(CppUnit_FOUND)
|
||||
add_subdirectory(tests)
|
||||
endif(BUILD_TESTS)
|
||||
add_subdirectory(examples)
|
||||
else()
|
||||
message(WARNING "BUILD_TESTING requested, but CppUnit not found, skipping tests.")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(BUILD_EXAMPLES)
|
||||
add_subdirectory(examples)
|
||||
endif()
|
||||
|
||||
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/Doxyfile.cmake" "${CMAKE_CURRENT_BINARY_DIR}/Doxyfile")
|
||||
file(COPY doc/taglib.png DESTINATION doc)
|
||||
add_custom_target(docs doxygen)
|
||||
|
||||
# uninstall target
|
||||
configure_file(
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/cmake_uninstall.cmake.in"
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake"
|
||||
IMMEDIATE @ONLY)
|
||||
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/cmake_uninstall.cmake.in" "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake" IMMEDIATE @ONLY)
|
||||
|
||||
if (NOT TARGET uninstall)
|
||||
add_custom_target(uninstall
|
||||
COMMAND "${CMAKE_COMMAND}" -P "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake")
|
||||
if(NOT TARGET uninstall)
|
||||
add_custom_target(uninstall COMMAND "${CMAKE_COMMAND}" -P "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake")
|
||||
endif()
|
||||
|
||||
feature_summary(WHAT ALL FATAL_ON_MISSING_REQUIRED_PACKAGES)
|
||||
|
||||
@@ -34,77 +34,6 @@ if(NOT ${SIZEOF_DOUBLE} EQUAL 8)
|
||||
message(FATAL_ERROR "TagLib requires that double is 64-bit wide.")
|
||||
endif()
|
||||
|
||||
# Determine which kind of atomic operations your compiler supports.
|
||||
|
||||
check_cxx_source_compiles("
|
||||
#include <atomic>
|
||||
int main() {
|
||||
std::atomic<unsigned int> x;
|
||||
x.fetch_add(1);
|
||||
x.fetch_sub(1);
|
||||
return 0;
|
||||
}
|
||||
" HAVE_STD_ATOMIC)
|
||||
|
||||
if(NOT HAVE_STD_ATOMIC)
|
||||
check_cxx_source_compiles("
|
||||
#include <boost/atomic.hpp>
|
||||
int main() {
|
||||
boost::atomic<unsigned int> x(1);
|
||||
x.fetch_add(1);
|
||||
x.fetch_sub(1);
|
||||
return 0;
|
||||
}
|
||||
" HAVE_BOOST_ATOMIC)
|
||||
|
||||
if(NOT HAVE_BOOST_ATOMIC)
|
||||
check_cxx_source_compiles("
|
||||
int main() {
|
||||
volatile int x;
|
||||
__sync_add_and_fetch(&x, 1);
|
||||
int y = __sync_sub_and_fetch(&x, 1);
|
||||
return 0;
|
||||
}
|
||||
" HAVE_GCC_ATOMIC)
|
||||
|
||||
if(NOT HAVE_GCC_ATOMIC)
|
||||
check_cxx_source_compiles("
|
||||
#include <libkern/OSAtomic.h>
|
||||
int main() {
|
||||
volatile int32_t x;
|
||||
OSAtomicIncrement32Barrier(&x);
|
||||
int32_t y = OSAtomicDecrement32Barrier(&x);
|
||||
return 0;
|
||||
}
|
||||
" HAVE_MAC_ATOMIC)
|
||||
|
||||
if(NOT HAVE_MAC_ATOMIC)
|
||||
check_cxx_source_compiles("
|
||||
#include <windows.h>
|
||||
int main() {
|
||||
volatile LONG x;
|
||||
InterlockedIncrement(&x);
|
||||
LONG y = InterlockedDecrement(&x);
|
||||
return 0;
|
||||
}
|
||||
" HAVE_WIN_ATOMIC)
|
||||
|
||||
if(NOT HAVE_WIN_ATOMIC)
|
||||
check_cxx_source_compiles("
|
||||
#include <ia64intrin.h>
|
||||
int main() {
|
||||
volatile int x;
|
||||
__sync_add_and_fetch(&x, 1);
|
||||
int y = __sync_sub_and_fetch(&x, 1);
|
||||
return 0;
|
||||
}
|
||||
" HAVE_IA64_ATOMIC)
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Determine which kind of byte swap functions your compiler supports.
|
||||
|
||||
check_cxx_source_compiles("
|
||||
@@ -129,7 +58,7 @@ if(NOT HAVE_GCC_BYTESWAP)
|
||||
|
||||
if(NOT HAVE_GLIBC_BYTESWAP)
|
||||
check_cxx_source_compiles("
|
||||
#include <stdlib.h>
|
||||
#include <cstdlib>
|
||||
int main() {
|
||||
_byteswap_ushort(0);
|
||||
_byteswap_ulong(0);
|
||||
@@ -164,48 +93,17 @@ if(NOT HAVE_GCC_BYTESWAP)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Determine whether your compiler supports some safer version of vsprintf.
|
||||
# Determine whether your compiler supports ISO _strdup.
|
||||
|
||||
check_cxx_source_compiles("
|
||||
#include <cstdio>
|
||||
#include <cstdarg>
|
||||
#include <cstring>
|
||||
int main() {
|
||||
char buf[20];
|
||||
va_list args;
|
||||
vsnprintf(buf, 20, \"%d\", args);
|
||||
_strdup(0);
|
||||
return 0;
|
||||
}
|
||||
" HAVE_VSNPRINTF)
|
||||
" HAVE_ISO_STRDUP)
|
||||
|
||||
if(NOT HAVE_VSNPRINTF)
|
||||
check_cxx_source_compiles("
|
||||
#include <cstdio>
|
||||
#include <cstdarg>
|
||||
int main() {
|
||||
char buf[20];
|
||||
va_list args;
|
||||
vsprintf_s(buf, \"%d\", args);
|
||||
return 0;
|
||||
}
|
||||
" HAVE_VSPRINTF_S)
|
||||
# Detect WinRT mode
|
||||
if(CMAKE_SYSTEM_NAME STREQUAL "WindowsStore")
|
||||
set(PLATFORM_WINRT 1)
|
||||
endif()
|
||||
|
||||
# Check for libz using the cmake supplied FindZLIB.cmake
|
||||
|
||||
if(NOT ZLIB_SOURCE)
|
||||
find_package(ZLIB)
|
||||
if(ZLIB_FOUND)
|
||||
set(HAVE_ZLIB 1)
|
||||
else()
|
||||
set(HAVE_ZLIB 0)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(BUILD_TESTS)
|
||||
find_package(CppUnit)
|
||||
if(NOT CppUnit_FOUND)
|
||||
message(STATUS "CppUnit not found, disabling tests.")
|
||||
set(BUILD_TESTS OFF)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
|
||||
264
Doxyfile.cmake
264
Doxyfile.cmake
@@ -1,37 +1,79 @@
|
||||
# Doxyfile 1.3.4
|
||||
# Doxyfile 1.9.1
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# Project related configuration options
|
||||
#---------------------------------------------------------------------------
|
||||
DOXYFILE_ENCODING = UTF-8
|
||||
PROJECT_NAME = TagLib
|
||||
PROJECT_NUMBER = ${TAGLIB_LIB_VERSION_STRING}
|
||||
PROJECT_BRIEF =
|
||||
PROJECT_LOGO = @CMAKE_SOURCE_DIR@/doc/taglib.svg
|
||||
OUTPUT_DIRECTORY = doc
|
||||
CREATE_SUBDIRS = NO
|
||||
ALLOW_UNICODE_NAMES = NO
|
||||
OUTPUT_LANGUAGE = English
|
||||
USE_WINDOWS_ENCODING = NO
|
||||
OUTPUT_TEXT_DIRECTION = None
|
||||
BRIEF_MEMBER_DESC = YES
|
||||
REPEAT_BRIEF = YES
|
||||
ABBREVIATE_BRIEF = "The $name class" \
|
||||
"The $name widget" \
|
||||
"The $name file" \
|
||||
is \
|
||||
provides \
|
||||
specifies \
|
||||
contains \
|
||||
represents \
|
||||
a \
|
||||
an \
|
||||
the
|
||||
ALWAYS_DETAILED_SEC = NO
|
||||
INLINE_INHERITED_MEMB = NO
|
||||
FULL_PATH_NAMES = NO
|
||||
STRIP_FROM_PATH =
|
||||
STRIP_FROM_PATH =
|
||||
STRIP_FROM_INC_PATH =
|
||||
SHORT_NAMES = NO
|
||||
JAVADOC_AUTOBRIEF = NO
|
||||
JAVADOC_BANNER = NO
|
||||
QT_AUTOBRIEF = NO
|
||||
MULTILINE_CPP_IS_BRIEF = NO
|
||||
DETAILS_AT_TOP = NO
|
||||
PYTHON_DOCSTRING = YES
|
||||
INHERIT_DOCS = YES
|
||||
DISTRIBUTE_GROUP_DOC = NO
|
||||
SEPARATE_MEMBER_PAGES = NO
|
||||
TAB_SIZE = 4
|
||||
ALIASES =
|
||||
ALIASES =
|
||||
OPTIMIZE_OUTPUT_FOR_C = NO
|
||||
OPTIMIZE_OUTPUT_JAVA = NO
|
||||
OPTIMIZE_FOR_FORTRAN = NO
|
||||
OPTIMIZE_OUTPUT_VHDL = NO
|
||||
OPTIMIZE_OUTPUT_SLICE = NO
|
||||
EXTENSION_MAPPING =
|
||||
MARKDOWN_SUPPORT = YES
|
||||
TOC_INCLUDE_HEADINGS = 5
|
||||
AUTOLINK_SUPPORT = YES
|
||||
BUILTIN_STL_SUPPORT = NO
|
||||
CPP_CLI_SUPPORT = NO
|
||||
SIP_SUPPORT = NO
|
||||
IDL_PROPERTY_SUPPORT = YES
|
||||
DISTRIBUTE_GROUP_DOC = NO
|
||||
GROUP_NESTED_COMPOUNDS = NO
|
||||
SUBGROUPING = YES
|
||||
INLINE_GROUPED_CLASSES = NO
|
||||
INLINE_SIMPLE_STRUCTS = NO
|
||||
TYPEDEF_HIDES_STRUCT = NO
|
||||
LOOKUP_CACHE_SIZE = 0
|
||||
NUM_PROC_THREADS = 1
|
||||
#---------------------------------------------------------------------------
|
||||
# Build related configuration options
|
||||
#---------------------------------------------------------------------------
|
||||
EXTRACT_ALL = YES
|
||||
EXTRACT_PRIVATE = NO
|
||||
EXTRACT_PRIV_VIRTUAL = NO
|
||||
EXTRACT_PACKAGE = NO
|
||||
EXTRACT_STATIC = NO
|
||||
EXTRACT_LOCAL_CLASSES = NO
|
||||
EXTRACT_LOCAL_METHODS = NO
|
||||
EXTRACT_ANON_NSPACES = NO
|
||||
RESOLVE_UNNAMED_PARAMS = YES
|
||||
HIDE_UNDOC_MEMBERS = NO
|
||||
HIDE_UNDOC_CLASSES = NO
|
||||
HIDE_FRIEND_COMPOUNDS = NO
|
||||
@@ -39,172 +81,268 @@ HIDE_IN_BODY_DOCS = NO
|
||||
INTERNAL_DOCS = YES
|
||||
CASE_SENSE_NAMES = YES
|
||||
HIDE_SCOPE_NAMES = NO
|
||||
HIDE_COMPOUND_REFERENCE= NO
|
||||
SHOW_INCLUDE_FILES = YES
|
||||
SHOW_GROUPED_MEMB_INC = NO
|
||||
FORCE_LOCAL_INCLUDES = NO
|
||||
INLINE_INFO = YES
|
||||
SORT_MEMBER_DOCS = YES
|
||||
SORT_BRIEF_DOCS = NO
|
||||
SORT_MEMBERS_CTORS_1ST = NO
|
||||
SORT_GROUP_NAMES = NO
|
||||
SORT_BY_SCOPE_NAME = NO
|
||||
STRICT_PROTO_MATCHING = NO
|
||||
GENERATE_TODOLIST = NO
|
||||
GENERATE_TESTLIST = NO
|
||||
GENERATE_BUGLIST = NO
|
||||
GENERATE_DEPRECATEDLIST= NO
|
||||
ENABLED_SECTIONS =
|
||||
GENERATE_DEPRECATEDLIST= YES
|
||||
ENABLED_SECTIONS =
|
||||
MAX_INITIALIZER_LINES = 30
|
||||
SHOW_USED_FILES = YES
|
||||
SHOW_FILES = YES
|
||||
SHOW_NAMESPACES = YES
|
||||
FILE_VERSION_FILTER =
|
||||
LAYOUT_FILE =
|
||||
CITE_BIB_FILES =
|
||||
#---------------------------------------------------------------------------
|
||||
# configuration options related to warning and progress messages
|
||||
# Configuration options related to warning and progress messages
|
||||
#---------------------------------------------------------------------------
|
||||
QUIET = NO
|
||||
WARNINGS = YES
|
||||
WARN_IF_UNDOCUMENTED = NO
|
||||
WARN_IF_DOC_ERROR = YES
|
||||
WARN_NO_PARAMDOC = NO
|
||||
WARN_AS_ERROR = NO
|
||||
WARN_FORMAT = "$file:$line: $text"
|
||||
WARN_LOGFILE =
|
||||
WARN_LOGFILE =
|
||||
#---------------------------------------------------------------------------
|
||||
# configuration options related to the input files
|
||||
# Configuration options related to the input files
|
||||
#---------------------------------------------------------------------------
|
||||
INPUT = @CMAKE_SOURCE_DIR@/taglib
|
||||
INPUT_ENCODING = UTF-8
|
||||
FILE_PATTERNS = *.h \
|
||||
*.hh \
|
||||
*.H
|
||||
*.H \
|
||||
*.dox
|
||||
RECURSIVE = YES
|
||||
EXCLUDE =
|
||||
EXCLUDE =
|
||||
EXCLUDE_SYMLINKS = NO
|
||||
EXCLUDE_PATTERNS =
|
||||
EXAMPLE_PATH =
|
||||
EXAMPLE_PATTERNS =
|
||||
EXCLUDE_PATTERNS =
|
||||
EXCLUDE_SYMBOLS =
|
||||
EXAMPLE_PATH =
|
||||
EXAMPLE_PATTERNS = *
|
||||
EXAMPLE_RECURSIVE = NO
|
||||
IMAGE_PATH =
|
||||
INPUT_FILTER =
|
||||
IMAGE_PATH =
|
||||
INPUT_FILTER =
|
||||
FILTER_PATTERNS =
|
||||
FILTER_SOURCE_FILES = NO
|
||||
FILTER_SOURCE_PATTERNS =
|
||||
USE_MDFILE_AS_MAINPAGE =
|
||||
#---------------------------------------------------------------------------
|
||||
# configuration options related to source browsing
|
||||
# Configuration options related to source browsing
|
||||
#---------------------------------------------------------------------------
|
||||
SOURCE_BROWSER = NO
|
||||
INLINE_SOURCES = NO
|
||||
STRIP_CODE_COMMENTS = YES
|
||||
REFERENCED_BY_RELATION = YES
|
||||
REFERENCES_RELATION = YES
|
||||
REFERENCES_LINK_SOURCE = YES
|
||||
SOURCE_TOOLTIPS = YES
|
||||
USE_HTAGS = NO
|
||||
VERBATIM_HEADERS = YES
|
||||
CLANG_ASSISTED_PARSING = NO
|
||||
CLANG_ADD_INC_PATHS = YES
|
||||
CLANG_OPTIONS =
|
||||
CLANG_DATABASE_PATH =
|
||||
#---------------------------------------------------------------------------
|
||||
# configuration options related to the alphabetical class index
|
||||
# Configuration options related to the alphabetical class index
|
||||
#---------------------------------------------------------------------------
|
||||
ALPHABETICAL_INDEX = YES
|
||||
COLS_IN_ALPHA_INDEX = 5
|
||||
IGNORE_PREFIX =
|
||||
IGNORE_PREFIX =
|
||||
#---------------------------------------------------------------------------
|
||||
# configuration options related to the HTML output
|
||||
# Configuration options related to the HTML output
|
||||
#---------------------------------------------------------------------------
|
||||
GENERATE_HTML = YES
|
||||
HTML_OUTPUT = html
|
||||
HTML_FILE_EXTENSION = .html
|
||||
HTML_HEADER = @CMAKE_SOURCE_DIR@/doc/api-header.html
|
||||
HTML_FOOTER = @CMAKE_SOURCE_DIR@/doc/api-footer.html
|
||||
HTML_STYLESHEET = @CMAKE_SOURCE_DIR@/doc/taglib-api.css
|
||||
HTML_ALIGN_MEMBERS = YES
|
||||
HTML_STYLESHEET =
|
||||
HTML_EXTRA_STYLESHEET =
|
||||
HTML_EXTRA_FILES =
|
||||
HTML_COLORSTYLE_HUE = 220
|
||||
HTML_COLORSTYLE_SAT = 100
|
||||
HTML_COLORSTYLE_GAMMA = 80
|
||||
HTML_TIMESTAMP = NO
|
||||
HTML_DYNAMIC_MENUS = YES
|
||||
HTML_DYNAMIC_SECTIONS = NO
|
||||
HTML_INDEX_NUM_ENTRIES = 100
|
||||
GENERATE_DOCSET = NO
|
||||
DOCSET_FEEDNAME = "Doxygen generated docs"
|
||||
DOCSET_BUNDLE_ID = org.doxygen.Project
|
||||
DOCSET_PUBLISHER_ID = org.doxygen.Publisher
|
||||
DOCSET_PUBLISHER_NAME = Publisher
|
||||
GENERATE_HTMLHELP = NO
|
||||
CHM_FILE =
|
||||
HHC_LOCATION =
|
||||
CHM_FILE =
|
||||
HHC_LOCATION =
|
||||
GENERATE_CHI = NO
|
||||
CHM_INDEX_ENCODING =
|
||||
BINARY_TOC = NO
|
||||
TOC_EXPAND = NO
|
||||
DISABLE_INDEX = YES
|
||||
ENUM_VALUES_PER_LINE = 4
|
||||
GENERATE_QHP = NO
|
||||
QCH_FILE =
|
||||
QHP_NAMESPACE = org.doxygen.Project
|
||||
QHP_VIRTUAL_FOLDER = doc
|
||||
QHP_CUST_FILTER_NAME =
|
||||
QHP_CUST_FILTER_ATTRS =
|
||||
QHP_SECT_FILTER_ATTRS =
|
||||
QHG_LOCATION =
|
||||
GENERATE_ECLIPSEHELP = NO
|
||||
ECLIPSE_DOC_ID = org.doxygen.Project
|
||||
DISABLE_INDEX = NO
|
||||
GENERATE_TREEVIEW = NO
|
||||
ENUM_VALUES_PER_LINE = 4
|
||||
TREEVIEW_WIDTH = 250
|
||||
EXT_LINKS_IN_WINDOW = NO
|
||||
HTML_FORMULA_FORMAT = png
|
||||
FORMULA_FONTSIZE = 10
|
||||
FORMULA_TRANSPARENT = YES
|
||||
FORMULA_MACROFILE =
|
||||
USE_MATHJAX = NO
|
||||
MATHJAX_FORMAT = HTML-CSS
|
||||
MATHJAX_RELPATH = https://cdn.jsdelivr.net/npm/mathjax@2
|
||||
MATHJAX_EXTENSIONS =
|
||||
MATHJAX_CODEFILE =
|
||||
SEARCHENGINE = NO
|
||||
SERVER_BASED_SEARCH = NO
|
||||
EXTERNAL_SEARCH = NO
|
||||
SEARCHENGINE_URL =
|
||||
SEARCHDATA_FILE = searchdata.xml
|
||||
EXTERNAL_SEARCH_ID =
|
||||
EXTRA_SEARCH_MAPPINGS =
|
||||
#---------------------------------------------------------------------------
|
||||
# configuration options related to the LaTeX output
|
||||
# Configuration options related to the LaTeX output
|
||||
#---------------------------------------------------------------------------
|
||||
GENERATE_LATEX = NO
|
||||
LATEX_OUTPUT = latex
|
||||
LATEX_CMD_NAME = latex
|
||||
MAKEINDEX_CMD_NAME = makeindex
|
||||
LATEX_MAKEINDEX_CMD = makeindex
|
||||
COMPACT_LATEX = NO
|
||||
PAPER_TYPE = letter
|
||||
EXTRA_PACKAGES =
|
||||
LATEX_HEADER =
|
||||
EXTRA_PACKAGES =
|
||||
LATEX_HEADER =
|
||||
LATEX_FOOTER =
|
||||
LATEX_EXTRA_STYLESHEET =
|
||||
LATEX_EXTRA_FILES =
|
||||
PDF_HYPERLINKS = YES
|
||||
USE_PDFLATEX = YES
|
||||
LATEX_BATCHMODE = NO
|
||||
LATEX_HIDE_INDICES = NO
|
||||
LATEX_SOURCE_CODE = NO
|
||||
LATEX_BIB_STYLE = plain
|
||||
LATEX_TIMESTAMP = NO
|
||||
LATEX_EMOJI_DIRECTORY =
|
||||
#---------------------------------------------------------------------------
|
||||
# configuration options related to the RTF output
|
||||
# Configuration options related to the RTF output
|
||||
#---------------------------------------------------------------------------
|
||||
GENERATE_RTF = NO
|
||||
RTF_OUTPUT = rtf
|
||||
COMPACT_RTF = NO
|
||||
RTF_HYPERLINKS = NO
|
||||
RTF_STYLESHEET_FILE =
|
||||
RTF_EXTENSIONS_FILE =
|
||||
RTF_STYLESHEET_FILE =
|
||||
RTF_EXTENSIONS_FILE =
|
||||
RTF_SOURCE_CODE = NO
|
||||
#---------------------------------------------------------------------------
|
||||
# configuration options related to the man page output
|
||||
# Configuration options related to the man page output
|
||||
#---------------------------------------------------------------------------
|
||||
GENERATE_MAN = NO
|
||||
MAN_OUTPUT = man
|
||||
MAN_EXTENSION = .3
|
||||
MAN_SUBDIR =
|
||||
MAN_LINKS = NO
|
||||
#---------------------------------------------------------------------------
|
||||
# configuration options related to the XML output
|
||||
# Configuration options related to the XML output
|
||||
#---------------------------------------------------------------------------
|
||||
GENERATE_XML = NO
|
||||
XML_OUTPUT = xml
|
||||
XML_SCHEMA =
|
||||
XML_DTD =
|
||||
XML_PROGRAMLISTING = YES
|
||||
XML_NS_MEMB_FILE_SCOPE = NO
|
||||
#---------------------------------------------------------------------------
|
||||
# configuration options for the AutoGen Definitions output
|
||||
# Configuration options related to the DOCBOOK output
|
||||
#---------------------------------------------------------------------------
|
||||
GENERATE_DOCBOOK = NO
|
||||
DOCBOOK_OUTPUT = docbook
|
||||
DOCBOOK_PROGRAMLISTING = NO
|
||||
#---------------------------------------------------------------------------
|
||||
# Configuration options for the AutoGen Definitions output
|
||||
#---------------------------------------------------------------------------
|
||||
GENERATE_AUTOGEN_DEF = NO
|
||||
#---------------------------------------------------------------------------
|
||||
# configuration options related to the Perl module output
|
||||
# Configuration options related to the Perl module output
|
||||
#---------------------------------------------------------------------------
|
||||
GENERATE_PERLMOD = NO
|
||||
PERLMOD_LATEX = NO
|
||||
PERLMOD_PRETTY = YES
|
||||
PERLMOD_MAKEVAR_PREFIX =
|
||||
PERLMOD_MAKEVAR_PREFIX =
|
||||
#---------------------------------------------------------------------------
|
||||
# Configuration options related to the preprocessor
|
||||
# Configuration options related to the preprocessor
|
||||
#---------------------------------------------------------------------------
|
||||
ENABLE_PREPROCESSING = YES
|
||||
MACRO_EXPANSION = NO
|
||||
EXPAND_ONLY_PREDEF = NO
|
||||
SEARCH_INCLUDES = YES
|
||||
INCLUDE_PATH =
|
||||
INCLUDE_FILE_PATTERNS =
|
||||
INCLUDE_PATH =
|
||||
INCLUDE_FILE_PATTERNS =
|
||||
PREDEFINED = DO_NOT_DOCUMENT \
|
||||
DOXYGEN \
|
||||
WITH_MP4 \
|
||||
WITH_ASF
|
||||
EXPAND_AS_DEFINED =
|
||||
DOXYGEN
|
||||
EXPAND_AS_DEFINED =
|
||||
SKIP_FUNCTION_MACROS = YES
|
||||
#---------------------------------------------------------------------------
|
||||
# Configuration::addtions related to external references
|
||||
# Configuration options related to external references
|
||||
#---------------------------------------------------------------------------
|
||||
TAGFILES =
|
||||
GENERATE_TAGFILE =
|
||||
TAGFILES =
|
||||
GENERATE_TAGFILE =
|
||||
ALLEXTERNALS = NO
|
||||
EXTERNAL_GROUPS = YES
|
||||
PERL_PATH = /usr/bin/perl
|
||||
EXTERNAL_PAGES = YES
|
||||
#---------------------------------------------------------------------------
|
||||
# Configuration options related to the dot tool
|
||||
# Configuration options related to the dot tool
|
||||
#---------------------------------------------------------------------------
|
||||
CLASS_DIAGRAMS = YES
|
||||
DIA_PATH =
|
||||
HIDE_UNDOC_RELATIONS = YES
|
||||
HAVE_DOT = YES
|
||||
DOT_NUM_THREADS = 0
|
||||
DOT_FONTNAME = Helvetica
|
||||
DOT_FONTSIZE = 10
|
||||
DOT_FONTPATH =
|
||||
CLASS_GRAPH = YES
|
||||
COLLABORATION_GRAPH = YES
|
||||
GROUP_GRAPHS = YES
|
||||
UML_LOOK = NO
|
||||
UML_LIMIT_NUM_FIELDS = 10
|
||||
DOT_UML_DETAILS = NO
|
||||
DOT_WRAP_THRESHOLD = 17
|
||||
TEMPLATE_RELATIONS = YES
|
||||
INCLUDE_GRAPH = YES
|
||||
INCLUDED_BY_GRAPH = YES
|
||||
CALL_GRAPH = NO
|
||||
CALLER_GRAPH = NO
|
||||
GRAPHICAL_HIERARCHY = YES
|
||||
DOT_IMAGE_FORMAT = png
|
||||
DOT_PATH =
|
||||
DOTFILE_DIRS =
|
||||
MAX_DOT_GRAPH_WIDTH = 1024
|
||||
MAX_DOT_GRAPH_HEIGHT = 1024
|
||||
DIRECTORY_GRAPH = YES
|
||||
DOT_IMAGE_FORMAT = svg
|
||||
INTERACTIVE_SVG = NO
|
||||
DOT_PATH =
|
||||
DOTFILE_DIRS =
|
||||
MSCFILE_DIRS =
|
||||
DIAFILE_DIRS =
|
||||
PLANTUML_JAR_PATH =
|
||||
PLANTUML_CFG_FILE =
|
||||
PLANTUML_INCLUDE_PATH =
|
||||
DOT_GRAPH_MAX_NODES = 100
|
||||
MAX_DOT_GRAPH_DEPTH = 0
|
||||
DOT_TRANSPARENT = NO
|
||||
DOT_MULTI_TARGETS = NO
|
||||
GENERATE_LEGEND = YES
|
||||
DOT_CLEANUP = YES
|
||||
#---------------------------------------------------------------------------
|
||||
# Configuration::addtions related to the search engine
|
||||
#---------------------------------------------------------------------------
|
||||
SEARCHENGINE = NO
|
||||
|
||||
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" \
|
||||
-DENABLE_STATIC=ON \
|
||||
-DCMAKE_INSTALL_PREFIX="<folder you want to build to>"
|
||||
|
||||
After 'make', and 'make install', add libtag.a to your XCode project, and add
|
||||
the include folder to the project's User Header Search Paths.
|
||||
|
||||
Windows
|
||||
-------
|
||||
|
||||
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 enable the following two options with CMake.
|
||||
-DENABLE_STATIC=ON -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
|
||||
|
||||
440
INSTALL.md
Normal file
440
INSTALL.md
Normal file
@@ -0,0 +1,440 @@
|
||||
# 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 [...]
|
||||
|
||||
If you want to build TagLib without ZLib, you can use
|
||||
|
||||
cmake -DCMAKE_INSTALL_PREFIX=/usr/local -DCMAKE_BUILD_TYPE=Release -DWITH_ZLIB=OFF .
|
||||
make
|
||||
sudo make install
|
||||
|
||||
See [cmake(1)](https://cmake.org/cmake/help/latest/manual/cmake.1.html) for
|
||||
generic help on running CMake.
|
||||
|
||||
## Build Options
|
||||
|
||||
These are the most important build options. For details, have a look into the
|
||||
CMakeLists.txt file.
|
||||
|
||||
| Option | Description |
|
||||
| ----------------------- | -------------------------------------------------- |
|
||||
| `BUILD_SHARED_LIBS` | Build shared libraries |
|
||||
| `CMAKE_BUILD_TYPE` | Debug, Release, RelWithDebInfo, MinSizeRel |
|
||||
| `BUILD_EXAMPLES` | Build examples |
|
||||
| `BUILD_BINDINGS` | Build C bindings |
|
||||
| `BUILD_TESTING` | Build unit tests |
|
||||
| `TRACE_IN_RELEASE` | Enable debug output in release builds |
|
||||
| `WITH_ZLIB` | Whether to build with ZLib (default ON) |
|
||||
| `ZLIB_ROOT` | Where to find ZLib's root directory |
|
||||
| `ZLIB_INCLUDE_DIR` | Where to find ZLib's include directory |
|
||||
| `ZLIB_LIBRARY` | Where to find ZLib's library |
|
||||
| `CMAKE_INSTALL_PREFIX` | Where to install Taglib |
|
||||
| `TAGLIB_INSTALL_SUFFIX` | Suffix added to installed libraries, includes, ... |
|
||||
| `ENABLE_STATIC_RUNTIME` | Link with MSVC runtime statically |
|
||||
| `BUILD_FRAMEWORK` | Build a macOS framework |
|
||||
|
||||
If you want to install TagLib 2 alongside TagLib 1, you can use
|
||||
`-DTAGLIB_INSTALL_SUFFIX=-2` and make sure that `BUILD_EXAMPLES` is not `ON`
|
||||
for both versions. The installed files will then include bin/taglib-2-config,
|
||||
include/taglib-2, cmake/taglib-2, pkgconfig/taglib-2.pc,
|
||||
pkgconfig/taglib_c-2.pc and the libraries have a suffix "-2".
|
||||
|
||||
|
||||
## Dependencies
|
||||
|
||||
A required dependency is [utf8cpp](https://github.com/nemtrif/utfcpp). You can
|
||||
install the corresponding package (libutfcpp-dev on Ubuntu, utf8cpp in Homebrew,
|
||||
utfcpp in vcpkg) or fetch the Git submodule with `git submodule update --init`.
|
||||
|
||||
Optional dependencies are
|
||||
- [zlib](https://www.zlib.net/): You can disable it with `-DWITH_ZLIB=OFF`,
|
||||
build and install it from the sources or use a package (zlib1g-dev on Ubuntu,
|
||||
zlib in vcpkg). It is needed for compressed ID3v2 frames.
|
||||
- [CppUnit](https://wiki.documentfoundation.org/Cppunit): Is required for unit
|
||||
tests, which are disabled by default. If you enable them with
|
||||
`-DBUILD_TESTING=ON`, you can build and install it from the sources or use a
|
||||
package (libcppunit-dev on Ubuntu, cppunit in Homebrew, cppunit in vcpkg).
|
||||
If the unit tests are enabled, you can run them with your build tool
|
||||
(`make check`, `ninja check`) or via CMake
|
||||
`cmake --build /path/to/build-dir --target check`.
|
||||
|
||||
## UNIX (Including Linux, BSD and macOS)
|
||||
|
||||
#### Building TagLib
|
||||
|
||||
On Linux, you can install the dependencies using the package manager of your
|
||||
distribution. On macOS with Homebrew, you can use `brew install cppunit utf8cpp`
|
||||
to install the dependencies.
|
||||
|
||||
```
|
||||
# Adapt these environment variables to your directories
|
||||
TAGLIB_SRC_DIR=$HOME/projects/taglib/src/taglib
|
||||
TAGLIB_DST_DIR=$HOME/projects/taglib/src/build
|
||||
cd $TAGLIB_SRC_DIR
|
||||
cmake -B $TAGLIB_DST_DIR -DBUILD_SHARED_LIBS=ON -DVISIBILITY_HIDDEN=ON \
|
||||
-DBUILD_TESTING=ON -DBUILD_EXAMPLES=ON -DBUILD_BINDINGS=ON \
|
||||
-DCMAKE_BUILD_TYPE=Release
|
||||
cmake --build $TAGLIB_DST_DIR --config Release
|
||||
cmake --build $TAGLIB_DST_DIR --config Release --target check
|
||||
|
||||
# Install to ~/pkg folder
|
||||
cmake --install $TAGLIB_DST_DIR --config Release --prefix $HOME/pkg --strip
|
||||
|
||||
# Run example from installed package
|
||||
LD_LIBRARY_PATH=$HOME/pkg/lib $HOME/pkg/bin/tagreader /path/to/audio-file
|
||||
```
|
||||
|
||||
#### Building a Project Using TagLib
|
||||
|
||||
As an example for an external application using TagLib, we create a folder
|
||||
taglib-client and copy the file tagreader.cpp from the examples folder
|
||||
of the TagLib sources into it. We then add this simple CMakeLists.txt.
|
||||
|
||||
```
|
||||
cmake_minimum_required(VERSION 3.5.0)
|
||||
project(taglib-client)
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
find_package(ZLIB)
|
||||
find_package(TagLib 2.0.0 REQUIRED)
|
||||
add_executable(tagreader tagreader.cpp)
|
||||
target_link_libraries(tagreader TagLib::tag)
|
||||
```
|
||||
|
||||
To build into a folder `build` inside this directory, just run
|
||||
|
||||
```
|
||||
# Set this to the path where TagLib is installed
|
||||
TAGLIB_PREFIX=$HOME/pkg
|
||||
cmake -B build -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=$TAGLIB_PREFIX
|
||||
cmake --build build --config Release
|
||||
|
||||
build/tagreader /path/to/audio-file
|
||||
```
|
||||
|
||||
If TagLib is installed in a standard location (e.g. /usr, /usr/local), its CMake
|
||||
configuration is found without setting CMAKE_INSTALL_PREFIX (or alternatives
|
||||
like CMAKE_PREFIX_PATH, CMAKE_SYSTEM_PREFIX_PATH).
|
||||
|
||||
To use the C-bindings with tagreader_c.c, you can change the last two lines in
|
||||
CMakeLists.txt to
|
||||
|
||||
```
|
||||
add_executable(tagreader_c tagreader_c.c)
|
||||
target_link_libraries(tagreader_c TagLib::tag_c)
|
||||
```
|
||||
|
||||
#### Building a Project Using pkg-config
|
||||
|
||||
Before version 2.0, TagLib did not export CMake configuration, therefore
|
||||
`pkg-config` could be used. This is still possible.
|
||||
|
||||
Note, however, that `pkg-config` makes it more difficult to use packages which
|
||||
are not installed in a standard location. You can point `pkg-config` to search
|
||||
in non-standard locations for .pc-files, but they still contain the install
|
||||
locations defined when building TagLib. Since we did not give a
|
||||
`CMAKE_INSTALL_PREFIX` in the example above, the default `/usr/local/` is used.
|
||||
|
||||
```
|
||||
PKG_CONFIG_PATH=$HOME/pkg/lib/pkgconfig pkg-config --libs --cflags taglib
|
||||
-I/usr/local/include -I/usr/local/include/taglib -L/usr/local/lib -ltag -lz
|
||||
```
|
||||
|
||||
The following examples use the same build example with additional CMake
|
||||
parameters affecting the installation location.
|
||||
|
||||
- Using the default prefix `-DCMAKE_INSTALL_PREFIX=/usr/local`:
|
||||
```
|
||||
-I/usr/local/include -I/usr/local/include/taglib -L/usr/local/lib -ltag -lz
|
||||
```
|
||||
- Using an absolute prefix `-DCMAKE_INSTALL_PREFIX=/usr`:
|
||||
```
|
||||
-I/usr/include/taglib -ltag -lz
|
||||
```
|
||||
- Using absolute lib and include directories
|
||||
`-DCMAKE_INSTALL_LIBDIR=/abs-lib -DCMAKE_INSTALL_INCLUDEDIR=/abs-include -DCMAKE_INSTALL_PREFIX=/usr`:
|
||||
```
|
||||
-I/abs-include -I/abs-include/taglib -L/abs-lib -ltag -lz
|
||||
```
|
||||
- Using relative lib and include directories
|
||||
`-DCMAKE_INSTALL_LIBDIR=rel-lib -DCMAKE_INSTALL_INCLUDEDIR=rel-include -DCMAKE_INSTALL_PREFIX=/usr`:
|
||||
```
|
||||
-I/usr/rel-include -I/usr/rel-include/taglib -L/usr/rel-lib -ltag -lz
|
||||
```
|
||||
This is the output of
|
||||
```
|
||||
PKG_CONFIG_PATH=$HOME/pkg/rel-lib/pkgconfig pkg-config --libs --cflags taglib
|
||||
```
|
||||
You could add `--define-prefix` to the `pkg-config` arguments to have the
|
||||
effective location `$HOME/pkg/` instead of `/usr/` in the output.
|
||||
|
||||
Therefore, the correct paths for our example package would be given when using
|
||||
the `--define-prefix` feature.
|
||||
|
||||
```
|
||||
PKG_CONFIG_PATH=$HOME/pkg/lib/pkgconfig pkg-config --define-prefix --libs --cflags taglib
|
||||
```
|
||||
|
||||
You can use pkg-config from CMake, however, relocation with `--define-prefix`
|
||||
is not supported.
|
||||
|
||||
```
|
||||
cmake_minimum_required(VERSION 3.6.0)
|
||||
project(taglib-client)
|
||||
find_package(PkgConfig)
|
||||
pkg_check_modules(TAGLIB REQUIRED IMPORTED_TARGET taglib)
|
||||
add_executable(tagreader tagreader.cpp)
|
||||
target_link_libraries(tagreader PkgConfig::TAGLIB)
|
||||
```
|
||||
|
||||
#### Framework on macOS
|
||||
|
||||
On macOS, you might want to build a framework that can be easily integrated
|
||||
into your application. If you set the BUILD_FRAMEWORK option on, it will compile
|
||||
TagLib as a framework. For example, the following command can be used to build
|
||||
a framework with macOS 10.10 as the deployment target:
|
||||
|
||||
mkdir build; cd build
|
||||
cmake .. -DCMAKE_BUILD_TYPE=Release \
|
||||
-DBUILD_TESTING=OFF \
|
||||
-DBUILD_FRAMEWORK=ON \
|
||||
-DCMAKE_OSX_DEPLOYMENT_TARGET=10.10 \
|
||||
-DCMAKE_OSX_ARCHITECTURES="arm64;x86_64"
|
||||
make
|
||||
|
||||
For a 10.10 static library, use:
|
||||
|
||||
mkdir build; cd build
|
||||
cmake .. -DCMAKE_BUILD_TYPE=Release \
|
||||
-DBUILD_TESTING=OFF \
|
||||
-DBUILD_SHARED_LIBS=OFF \
|
||||
-DCMAKE_OSX_DEPLOYMENT_TARGET=10.10 \
|
||||
-DCMAKE_OSX_ARCHITECTURES="arm64;x86_64"
|
||||
make
|
||||
|
||||
After `make`, and `make install`, add `libtag.` to your XCode project, and add
|
||||
the include folder to the project's User Header Search Paths.
|
||||
|
||||
## Windows
|
||||
|
||||
### Using Visual Studio Build Tools
|
||||
|
||||
For this example, we assume that you have the Visual Studio Build Tools 2022
|
||||
installed. Additionally, you need [cmake](https://cmake.org/), which can be
|
||||
installed for example using [scoop](https://scoop.sh/) with
|
||||
`scoop install cmake`.
|
||||
|
||||
#### Building TagLib (MSVC)
|
||||
|
||||
You can build and install the dependencies
|
||||
[utf8cpp](https://github.com/nemtrif/utfcpp) and optionally
|
||||
[zlib](https://www.zlib.net/) and
|
||||
[CppUnit](https://wiki.documentfoundation.org/Cppunit) yourself, but it is
|
||||
probably easier using a package manager such as [vcpkg](https://vcpkg.io/),
|
||||
which can be installed using `scoop install vcpkg` and then install the
|
||||
dependencies using `vcpkg install utfcpp zlib cppunit`.
|
||||
|
||||
Now you can build TagLib from PowerShell.
|
||||
|
||||
```
|
||||
# Adapt these environment variables to your directories
|
||||
$env:TAGLIB_SRC_DIR = "${env:HOMEDRIVE}${env:HOMEPATH}/projects/taglib/src/taglib"
|
||||
$env:TAGLIB_DST_DIR = "${env:HOMEDRIVE}${env:HOMEPATH}/projects/taglib/src/msvs_vcpkg_build"
|
||||
cd $env:TAGLIB_SRC_DIR
|
||||
cmake -B $env:TAGLIB_DST_DIR -DBUILD_SHARED_LIBS=ON -DVISIBILITY_HIDDEN=ON `
|
||||
-DBUILD_TESTING=ON -DBUILD_EXAMPLES=ON -DBUILD_BINDINGS=ON `
|
||||
-DCMAKE_BUILD_TYPE=Release `
|
||||
-DCMAKE_TOOLCHAIN_FILE="$env:VCPKG_ROOT/scripts/buildsystems/vcpkg.cmake" `
|
||||
-G "Visual Studio 17 2022"
|
||||
cmake --build $env:TAGLIB_DST_DIR --config Release
|
||||
|
||||
# Add directories containing DLL dependencies to path
|
||||
$env:Path += -join(";$env:TAGLIB_DST_DIR\taglib\Release;",
|
||||
"$env:TAGLIB_DST_DIR\bindings\c\Release;",
|
||||
"$env:VCPKG_ROOT\packages\cppunit_x64-windows\bin;",
|
||||
"$env:VCPKG_ROOT\packages\utfcpp_x64-windows\bin;",
|
||||
"$env:VCPKG_ROOT\packages\zlib_x64-windows\bin")
|
||||
cmake --build $env:TAGLIB_DST_DIR --config Release --target check
|
||||
|
||||
# Install to \pkg folder on current drive
|
||||
cmake --install $env:TAGLIB_DST_DIR --config Release --prefix /pkg --strip
|
||||
|
||||
# Static library
|
||||
$env:TAGLIB_DST_DIR = "C:/Users/fle/projects/taglib/src/msvs_vcpkg_static_build"
|
||||
cmake -B $env:TAGLIB_DST_DIR -DBUILD_SHARED_LIBS=OFF -DVISIBILITY_HIDDEN=ON `
|
||||
-DBUILD_TESTING=ON -DBUILD_EXAMPLES=ON -DBUILD_BINDINGS=ON `
|
||||
-DCMAKE_BUILD_TYPE=Release `
|
||||
-DCMAKE_TOOLCHAIN_FILE="$env:VCPKG_ROOT/scripts/buildsystems/vcpkg.cmake" `
|
||||
-G "Visual Studio 17 2022"
|
||||
cmake --build $env:TAGLIB_DST_DIR --config Release
|
||||
|
||||
cmake --build $env:TAGLIB_DST_DIR --config Release --target check
|
||||
|
||||
# Install to \pkg_static folder on current drive
|
||||
cmake --install $env:TAGLIB_DST_DIR --config Release --prefix /pkg_static --strip
|
||||
```
|
||||
|
||||
Including `ENABLE_STATIC_RUNTIME=ON` indicates you want TagLib built using the
|
||||
static runtime library, rather than the DLL form of the runtime.
|
||||
|
||||
#### Building a Project Using TagLib (MSVC)
|
||||
|
||||
As an example for an external application using TagLib, we create a folder
|
||||
taglib-client and copy the file tagreader.cpp from the examples folder
|
||||
of the TagLib sources into it. We then add this simple CMakeLists.txt.
|
||||
|
||||
```
|
||||
cmake_minimum_required(VERSION 3.5.0)
|
||||
project(taglib-client)
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
find_package(ZLIB)
|
||||
find_package(TagLib 2.0.0 REQUIRED)
|
||||
add_executable(tagreader tagreader.cpp)
|
||||
target_link_libraries(tagreader TagLib::tag)
|
||||
```
|
||||
|
||||
To build into a folder build inside this directory, just run
|
||||
|
||||
```
|
||||
# Set this to the path where TagLib is installed
|
||||
$env:TAGLIB_PREFIX = "/pkg"
|
||||
cmake -B build -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX="$env:TAGLIB_PREFIX" `
|
||||
-DCMAKE_TOOLCHAIN_FILE="$env:VCPKG_ROOT/scripts/buildsystems/vcpkg.cmake" `
|
||||
-G "Visual Studio 17 2022"
|
||||
cmake --build build --config Release
|
||||
|
||||
# Add directories containing DLL dependencies to path
|
||||
$env:Path += ";$env:TAGLIB_PREFIX\bin;$env:VCPKG_ROOT\packages\zlib_x64-windows\bin"
|
||||
build\Release\tagreader /path/to/audio-file
|
||||
```
|
||||
|
||||
To use the C-bindings with tagreader_c.c, you can change the last two lines in
|
||||
CMakeLists.txt to
|
||||
|
||||
```
|
||||
add_executable(tagreader_c tagreader_c.c)
|
||||
target_link_libraries(tagreader_c TagLib::tag_c)
|
||||
```
|
||||
|
||||
If you link against a static TagLib, you have to adapt the installation path
|
||||
(e.g. "/pkg_static") and add the following line to CMakeLists.txt
|
||||
|
||||
```
|
||||
target_compile_definitions(tagreader_c PRIVATE TAGLIB_STATIC)
|
||||
```
|
||||
|
||||
### Using MSYS2
|
||||
|
||||
#### Building TagLib (MSYS2)
|
||||
|
||||
To build TagLib using Clang from MSYS, install [msys2](https://www.msys2.org/),
|
||||
then start the MSYS CLANG64 shell and install the dependencies as they are
|
||||
specified in the [MSYS recipe for TagLib](
|
||||
https://packages.msys2.org/package/mingw-w64-clang-x86_64-taglib?repo=clang64).
|
||||
|
||||
```
|
||||
pacman -Suy
|
||||
pacman -S mingw-w64-clang-x86_64-gcc-libs mingw-w64-clang-x86_64-zlib \
|
||||
mingw-w64-clang-x86_64-cc mingw-w64-clang-x86_64-cmake \
|
||||
mingw-w64-clang-x86_64-cppunit mingw-w64-clang-x86_64-ninja
|
||||
```
|
||||
|
||||
Then you can build TagLib from the MSYS CLANG64 Bash.
|
||||
|
||||
```
|
||||
# Adapt these environment variables to your directories
|
||||
TAGLIB_SRC_DIR=$HOME/projects/taglib/src/taglib
|
||||
TAGLIB_DST_DIR=$HOME/projects/taglib/src/msys_build
|
||||
cd $TAGLIB_SRC_DIR
|
||||
cmake -B $TAGLIB_DST_DIR -DBUILD_SHARED_LIBS=ON -DVISIBILITY_HIDDEN=ON \
|
||||
-DBUILD_TESTING=ON -DBUILD_EXAMPLES=ON -DBUILD_BINDINGS=ON \
|
||||
-DCMAKE_BUILD_TYPE=Release -G Ninja
|
||||
cmake --build $TAGLIB_DST_DIR --config Release
|
||||
|
||||
PATH=$PATH:/clang64/bin:$TAGLIB_DST_DIR/taglib:$TAGLIB_DST_DIR/bindings/c \
|
||||
cmake --build $TAGLIB_DST_DIR --config Release --target check
|
||||
|
||||
# Install to /pkg_msys folder inside MSYS root (e.g. C:/msys64/pkg_msys/)
|
||||
cmake --install $TAGLIB_DST_DIR --config Release --prefix /pkg_msys --strip
|
||||
```
|
||||
|
||||
#### Building a Project Using TagLib (MSYS2)
|
||||
|
||||
As an example for an external application using TagLib, we create a folder
|
||||
taglib-client and copy the file tagreader.cpp from the examples folder
|
||||
of the TagLib sources into it. We then add this simple CMakeLists.txt.
|
||||
|
||||
```
|
||||
cmake_minimum_required(VERSION 3.5.0)
|
||||
project(taglib-client)
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
find_package(ZLIB)
|
||||
find_package(TagLib 2.0.0 REQUIRED)
|
||||
add_executable(tagreader tagreader.cpp)
|
||||
target_link_libraries(tagreader TagLib::tag)
|
||||
```
|
||||
|
||||
To build into a folder build_msys inside this directory, just run
|
||||
|
||||
```
|
||||
# Set this to the path where TagLib is installed
|
||||
TAGLIB_PREFIX=/pkg_msys
|
||||
cmake -B build_msys -DCMAKE_BUILD_TYPE=Release \
|
||||
-DCMAKE_INSTALL_PREFIX=$TAGLIB_PREFIX -G Ninja
|
||||
cmake --build build_msys --config Release
|
||||
|
||||
PATH=$PATH:$TAGLIB_PREFIX/bin
|
||||
build_msys/tagreader /path/to/audio-file
|
||||
```
|
||||
|
||||
To use the C-bindings with tagreader_c.c, you can change the last two lines in
|
||||
CMakeLists.txt to
|
||||
|
||||
```
|
||||
add_executable(tagreader_c tagreader_c.c)
|
||||
target_link_libraries(tagreader_c TagLib::tag_c)
|
||||
```
|
||||
|
||||
### Using MinGW
|
||||
|
||||
The instructions for MSYS2 can also be used to build with MinGW. You could use
|
||||
Git Bash together with the MinGW provided by Qt.
|
||||
|
||||
```
|
||||
# Adapt these environment variables to your directories
|
||||
PATH=$PATH:/c/Qt/Tools/mingw1120_64/bin
|
||||
TAGLIB_SRC_DIR=$HOME/projects/taglib/src/taglib
|
||||
TAGLIB_DST_DIR=$HOME/projects/taglib/src/mingw_build
|
||||
cd $TAGLIB_SRC_DIR
|
||||
cmake -B $TAGLIB_DST_DIR -DBUILD_SHARED_LIBS=ON -DVISIBILITY_HIDDEN=ON \
|
||||
-DBUILD_EXAMPLES=ON -DBUILD_BINDINGS=ON -DWITH_ZLIB=OFF \
|
||||
-DCMAKE_BUILD_TYPE=Release -G 'MinGW Makefiles'
|
||||
cmake --build $TAGLIB_DST_DIR --config Release
|
||||
|
||||
PATH=$PATH:$TAGLIB_DST_DIR/taglib \
|
||||
$TAGLIB_DST_DIR/examples/tagreader /path/to/audio-file
|
||||
|
||||
# Install to C:\pkg_mingw
|
||||
cmake --install $TAGLIB_DST_DIR --config Release --prefix /c/pkg_mingw --strip
|
||||
```
|
||||
|
||||
The installed package can then be used by other projects.
|
||||
|
||||
```
|
||||
TAGLIB_PREFIX=/c/pkg_mingw
|
||||
cmake -B build_mingw -DCMAKE_BUILD_TYPE=Release \
|
||||
-DCMAKE_INSTALL_PREFIX=$TAGLIB_PREFIX -G 'MinGW Makefiles'
|
||||
cmake --build build_mingw --config Release
|
||||
|
||||
PATH=$PATH:$TAGLIB_PREFIX/bin
|
||||
build_mingw/tagreader /path/to/audio-file
|
||||
```
|
||||
241
NEWS
241
NEWS
@@ -1,241 +0,0 @@
|
||||
TagLib 1.10 (Nov 11, 2015)
|
||||
==========================
|
||||
|
||||
1.10:
|
||||
|
||||
* Added new options to the tagwriter example.
|
||||
* Fixed self-assignment operator in some types.
|
||||
* Fixed extraction of MP4 tag keys with an empty list.
|
||||
|
||||
1.10 BETA:
|
||||
|
||||
* New API for the audio length in milliseconds.
|
||||
* Added support for ID3v2 ETCO and SYLT frames.
|
||||
* Added support for album artist in PropertyMap API of MP4 files.
|
||||
* Added support for embedded frames in ID3v2 CHAP and CTOC frames.
|
||||
* Added support for AIFF-C files.
|
||||
* Better handling of duplicate ID3v2 tags in MPEG files.
|
||||
* Allowed generating taglib.pc on Windows.
|
||||
* Added ZLIB_SOURCE build option.
|
||||
* Fixed backwards-incompatible change in TagLib::String when constructing UTF16 strings.
|
||||
* Fixed crash when parsing certain FLAC files.
|
||||
* Fixed crash when encoding empty strings.
|
||||
* Fixed saving of certain XM files on OS X.
|
||||
* Changed Xiph and APE generic getters to return space-concatenated values.
|
||||
* Fixed possible file corruptions when removing tags from WAV files.
|
||||
* Added support for MP4 files with 64-bit atoms in certain 64-bit environments.
|
||||
* Prevented ID3v2 padding from being too large.
|
||||
* Fixed crash when parsing corrupted APE files.
|
||||
* Fixed crash when parsing corrupted WAV files.
|
||||
* Fixed crash when parsing corrupted Ogg FLAC files.
|
||||
* Fixed crash when parsing corrupted MPEG files.
|
||||
* Fixed saving empty tags in WAV files.
|
||||
* Fixed crash when parsing corrupted Musepack files.
|
||||
* Fixed possible memory leaks when parsing AIFF and WAV files.
|
||||
* Fixed crash when parsing corrupted MP4 files.
|
||||
* Stopped writing empty ID3v2 frames.
|
||||
* Fixed possible file corruptions when saving WMA files.
|
||||
* Added TagLib::MP4::Tag::isEmpty().
|
||||
* Added accessors to manipulate MP4 tags.
|
||||
* Fixed crash when parsing corrupted WavPack files.
|
||||
* Fixed seeking MPEG frames.
|
||||
* Fixed reading FLAC files with zero-sized padding blocks.
|
||||
* Added support for reading the encoder information of WMA files.
|
||||
* Added support for reading the codec of WAV files.
|
||||
* Added support for multi channel WavPack files.
|
||||
* Added support for reading the nominal bitrate of Ogg Speex files.
|
||||
* Added support for VBR headers in MPEG files.
|
||||
* Marked FLAC::File::streamInfoData() deprecated. It returns an empty ByteVector.
|
||||
* Marked FLAC::File::streamLength() deprecated. It returns zero.
|
||||
* Fixed possible file corruptions when adding an ID3v1 tag to FLAC files.
|
||||
* Many smaller bug fixes and performance improvements.
|
||||
|
||||
TagLib 1.9.1 (Oct 8, 2013)
|
||||
==========================
|
||||
|
||||
* Fixed binary incompatible change in TagLib::Map and TagLib::List.
|
||||
* Fixed constructing String from ByteVector.
|
||||
* Fixed compilation on MSVC with the /Zc:wchar_t- option.
|
||||
* Fixed detecting of RIFF files with invalid chunk sizes.
|
||||
* Added TagLib::MP4::Properties::codec().
|
||||
|
||||
TagLib 1.9 (Oct 6, 2013)
|
||||
========================
|
||||
|
||||
* Added support for the Ogg Opus file format.
|
||||
* Added support for INFO tags in WAV files.
|
||||
* Changed FileStream to use Windows file API.
|
||||
* Included taglib-config.cmd script for Windows.
|
||||
* New ID3v1::Tag methods for working directly with genre numbers.
|
||||
* New MPEG::File methods for checking which tags are saved in the file.
|
||||
* Added support for the PropertyMap API to ASF and MP4 files.
|
||||
* Added MusicBrainz identifiers to the PropertyMap API.
|
||||
* Allowed reading of MP4 cover art without an explicitly specified format.
|
||||
* Better parsing of corrupted FLAC files.
|
||||
* Fixed saving of PropertyMap comments without description into ID3v2 tags.
|
||||
* Fixed crash when parsing certain XM files.
|
||||
* Fixed compilation of unit test with clang.
|
||||
* Better handling of files that can't be open or have read-only permissions.
|
||||
* Improved atomic reference counting.
|
||||
* New hookable API for debug messages.
|
||||
* More complete Windows install instructions.
|
||||
* Many smaller bug fixes and performance improvements.
|
||||
|
||||
TagLib 1.8 (Sep 6, 2012)
|
||||
========================
|
||||
|
||||
1.8:
|
||||
|
||||
* Added support for OWNE ID3 frames.
|
||||
* Changed key validation in the new PropertyMap API.
|
||||
* ID3v1::Tag::setStringHandler will no londer delete the previous handler,
|
||||
the caller is responsible for this.
|
||||
* File objects will also no longer delete the passed IOStream objects. It
|
||||
should be done in the caller code after the File object is no longer
|
||||
used.
|
||||
* Added ID3v2::Tag::setLatin1StringHandler for custom handling of
|
||||
latin1-encoded text in ID3v2 frames.
|
||||
* Fixed validation of ID3v2 frame IDs (IDs with '0' were ignored).
|
||||
|
||||
1.8 BETA:
|
||||
|
||||
* New API for accessing tags by name.
|
||||
* New abstract I/O stream layer to allow custom I/O handlers.
|
||||
* Support for writing ID3v2.3 tags.
|
||||
* Support for various module file formats (MOD, S3M, IT, XM).
|
||||
* Support for MP4 and ASF is now enabled by default.
|
||||
* Started using atomic int operations for reference counting.
|
||||
* Added methods for checking if WMA and MP4 files are DRM-protected.
|
||||
* Added taglib_free to the C bindings.
|
||||
* New method to allow removing pictures from FLAC files.
|
||||
* Support for reading audio properties from ALAC and Musepack SV8 files.
|
||||
* Added replay-gain information to Musepack audio properties.
|
||||
* Support for APEv2 binary tags.
|
||||
* Many AudioProperties subclasses now provide information about the total number of samples.
|
||||
* Various small bug fixes.
|
||||
|
||||
TagLib 1.7.2 (Apr 20, 2012)
|
||||
===========================
|
||||
|
||||
* Fixed division by zero while parsing corrupted MP4 files (CVE-2012-2396).
|
||||
* Fixed compilation on Haiku.
|
||||
|
||||
TagLib 1.7.1 (Mar 17, 2012)
|
||||
===========================
|
||||
|
||||
* Improved parsing of corrupted WMA, RIFF and OGG files.
|
||||
* Fixed a memory leak in the WMA parser.
|
||||
* Fixed a memory leak in the FLAC parser.
|
||||
* Fixed a possible division by zero in the APE parser.
|
||||
* Added detection of TTA2 files.
|
||||
* Fixed saving of multiple identically named tags to Vorbis Comments.
|
||||
|
||||
TagLib 1.7 (Mar 11, 2011)
|
||||
=========================
|
||||
|
||||
1.7:
|
||||
|
||||
* Fixed memory leaks in the FLAC file format parser.
|
||||
* Fixed bitrate calculation for WAV files.
|
||||
|
||||
1.7 RC1:
|
||||
|
||||
* Support for reading/writing tags from Monkey's Audio files. (BUG:210404)
|
||||
* Support for reading/writing embedded pictures from WMA files.
|
||||
* Support for reading/writing embedded pictures from FLAC files (BUG:218696).
|
||||
* Implemented APE::Tag::isEmpty() to check for all APE tags, not just the
|
||||
basic ones.
|
||||
* Added reading of WAV audio length. (BUG:116033)
|
||||
* Exposed FLAC MD5 signature of the uncompressed audio stream via
|
||||
FLAC::Properties::signature(). (BUG:160172)
|
||||
* Added function ByteVector::toHex() for hex-encoding of byte vectors.
|
||||
* WavPack reader now tries to get the audio length by finding the final
|
||||
block, if the header doesn't have the information. (BUG:258016)
|
||||
* Fixed a memory leak in the ID3v2.2 PIC frame parser. (BUG:257007)
|
||||
* Fixed writing of RIFF files with even chunk sizes. (BUG:243954)
|
||||
* Fixed compilation on MSVC 2010.
|
||||
* Removed support for building using autoconf/automake.
|
||||
* API docs can be now built using "make docs".
|
||||
|
||||
TagLib 1.6.3 (Apr 17, 2010)
|
||||
===========================
|
||||
|
||||
* Fixed definitions of the TAGLIB_WITH_MP4 and TAGLIB_WITH_ASF macros.
|
||||
* Fixed upgrading of ID3v2.3 genre frame with ID3v1 code 0 (Blues).
|
||||
* New method `int String::toInt(bool *ok)` which can return whether the
|
||||
conversion to a number was successfull.
|
||||
* Fixed parsing of incorrectly written lengths in ID3v2 (affects mainly
|
||||
compressed frames). (BUG:231075)
|
||||
|
||||
TagLib 1.6.2 (Apr 9, 2010)
|
||||
==========================
|
||||
|
||||
* Read Vorbis Comments from the first FLAC metadata block, if there are
|
||||
multipe ones. (BUG:211089)
|
||||
* Fixed a memory leak in FileRef's OGA format detection.
|
||||
* Fixed compilation with the Sun Studio compiler. (BUG:215225)
|
||||
* Handle WM/TrackNumber attributes with DWORD content in WMA files.
|
||||
(BUG:218526)
|
||||
* More strict check if something is a valid MP4 file. (BUG:216819)
|
||||
* Correctly save MP4 int-pair atoms with flags set to 0.
|
||||
* Fixed compilation of the test runner on Windows.
|
||||
* Store ASF attributes larger than 64k in the metadata library object.
|
||||
* Ignore trailing non-data atoms when parsing MP4 covr atoms.
|
||||
* Don't upgrade ID3v2.2 frame TDA to TDRC. (BUG:228968)
|
||||
|
||||
TagLib 1.6.1 (Oct 31, 2009)
|
||||
===========================
|
||||
|
||||
* Better detection of the audio codec of .oga files in FileRef.
|
||||
* Fixed saving of Vorbis comments to Ogg FLAC files. TagLib tried to
|
||||
include the Vorbis framing bit, which is only correct for Ogg Vorbis.
|
||||
* Public symbols now have explicitly set visibility to "default" on GCC.
|
||||
* Added missing exports for static ID3v1 functions.
|
||||
* Fixed a typo in taglib_c.pc
|
||||
* Fixed a failing test on ppc64.
|
||||
* Support for binary 'covr' atom in MP4 files. TagLib 1.6 treated them
|
||||
as text atoms, which corrupted them in some cases.
|
||||
* Fixed ID3v1-style genre to string conversion in MP4 files.
|
||||
|
||||
TagLib 1.6 (Sep 13, 2009)
|
||||
=========================
|
||||
|
||||
1.6:
|
||||
|
||||
* New CMake option to build a static version - ENABLE_STATIC.
|
||||
* Added support for disabling dllimport/dllexport on Windows using the
|
||||
TAGLIB_STATIC macro.
|
||||
* Support for parsing the obsolete 'gnre' MP4 atom.
|
||||
* New cpp macros TAGLIB_WITH_MP4 and TAGLIB_WITH_ASF to determin if
|
||||
TagLib was built with MP4/ASF support.
|
||||
|
||||
1.6 RC1:
|
||||
|
||||
* Split Ogg packets larger than 64k into multiple pages. (BUG:171957)
|
||||
* TagLib can now use FLAC padding block. (BUG:107659)
|
||||
* ID3v2.2 frames are now not incorrectly saved. (BUG:176373)
|
||||
* Support for ID3v2.2 PIC frames. (BUG:167786)
|
||||
* Fixed a bug in ByteVectorList::split().
|
||||
* XiphComment::year() now falls back to YEAR if DATE doesn't exist
|
||||
and XiphComment::year() falls back to TRACKNUM if TRACKNUMBER doesn't
|
||||
exist. (BUG:144396)
|
||||
* Improved ID3v2.3 genre parsing. (BUG:188578)
|
||||
* Better checking of corrupted ID3v2 APIC data. (BUG:168382)
|
||||
* Bitrate calculating using the Xing header now uses floating point
|
||||
numbers. (BUG:172556)
|
||||
* New TagLib::String method rfind().
|
||||
* Added support for MP4 file format with iTunes-style metadata [optional].
|
||||
* Added support for ASF (WMA) file format [optional].
|
||||
* Fixed crash when saving a Locator APEv2 tag. (BUG:169810)
|
||||
* Fixed a possible crash in the non-const version of String::operator[]
|
||||
and in String::operator+=. (BUG:169389)
|
||||
* Added support for PRIV ID3v2 frames.
|
||||
* Empty ID3v2 genres are no longer treated as numeric ID3v1 genres.
|
||||
* Added support for the POPM (rating/playcount) ID3v2 frame.
|
||||
* Generic RIFF file format support:
|
||||
* Support for AIFF files with ID3v2 tags.
|
||||
* Support for WAV files with ID3v2 tags.
|
||||
* Fixed crash on handling unsupported ID3v2 frames, e.g. on encrypted
|
||||
frames. (BUG:161721)
|
||||
* Fixed overflow while calculating bitrate of FLAC files with a very
|
||||
high bitrate.
|
||||
25
README.md
Normal file
25
README.md
Normal file
@@ -0,0 +1,25 @@
|
||||
# TagLib
|
||||
|
||||
[](../../actions)
|
||||
|
||||
### TagLib Audio Metadata Library
|
||||
|
||||
https://taglib.org/
|
||||
|
||||
TagLib is a library for reading and editing the metadata of several
|
||||
popular audio formats. Currently, it supports both ID3v1 and [ID3v2][]
|
||||
for MP3 files, [Ogg Vorbis][] comments and ID3 tags
|
||||
in [FLAC][], MPC, Speex, WavPack, TrueAudio, WAV, AIFF, MP4, APE,
|
||||
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]: https://id3.org/
|
||||
[Ogg Vorbis]: https://xiph.org/vorbis/
|
||||
[FLAC]: https://xiph.org/flac/
|
||||
[GNU Lesser General Public License]: https://www.gnu.org/licenses/lgpl.html
|
||||
[Mozilla Public License]: https://www.mozilla.org/MPL/MPL-1.1.html
|
||||
@@ -1 +1 @@
|
||||
add_subdirectory(c)
|
||||
add_subdirectory(c)
|
||||
|
||||
@@ -14,14 +14,35 @@ include_directories(
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/wavpack
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/ogg/speex
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/trueaudio
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/riff
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/riff/aiff
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/riff/wav
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/ape
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/it
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/mod
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/s3m
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/xm
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/ogg/opus
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/dsf
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/dsdiff
|
||||
)
|
||||
|
||||
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}")
|
||||
target_include_directories(tag_c INTERFACE
|
||||
$<INSTALL_INTERFACE:include/taglib${TAGLIB_INSTALL_SUFFIX}>
|
||||
)
|
||||
|
||||
target_link_libraries(tag_c PRIVATE tag)
|
||||
set_target_properties(tag_c PROPERTIES
|
||||
PUBLIC_HEADER "${tag_c_HDRS}"
|
||||
DEFINE_SYMBOL MAKE_TAGLIB_LIB
|
||||
)
|
||||
if(VISIBILITY_HIDDEN)
|
||||
set_target_properties(tag_c PROPERTIES C_VISIBILITY_PRESET hidden)
|
||||
endif()
|
||||
if(BUILD_FRAMEWORK)
|
||||
set_target_properties(tag_c PROPERTIES FRAMEWORK TRUE)
|
||||
endif()
|
||||
@@ -38,7 +59,7 @@ if(HAVE_CRUN_LIB)
|
||||
# the only game in town, the three available STLs -- Cstd,
|
||||
# stlport4 and stdcxx -- make this a mess. The KDE-Solaris
|
||||
# team supports stdcxx (Apache RogueWave stdcxx 4.1.3).
|
||||
|
||||
|
||||
# According to http://bugs.kde.org/show_bug.cgi?id=215225 the library can have the following two names:
|
||||
find_library(ROGUEWAVE_STDCXX_LIBRARY NAMES stdcxx4 stdcxx)
|
||||
if(NOT ROGUEWAVE_STDCXX_LIBRARY)
|
||||
@@ -48,21 +69,42 @@ if(HAVE_CRUN_LIB)
|
||||
endif()
|
||||
|
||||
set_target_properties(tag_c PROPERTIES
|
||||
VERSION 0.0.0
|
||||
SOVERSION 0
|
||||
VERSION ${TAGLIB_SOVERSION_MAJOR}.${TAGLIB_SOVERSION_MINOR}.${TAGLIB_SOVERSION_PATCH}
|
||||
SOVERSION ${TAGLIB_SOVERSION_MAJOR}
|
||||
DEFINE_SYMBOL MAKE_TAGLIB_C_LIB
|
||||
INSTALL_NAME_DIR ${LIB_INSTALL_DIR}
|
||||
INSTALL_NAME_DIR ${CMAKE_INSTALL_FULL_LIBDIR}
|
||||
)
|
||||
|
||||
if(TAGLIB_INSTALL_SUFFIX)
|
||||
if(BUILD_SHARED_LIBS)
|
||||
set(TAGLIB_LIBRARY_SUFFIX "${TAGLIB_INSTALL_SUFFIX}${CMAKE_SHARED_LIBRARY_SUFFIX}")
|
||||
else()
|
||||
set(TAGLIB_LIBRARY_SUFFIX "${TAGLIB_INSTALL_SUFFIX}${CMAKE_STATIC_LIBRARY_SUFFIX}")
|
||||
endif()
|
||||
set_target_properties(tag_c PROPERTIES SUFFIX ${TAGLIB_LIBRARY_SUFFIX})
|
||||
endif()
|
||||
|
||||
install(TARGETS tag_c
|
||||
FRAMEWORK DESTINATION ${FRAMEWORK_INSTALL_DIR}
|
||||
LIBRARY DESTINATION ${LIB_INSTALL_DIR}
|
||||
RUNTIME DESTINATION ${BIN_INSTALL_DIR}
|
||||
ARCHIVE DESTINATION ${LIB_INSTALL_DIR}
|
||||
PUBLIC_HEADER DESTINATION ${INCLUDE_INSTALL_DIR}/taglib
|
||||
EXPORT taglibTargets
|
||||
FRAMEWORK DESTINATION ${FRAMEWORK_INSTALL_DIR}
|
||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
|
||||
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/taglib${TAGLIB_INSTALL_SUFFIX}
|
||||
)
|
||||
|
||||
if(NOT BUILD_FRAMEWORK)
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/taglib_c.pc.cmake ${CMAKE_CURRENT_BINARY_DIR}/taglib_c.pc)
|
||||
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/taglib_c.pc DESTINATION ${LIB_INSTALL_DIR}/pkgconfig)
|
||||
if(IS_ABSOLUTE ${CMAKE_INSTALL_INCLUDEDIR})
|
||||
set(CMAKE_PC_INCLUDEDIR ${CMAKE_INSTALL_INCLUDEDIR})
|
||||
else()
|
||||
set(CMAKE_PC_INCLUDEDIR "\${prefix}/${CMAKE_INSTALL_INCLUDEDIR}")
|
||||
endif()
|
||||
if(IS_ABSOLUTE ${CMAKE_INSTALL_LIBDIR})
|
||||
set(CMAKE_PC_LIBDIR ${CMAKE_INSTALL_LIBDIR})
|
||||
else()
|
||||
set(CMAKE_PC_LIBDIR "\${prefix}/${CMAKE_INSTALL_LIBDIR}")
|
||||
endif()
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/taglib_c.pc.cmake ${CMAKE_CURRENT_BINARY_DIR}/taglib_c.pc @ONLY)
|
||||
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/taglib_c.pc DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig
|
||||
RENAME taglib${TAGLIB_INSTALL_SUFFIX}_c.pc)
|
||||
endif()
|
||||
|
||||
|
||||
@@ -19,30 +19,74 @@
|
||||
* USA *
|
||||
***************************************************************************/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <fileref.h>
|
||||
#include <tfile.h>
|
||||
#include <asffile.h>
|
||||
#include <vorbisfile.h>
|
||||
#include <mpegfile.h>
|
||||
#include <flacfile.h>
|
||||
#include <oggflacfile.h>
|
||||
#include <mpcfile.h>
|
||||
#include <wavpackfile.h>
|
||||
#include <speexfile.h>
|
||||
#include <trueaudiofile.h>
|
||||
#include <mp4file.h>
|
||||
#include <tag.h>
|
||||
#include <string.h>
|
||||
#include <id3v2framefactory.h>
|
||||
|
||||
#include "tag_c.h"
|
||||
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <sstream>
|
||||
#include <utility>
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
#endif
|
||||
#include "tstringlist.h"
|
||||
#include "tbytevectorlist.h"
|
||||
#include "tbytevectorstream.h"
|
||||
#include "tiostream.h"
|
||||
#include "tfile.h"
|
||||
#include "tpropertymap.h"
|
||||
#include "fileref.h"
|
||||
#include "asffile.h"
|
||||
#include "vorbisfile.h"
|
||||
#include "mpegfile.h"
|
||||
#include "flacfile.h"
|
||||
#include "oggflacfile.h"
|
||||
#include "mpcfile.h"
|
||||
#include "wavpackfile.h"
|
||||
#include "speexfile.h"
|
||||
#include "trueaudiofile.h"
|
||||
#include "mp4file.h"
|
||||
#include "aifffile.h"
|
||||
#include "wavfile.h"
|
||||
#include "apefile.h"
|
||||
#include "itfile.h"
|
||||
#include "modfile.h"
|
||||
#include "s3mfile.h"
|
||||
#include "xmfile.h"
|
||||
#include "opusfile.h"
|
||||
#include "dsffile.h"
|
||||
#include "dsdifffile.h"
|
||||
#include "tag.h"
|
||||
#include "id3v2framefactory.h"
|
||||
|
||||
using namespace TagLib;
|
||||
|
||||
static List<char *> strings;
|
||||
static bool unicodeStrings = true;
|
||||
static bool stringManagementEnabled = true;
|
||||
namespace
|
||||
{
|
||||
List<char *> strings;
|
||||
bool unicodeStrings = true;
|
||||
bool stringManagementEnabled = true;
|
||||
|
||||
char *stringToCharArray(const String &s)
|
||||
{
|
||||
const std::string str = s.to8Bit(unicodeStrings);
|
||||
|
||||
#ifdef HAVE_ISO_STRDUP
|
||||
|
||||
return ::_strdup(str.c_str());
|
||||
|
||||
#else
|
||||
|
||||
return ::strdup(str.c_str());
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
String charArrayToString(const char *s)
|
||||
{
|
||||
return String(s, unicodeStrings ? String::UTF8 : String::Latin1);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void taglib_set_strings_unicode(BOOL unicode)
|
||||
{
|
||||
@@ -60,69 +104,130 @@ void taglib_free(void* pointer)
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// TagLib::File wrapper
|
||||
// TagLib::IOStream wrapper
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
TagLib_IOStream *taglib_memory_iostream_new(const char *data, unsigned int size)
|
||||
{
|
||||
return reinterpret_cast<TagLib_IOStream *>(
|
||||
new ByteVectorStream(ByteVector(data, size)));
|
||||
}
|
||||
|
||||
void taglib_iostream_free(TagLib_IOStream *stream)
|
||||
{
|
||||
delete reinterpret_cast<IOStream *>(stream);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// TagLib::FileRef wrapper
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
TagLib_File *taglib_file_new(const char *filename)
|
||||
{
|
||||
return reinterpret_cast<TagLib_File *>(FileRef::create(filename));
|
||||
return reinterpret_cast<TagLib_File *>(new FileRef(filename));
|
||||
}
|
||||
|
||||
TagLib_File *taglib_file_new_type(const char *filename, TagLib_File_Type type)
|
||||
{
|
||||
File *file = NULL;
|
||||
switch(type) {
|
||||
case TagLib_File_MPEG:
|
||||
return reinterpret_cast<TagLib_File *>(new MPEG::File(filename));
|
||||
file = new MPEG::File(filename);
|
||||
break;
|
||||
case TagLib_File_OggVorbis:
|
||||
return reinterpret_cast<TagLib_File *>(new Ogg::Vorbis::File(filename));
|
||||
file = new Ogg::Vorbis::File(filename);
|
||||
break;
|
||||
case TagLib_File_FLAC:
|
||||
return reinterpret_cast<TagLib_File *>(new FLAC::File(filename));
|
||||
file = new FLAC::File(filename);
|
||||
break;
|
||||
case TagLib_File_MPC:
|
||||
return reinterpret_cast<TagLib_File *>(new MPC::File(filename));
|
||||
file = new MPC::File(filename);
|
||||
break;
|
||||
case TagLib_File_OggFlac:
|
||||
return reinterpret_cast<TagLib_File *>(new Ogg::FLAC::File(filename));
|
||||
file = new Ogg::FLAC::File(filename);
|
||||
break;
|
||||
case TagLib_File_WavPack:
|
||||
return reinterpret_cast<TagLib_File *>(new WavPack::File(filename));
|
||||
file = new WavPack::File(filename);
|
||||
break;
|
||||
case TagLib_File_Speex:
|
||||
return reinterpret_cast<TagLib_File *>(new Ogg::Speex::File(filename));
|
||||
file = new Ogg::Speex::File(filename);
|
||||
break;
|
||||
case TagLib_File_TrueAudio:
|
||||
return reinterpret_cast<TagLib_File *>(new TrueAudio::File(filename));
|
||||
file = new TrueAudio::File(filename);
|
||||
break;
|
||||
case TagLib_File_MP4:
|
||||
return reinterpret_cast<TagLib_File *>(new MP4::File(filename));
|
||||
file = new MP4::File(filename);
|
||||
break;
|
||||
case TagLib_File_ASF:
|
||||
return reinterpret_cast<TagLib_File *>(new ASF::File(filename));
|
||||
file = new ASF::File(filename);
|
||||
break;
|
||||
case TagLib_File_AIFF:
|
||||
file = new RIFF::AIFF::File(filename);
|
||||
break;
|
||||
case TagLib_File_WAV:
|
||||
file = new RIFF::WAV::File(filename);
|
||||
break;
|
||||
case TagLib_File_APE:
|
||||
file = new APE::File(filename);
|
||||
break;
|
||||
case TagLib_File_IT:
|
||||
file = new IT::File(filename);
|
||||
break;
|
||||
case TagLib_File_Mod:
|
||||
file = new Mod::File(filename);
|
||||
break;
|
||||
case TagLib_File_S3M:
|
||||
file = new S3M::File(filename);
|
||||
break;
|
||||
case TagLib_File_XM:
|
||||
file = new XM::File(filename);
|
||||
break;
|
||||
case TagLib_File_Opus:
|
||||
file = new Ogg::Opus::File(filename);
|
||||
break;
|
||||
case TagLib_File_DSF:
|
||||
file = new DSF::File(filename);
|
||||
break;
|
||||
case TagLib_File_DSDIFF:
|
||||
file = new DSDIFF::File(filename);
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
break;
|
||||
}
|
||||
return file ? reinterpret_cast<TagLib_File *>(new FileRef(file)) : NULL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
TagLib_File *taglib_file_new_iostream(TagLib_IOStream *stream)
|
||||
{
|
||||
return reinterpret_cast<TagLib_File *>(
|
||||
new FileRef(reinterpret_cast<IOStream *>(stream)));
|
||||
}
|
||||
|
||||
void taglib_file_free(TagLib_File *file)
|
||||
{
|
||||
delete reinterpret_cast<File *>(file);
|
||||
delete reinterpret_cast<FileRef *>(file);
|
||||
}
|
||||
|
||||
BOOL taglib_file_is_valid(const TagLib_File *file)
|
||||
{
|
||||
return reinterpret_cast<const File *>(file)->isValid();
|
||||
return !reinterpret_cast<const FileRef *>(file)->isNull();
|
||||
}
|
||||
|
||||
TagLib_Tag *taglib_file_tag(const TagLib_File *file)
|
||||
{
|
||||
const File *f = reinterpret_cast<const File *>(file);
|
||||
auto f = reinterpret_cast<const FileRef *>(file);
|
||||
return reinterpret_cast<TagLib_Tag *>(f->tag());
|
||||
}
|
||||
|
||||
const TagLib_AudioProperties *taglib_file_audioproperties(const TagLib_File *file)
|
||||
{
|
||||
const File *f = reinterpret_cast<const File *>(file);
|
||||
auto f = reinterpret_cast<const FileRef *>(file);
|
||||
return reinterpret_cast<const TagLib_AudioProperties *>(f->audioProperties());
|
||||
}
|
||||
|
||||
BOOL taglib_file_save(TagLib_File *file)
|
||||
{
|
||||
return reinterpret_cast<File *>(file)->save();
|
||||
return reinterpret_cast<FileRef *>(file)->save();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -132,7 +237,7 @@ BOOL taglib_file_save(TagLib_File *file)
|
||||
char *taglib_tag_title(const TagLib_Tag *tag)
|
||||
{
|
||||
const Tag *t = reinterpret_cast<const Tag *>(tag);
|
||||
char *s = ::strdup(t->title().toCString(unicodeStrings));
|
||||
char *s = stringToCharArray(t->title());
|
||||
if(stringManagementEnabled)
|
||||
strings.append(s);
|
||||
return s;
|
||||
@@ -141,7 +246,7 @@ char *taglib_tag_title(const TagLib_Tag *tag)
|
||||
char *taglib_tag_artist(const TagLib_Tag *tag)
|
||||
{
|
||||
const Tag *t = reinterpret_cast<const Tag *>(tag);
|
||||
char *s = ::strdup(t->artist().toCString(unicodeStrings));
|
||||
char *s = stringToCharArray(t->artist());
|
||||
if(stringManagementEnabled)
|
||||
strings.append(s);
|
||||
return s;
|
||||
@@ -150,7 +255,7 @@ char *taglib_tag_artist(const TagLib_Tag *tag)
|
||||
char *taglib_tag_album(const TagLib_Tag *tag)
|
||||
{
|
||||
const Tag *t = reinterpret_cast<const Tag *>(tag);
|
||||
char *s = ::strdup(t->album().toCString(unicodeStrings));
|
||||
char *s = stringToCharArray(t->album());
|
||||
if(stringManagementEnabled)
|
||||
strings.append(s);
|
||||
return s;
|
||||
@@ -159,7 +264,7 @@ char *taglib_tag_album(const TagLib_Tag *tag)
|
||||
char *taglib_tag_comment(const TagLib_Tag *tag)
|
||||
{
|
||||
const Tag *t = reinterpret_cast<const Tag *>(tag);
|
||||
char *s = ::strdup(t->comment().toCString(unicodeStrings));
|
||||
char *s = stringToCharArray(t->comment());
|
||||
if(stringManagementEnabled)
|
||||
strings.append(s);
|
||||
return s;
|
||||
@@ -168,7 +273,7 @@ char *taglib_tag_comment(const TagLib_Tag *tag)
|
||||
char *taglib_tag_genre(const TagLib_Tag *tag)
|
||||
{
|
||||
const Tag *t = reinterpret_cast<const Tag *>(tag);
|
||||
char *s = ::strdup(t->genre().toCString(unicodeStrings));
|
||||
char *s = stringToCharArray(t->genre());
|
||||
if(stringManagementEnabled)
|
||||
strings.append(s);
|
||||
return s;
|
||||
@@ -189,31 +294,31 @@ unsigned int taglib_tag_track(const TagLib_Tag *tag)
|
||||
void taglib_tag_set_title(TagLib_Tag *tag, const char *title)
|
||||
{
|
||||
Tag *t = reinterpret_cast<Tag *>(tag);
|
||||
t->setTitle(String(title, unicodeStrings ? String::UTF8 : String::Latin1));
|
||||
t->setTitle(charArrayToString(title));
|
||||
}
|
||||
|
||||
void taglib_tag_set_artist(TagLib_Tag *tag, const char *artist)
|
||||
{
|
||||
Tag *t = reinterpret_cast<Tag *>(tag);
|
||||
t->setArtist(String(artist, unicodeStrings ? String::UTF8 : String::Latin1));
|
||||
t->setArtist(charArrayToString(artist));
|
||||
}
|
||||
|
||||
void taglib_tag_set_album(TagLib_Tag *tag, const char *album)
|
||||
{
|
||||
Tag *t = reinterpret_cast<Tag *>(tag);
|
||||
t->setAlbum(String(album, unicodeStrings ? String::UTF8 : String::Latin1));
|
||||
t->setAlbum(charArrayToString(album));
|
||||
}
|
||||
|
||||
void taglib_tag_set_comment(TagLib_Tag *tag, const char *comment)
|
||||
{
|
||||
Tag *t = reinterpret_cast<Tag *>(tag);
|
||||
t->setComment(String(comment, unicodeStrings ? String::UTF8 : String::Latin1));
|
||||
t->setComment(charArrayToString(comment));
|
||||
}
|
||||
|
||||
void taglib_tag_set_genre(TagLib_Tag *tag, const char *genre)
|
||||
{
|
||||
Tag *t = reinterpret_cast<Tag *>(tag);
|
||||
t->setGenre(String(genre, unicodeStrings ? String::UTF8 : String::Latin1));
|
||||
t->setGenre(charArrayToString(genre));
|
||||
}
|
||||
|
||||
void taglib_tag_set_year(TagLib_Tag *tag, unsigned int year)
|
||||
@@ -233,8 +338,8 @@ void taglib_tag_free_strings()
|
||||
if(!stringManagementEnabled)
|
||||
return;
|
||||
|
||||
for(List<char *>::ConstIterator it = strings.begin(); it != strings.end(); ++it)
|
||||
free(*it);
|
||||
for(auto &string : std::as_const(strings))
|
||||
free(string);
|
||||
strings.clear();
|
||||
}
|
||||
|
||||
@@ -244,25 +349,25 @@ void taglib_tag_free_strings()
|
||||
|
||||
int taglib_audioproperties_length(const TagLib_AudioProperties *audioProperties)
|
||||
{
|
||||
const AudioProperties *p = reinterpret_cast<const AudioProperties *>(audioProperties);
|
||||
return p->length();
|
||||
auto p = reinterpret_cast<const AudioProperties *>(audioProperties);
|
||||
return p->lengthInSeconds();
|
||||
}
|
||||
|
||||
int taglib_audioproperties_bitrate(const TagLib_AudioProperties *audioProperties)
|
||||
{
|
||||
const AudioProperties *p = reinterpret_cast<const AudioProperties *>(audioProperties);
|
||||
auto p = reinterpret_cast<const AudioProperties *>(audioProperties);
|
||||
return p->bitrate();
|
||||
}
|
||||
|
||||
int taglib_audioproperties_samplerate(const TagLib_AudioProperties *audioProperties)
|
||||
{
|
||||
const AudioProperties *p = reinterpret_cast<const AudioProperties *>(audioProperties);
|
||||
auto p = reinterpret_cast<const AudioProperties *>(audioProperties);
|
||||
return p->sampleRate();
|
||||
}
|
||||
|
||||
int taglib_audioproperties_channels(const TagLib_AudioProperties *audioProperties)
|
||||
{
|
||||
const AudioProperties *p = reinterpret_cast<const AudioProperties *>(audioProperties);
|
||||
auto p = reinterpret_cast<const AudioProperties *>(audioProperties);
|
||||
return p->channels();
|
||||
}
|
||||
|
||||
@@ -288,3 +393,423 @@ void taglib_id3v2_set_default_text_encoding(TagLib_ID3v2_Encoding encoding)
|
||||
|
||||
ID3v2::FrameFactory::instance()->setDefaultTextEncoding(type);
|
||||
}
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
* Properties API
|
||||
******************************************************************************/
|
||||
namespace {
|
||||
|
||||
void _taglib_property_set(TagLib_File *file, const char* prop, const char* value, bool append)
|
||||
{
|
||||
if(file == NULL || prop == NULL)
|
||||
return;
|
||||
|
||||
auto tfile = reinterpret_cast<FileRef *>(file);
|
||||
PropertyMap map = tfile->tag()->properties();
|
||||
|
||||
if(value) {
|
||||
auto property = map.find(prop);
|
||||
if(property == map.end()) {
|
||||
map.insert(prop, StringList(value));
|
||||
}
|
||||
else {
|
||||
if(append) {
|
||||
property->second.append(value);
|
||||
}
|
||||
else {
|
||||
property->second = StringList(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
map.erase(prop);
|
||||
}
|
||||
|
||||
tfile->setProperties(map);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void taglib_property_set(TagLib_File *f, const char *prop, const char *value)
|
||||
{
|
||||
_taglib_property_set(f, prop, value, false);
|
||||
}
|
||||
|
||||
void taglib_property_set_append(TagLib_File *f, const char *prop, const char *value)
|
||||
{
|
||||
_taglib_property_set(f, prop, value, true);
|
||||
}
|
||||
|
||||
char** taglib_property_keys(TagLib_File *file)
|
||||
{
|
||||
if(file == NULL)
|
||||
return NULL;
|
||||
|
||||
const PropertyMap map = reinterpret_cast<const FileRef *>(file)->properties();
|
||||
if(map.isEmpty())
|
||||
return NULL;
|
||||
|
||||
auto props = static_cast<char **>(malloc(sizeof(char *) * (map.size() + 1)));
|
||||
char **pp = props;
|
||||
|
||||
for(const auto &i : map) {
|
||||
*pp++ = stringToCharArray(i.first);
|
||||
}
|
||||
*pp = NULL;
|
||||
|
||||
return props;
|
||||
}
|
||||
|
||||
char **taglib_property_get(TagLib_File *file, const char *prop)
|
||||
{
|
||||
if(file == NULL || prop == NULL)
|
||||
return NULL;
|
||||
|
||||
const PropertyMap map = reinterpret_cast<const FileRef *>(file)->properties();
|
||||
|
||||
auto property = map.find(prop);
|
||||
if(property == map.end())
|
||||
return NULL;
|
||||
|
||||
auto props = static_cast<char **>(malloc(sizeof(char *) * (property->second.size() + 1)));
|
||||
char **pp = props;
|
||||
|
||||
for(const auto &i : property->second) {
|
||||
*pp++ = stringToCharArray(i);
|
||||
}
|
||||
*pp = NULL;
|
||||
|
||||
return props;
|
||||
}
|
||||
|
||||
void taglib_property_free(char **props)
|
||||
{
|
||||
if(props == NULL)
|
||||
return;
|
||||
|
||||
char **p = props;
|
||||
while(*p) {
|
||||
free(*p++);
|
||||
}
|
||||
free(props);
|
||||
}
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
* Complex Properties API
|
||||
******************************************************************************/
|
||||
|
||||
namespace {
|
||||
|
||||
bool _taglib_complex_property_set(
|
||||
TagLib_File *file, const char *key,
|
||||
const TagLib_Complex_Property_Attribute **value, bool append)
|
||||
{
|
||||
if(file == NULL || key == NULL)
|
||||
return false;
|
||||
|
||||
auto tfile = reinterpret_cast<FileRef *>(file);
|
||||
|
||||
if(value == NULL) {
|
||||
return tfile->setComplexProperties(key, {});
|
||||
}
|
||||
|
||||
VariantMap map;
|
||||
const TagLib_Complex_Property_Attribute** attrPtr = value;
|
||||
while(*attrPtr) {
|
||||
const TagLib_Complex_Property_Attribute *attr = *attrPtr;
|
||||
String attrKey(attr->key);
|
||||
TagLib_Variant_Type type = attr->value.type;
|
||||
switch(type) {
|
||||
case TagLib_Variant_Void:
|
||||
map.insert(attrKey, Variant());
|
||||
break;
|
||||
case TagLib_Variant_Bool:
|
||||
map.insert(attrKey, attr->value.value.boolValue != 0);
|
||||
break;
|
||||
case TagLib_Variant_Int:
|
||||
map.insert(attrKey, attr->value.value.intValue);
|
||||
break;
|
||||
case TagLib_Variant_UInt:
|
||||
map.insert(attrKey, attr->value.value.uIntValue);
|
||||
break;
|
||||
case TagLib_Variant_LongLong:
|
||||
map.insert(attrKey, attr->value.value.longLongValue);
|
||||
break;
|
||||
case TagLib_Variant_ULongLong:
|
||||
map.insert(attrKey, attr->value.value.uLongLongValue);
|
||||
break;
|
||||
case TagLib_Variant_Double:
|
||||
map.insert(attrKey, attr->value.value.doubleValue);
|
||||
break;
|
||||
case TagLib_Variant_String:
|
||||
map.insert(attrKey, attr->value.value.stringValue);
|
||||
break;
|
||||
case TagLib_Variant_StringList: {
|
||||
StringList strs;
|
||||
if(attr->value.value.stringListValue) {
|
||||
char **s = attr->value.value.stringListValue;;
|
||||
while(*s) {
|
||||
strs.append(*s++);
|
||||
}
|
||||
}
|
||||
map.insert(attrKey, strs);
|
||||
break;
|
||||
}
|
||||
case TagLib_Variant_ByteVector:
|
||||
map.insert(attrKey, ByteVector(attr->value.value.byteVectorValue,
|
||||
attr->value.size));
|
||||
break;
|
||||
}
|
||||
++attrPtr;
|
||||
}
|
||||
|
||||
return append ? tfile->setComplexProperties(key, tfile->complexProperties(key).append(map))
|
||||
: tfile->setComplexProperties(key, {map});
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
BOOL taglib_complex_property_set(
|
||||
TagLib_File *file, const char *key,
|
||||
const TagLib_Complex_Property_Attribute **value)
|
||||
{
|
||||
return _taglib_complex_property_set(file, key, value, false);
|
||||
}
|
||||
|
||||
BOOL taglib_complex_property_set_append(
|
||||
TagLib_File *file, const char *key,
|
||||
const TagLib_Complex_Property_Attribute **value)
|
||||
{
|
||||
return _taglib_complex_property_set(file, key, value, true);
|
||||
}
|
||||
|
||||
char** taglib_complex_property_keys(TagLib_File *file)
|
||||
{
|
||||
if(file == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const StringList strs = reinterpret_cast<const FileRef *>(file)->complexPropertyKeys();
|
||||
if(strs.isEmpty()) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
auto keys = static_cast<char **>(malloc(sizeof(char *) * (strs.size() + 1)));
|
||||
char **keyPtr = keys;
|
||||
|
||||
for(const auto &str : strs) {
|
||||
*keyPtr++ = stringToCharArray(str);
|
||||
}
|
||||
*keyPtr = NULL;
|
||||
|
||||
return keys;
|
||||
}
|
||||
|
||||
TagLib_Complex_Property_Attribute*** taglib_complex_property_get(
|
||||
TagLib_File *file, const char *key)
|
||||
{
|
||||
if(file == NULL || key == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const auto variantMaps = reinterpret_cast<const FileRef *>(file)->complexProperties(key);
|
||||
if(variantMaps.isEmpty()) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
TagLib_Complex_Property_Attribute ***props = static_cast<TagLib_Complex_Property_Attribute ***>(
|
||||
malloc(sizeof(TagLib_Complex_Property_Attribute **) * (variantMaps.size() + 1)));
|
||||
TagLib_Complex_Property_Attribute ***propPtr = props;
|
||||
|
||||
for(const auto &variantMap : variantMaps) {
|
||||
if(!variantMap.isEmpty()) {
|
||||
TagLib_Complex_Property_Attribute **attrs = static_cast<TagLib_Complex_Property_Attribute **>(
|
||||
malloc(sizeof(TagLib_Complex_Property_Attribute *) * (variantMap.size() + 1)));
|
||||
TagLib_Complex_Property_Attribute *attr = static_cast<TagLib_Complex_Property_Attribute *>(
|
||||
malloc(sizeof(TagLib_Complex_Property_Attribute) * variantMap.size()));
|
||||
TagLib_Complex_Property_Attribute **attrPtr = attrs;
|
||||
// The next assignment is redundant to silence the clang analyzer,
|
||||
// it is done at the end of the loop, which must be entered because
|
||||
// variantMap is not empty.
|
||||
*attrPtr = attr;
|
||||
for(const auto &[k, v] : variantMap) {
|
||||
attr->key = stringToCharArray(k);
|
||||
attr->value.size = 0;
|
||||
switch(v.type()) {
|
||||
case Variant::Void:
|
||||
attr->value.type = TagLib_Variant_Void;
|
||||
attr->value.value.stringValue = NULL;
|
||||
break;
|
||||
case Variant::Bool:
|
||||
attr->value.type = TagLib_Variant_Bool;
|
||||
attr->value.value.boolValue = v.value<bool>();
|
||||
break;
|
||||
case Variant::Int:
|
||||
attr->value.type = TagLib_Variant_Int;
|
||||
attr->value.value.intValue = v.value<int>();
|
||||
break;
|
||||
case Variant::UInt:
|
||||
attr->value.type = TagLib_Variant_UInt;
|
||||
attr->value.value.uIntValue = v.value<unsigned int>();
|
||||
break;
|
||||
case Variant::LongLong:
|
||||
attr->value.type = TagLib_Variant_LongLong;
|
||||
attr->value.value.longLongValue = v.value<long long>();
|
||||
break;
|
||||
case Variant::ULongLong:
|
||||
attr->value.type = TagLib_Variant_ULongLong;
|
||||
attr->value.value.uLongLongValue = v.value<unsigned long long>();
|
||||
break;
|
||||
case Variant::Double:
|
||||
attr->value.type = TagLib_Variant_Double;
|
||||
attr->value.value.doubleValue = v.value<double>();
|
||||
break;
|
||||
case Variant::String: {
|
||||
attr->value.type = TagLib_Variant_String;
|
||||
auto str = v.value<String>();
|
||||
attr->value.value.stringValue = stringToCharArray(str);
|
||||
attr->value.size = str.size();
|
||||
break;
|
||||
}
|
||||
case Variant::StringList: {
|
||||
attr->value.type = TagLib_Variant_StringList;
|
||||
StringList strs = v.value<StringList>();
|
||||
auto strPtr = static_cast<char **>(malloc(sizeof(char *) * (strs.size() + 1)));
|
||||
attr->value.value.stringListValue = strPtr;
|
||||
attr->value.size = strs.size();
|
||||
for(const auto &str : strs) {
|
||||
*strPtr++ = stringToCharArray(str);
|
||||
}
|
||||
*strPtr = NULL;
|
||||
break;
|
||||
}
|
||||
case Variant::ByteVector: {
|
||||
attr->value.type = TagLib_Variant_ByteVector;
|
||||
const ByteVector data = v.value<ByteVector>();
|
||||
auto bytePtr = static_cast<char *>(malloc(data.size()));
|
||||
attr->value.value.byteVectorValue = bytePtr;
|
||||
attr->value.size = data.size();
|
||||
::memcpy(bytePtr, data.data(), data.size());
|
||||
break;
|
||||
}
|
||||
case Variant::ByteVectorList:
|
||||
case Variant::VariantList:
|
||||
case Variant::VariantMap: {
|
||||
attr->value.type = TagLib_Variant_String;
|
||||
std::stringstream ss;
|
||||
ss << v;
|
||||
attr->value.value.stringValue = stringToCharArray(ss.str());
|
||||
break;
|
||||
}
|
||||
}
|
||||
*attrPtr++ = attr++;
|
||||
}
|
||||
*attrPtr = NULL;
|
||||
*propPtr++ = attrs;
|
||||
}
|
||||
}
|
||||
*propPtr = NULL;
|
||||
return props;
|
||||
}
|
||||
|
||||
void taglib_picture_from_complex_property(
|
||||
TagLib_Complex_Property_Attribute*** properties,
|
||||
TagLib_Complex_Property_Picture_Data *picture)
|
||||
{
|
||||
if(!properties || !picture) {
|
||||
return;
|
||||
}
|
||||
std::memset(picture, 0, sizeof(*picture));
|
||||
TagLib_Complex_Property_Attribute*** propPtr = properties;
|
||||
while(!picture->data && *propPtr) {
|
||||
TagLib_Complex_Property_Attribute** attrPtr = *propPtr;
|
||||
while(*attrPtr) {
|
||||
TagLib_Complex_Property_Attribute *attr = *attrPtr;
|
||||
TagLib_Variant_Type type = attr->value.type;
|
||||
switch(type) {
|
||||
case TagLib_Variant_String:
|
||||
if(strcmp("mimeType", attr->key) == 0) {
|
||||
picture->mimeType = attr->value.value.stringValue;
|
||||
}
|
||||
else if(strcmp("description", attr->key) == 0) {
|
||||
picture->description = attr->value.value.stringValue;
|
||||
}
|
||||
else if(strcmp("pictureType", attr->key) == 0) {
|
||||
picture->pictureType = attr->value.value.stringValue;
|
||||
}
|
||||
break;
|
||||
case TagLib_Variant_ByteVector:
|
||||
if(strcmp("data", attr->key) == 0) {
|
||||
picture->data = attr->value.value.byteVectorValue;
|
||||
picture->size = attr->value.size;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
++attrPtr;
|
||||
}
|
||||
++propPtr;
|
||||
}
|
||||
}
|
||||
|
||||
void taglib_complex_property_free_keys(char **keys)
|
||||
{
|
||||
if(keys == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
char **k = keys;
|
||||
while(*k) {
|
||||
free(*k++);
|
||||
}
|
||||
free(keys);
|
||||
}
|
||||
|
||||
void taglib_complex_property_free(
|
||||
TagLib_Complex_Property_Attribute ***props)
|
||||
{
|
||||
if(props == NULL) {
|
||||
return;
|
||||
}
|
||||
TagLib_Complex_Property_Attribute*** propPtr = props;
|
||||
while(*propPtr) {
|
||||
TagLib_Complex_Property_Attribute** attrPtr = *propPtr;
|
||||
while(*attrPtr) {
|
||||
TagLib_Complex_Property_Attribute *attr = *attrPtr;
|
||||
TagLib_Variant_Type type = attr->value.type;
|
||||
switch(type) {
|
||||
case TagLib_Variant_String:
|
||||
free(attr->value.value.stringValue);
|
||||
break;
|
||||
case TagLib_Variant_StringList:
|
||||
if(attr->value.value.stringListValue) {
|
||||
char **s = attr->value.value.stringListValue;
|
||||
while(*s) {
|
||||
free(*s++);
|
||||
}
|
||||
free(attr->value.value.stringListValue);
|
||||
}
|
||||
break;
|
||||
case TagLib_Variant_ByteVector:
|
||||
free(attr->value.value.byteVectorValue);
|
||||
break;
|
||||
case TagLib_Variant_Void:
|
||||
case TagLib_Variant_Bool:
|
||||
case TagLib_Variant_Int:
|
||||
case TagLib_Variant_UInt:
|
||||
case TagLib_Variant_LongLong:
|
||||
case TagLib_Variant_ULongLong:
|
||||
case TagLib_Variant_Double:
|
||||
break;
|
||||
}
|
||||
free(attr->key);
|
||||
++attrPtr;
|
||||
}
|
||||
free(**propPtr);
|
||||
free(*propPtr++);
|
||||
}
|
||||
free(props);
|
||||
}
|
||||
|
||||
@@ -43,7 +43,10 @@ extern "C" {
|
||||
#define TAGLIB_C_EXPORT
|
||||
#endif
|
||||
|
||||
#ifndef BOOL
|
||||
#ifdef _MSC_VER
|
||||
/* minwindef.h contains typedef int BOOL */
|
||||
#include <windows.h>
|
||||
#elif !defined BOOL
|
||||
#define BOOL int
|
||||
#endif
|
||||
|
||||
@@ -57,14 +60,15 @@ extern "C" {
|
||||
*******************************************************************************/
|
||||
|
||||
/*
|
||||
* These are used for type provide some type safety to the C API (as opposed to
|
||||
* using void *, but pointers to them are simply cast to the corresponding C++
|
||||
* These are used to give the C API some type safety (as opposed to
|
||||
* using void * ), but pointers to them are simply cast to the corresponding C++
|
||||
* types in the implementation.
|
||||
*/
|
||||
|
||||
typedef struct { int dummy; } TagLib_File;
|
||||
typedef struct { int dummy; } TagLib_Tag;
|
||||
typedef struct { int dummy; } TagLib_AudioProperties;
|
||||
typedef struct { int dummy; } TagLib_IOStream;
|
||||
|
||||
/*!
|
||||
* By default all strings coming into or out of TagLib's C API are in UTF8.
|
||||
@@ -86,6 +90,22 @@ TAGLIB_C_EXPORT void taglib_set_string_management_enabled(BOOL management);
|
||||
*/
|
||||
TAGLIB_C_EXPORT void taglib_free(void* pointer);
|
||||
|
||||
/*******************************************************************************
|
||||
* Stream API
|
||||
******************************************************************************/
|
||||
|
||||
/*!
|
||||
* Creates a byte vector stream from \a size bytes of \a data.
|
||||
* Such a stream can be used with taglib_file_new_iostream() to create a file
|
||||
* from memory.
|
||||
*/
|
||||
TAGLIB_C_EXPORT TagLib_IOStream *taglib_memory_iostream_new(const char *data, unsigned int size);
|
||||
|
||||
/*!
|
||||
* Frees and closes the stream.
|
||||
*/
|
||||
TAGLIB_C_EXPORT void taglib_iostream_free(TagLib_IOStream *stream);
|
||||
|
||||
/*******************************************************************************
|
||||
* File API
|
||||
******************************************************************************/
|
||||
@@ -100,7 +120,17 @@ typedef enum {
|
||||
TagLib_File_Speex,
|
||||
TagLib_File_TrueAudio,
|
||||
TagLib_File_MP4,
|
||||
TagLib_File_ASF
|
||||
TagLib_File_ASF,
|
||||
TagLib_File_AIFF,
|
||||
TagLib_File_WAV,
|
||||
TagLib_File_APE,
|
||||
TagLib_File_IT,
|
||||
TagLib_File_Mod,
|
||||
TagLib_File_S3M,
|
||||
TagLib_File_XM,
|
||||
TagLib_File_Opus,
|
||||
TagLib_File_DSF,
|
||||
TagLib_File_DSDIFF
|
||||
} TagLib_File_Type;
|
||||
|
||||
/*!
|
||||
@@ -118,13 +148,23 @@ TAGLIB_C_EXPORT TagLib_File *taglib_file_new(const char *filename);
|
||||
*/
|
||||
TAGLIB_C_EXPORT TagLib_File *taglib_file_new_type(const char *filename, TagLib_File_Type type);
|
||||
|
||||
/*!
|
||||
* Creates a TagLib file from a \a stream.
|
||||
* A byte vector stream can be used to read a file from memory and write to
|
||||
* memory, e.g. when fetching network data.
|
||||
* The stream has to be created using taglib_memory_iostream_new() and shall be
|
||||
* freed using taglib_iostream_free() \e after this file is freed with
|
||||
* taglib_file_free().
|
||||
*/
|
||||
TAGLIB_C_EXPORT TagLib_File *taglib_file_new_iostream(TagLib_IOStream *stream);
|
||||
|
||||
/*!
|
||||
* Frees and closes the file.
|
||||
*/
|
||||
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 +177,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);
|
||||
@@ -292,6 +332,275 @@ typedef enum {
|
||||
|
||||
TAGLIB_C_EXPORT void taglib_id3v2_set_default_text_encoding(TagLib_ID3v2_Encoding encoding);
|
||||
|
||||
/******************************************************************************
|
||||
* Properties API
|
||||
******************************************************************************/
|
||||
|
||||
/*!
|
||||
* Sets the property \a prop with \a value. Use \a value = NULL to remove
|
||||
* the property, otherwise it will be replaced.
|
||||
*/
|
||||
TAGLIB_C_EXPORT void taglib_property_set(TagLib_File *file, const char *prop, const char *value);
|
||||
|
||||
/*!
|
||||
* Appends \a value to the property \a prop (sets it if non-existing).
|
||||
* Use \a value = NULL to remove all values associated with the property.
|
||||
*/
|
||||
TAGLIB_C_EXPORT void taglib_property_set_append(TagLib_File *file, const char *prop, const char *value);
|
||||
|
||||
/*!
|
||||
* Get the keys of the property map.
|
||||
*
|
||||
* \return NULL terminated array of C-strings (char *), only NULL if empty.
|
||||
* It must be freed by the client using taglib_property_free().
|
||||
*/
|
||||
TAGLIB_C_EXPORT char** taglib_property_keys(TagLib_File *file);
|
||||
|
||||
/*!
|
||||
* Get value(s) of property \a prop.
|
||||
*
|
||||
* \return NULL terminated array of C-strings (char *), only NULL if empty.
|
||||
* It must be freed by the client using taglib_property_free().
|
||||
*/
|
||||
TAGLIB_C_EXPORT char** taglib_property_get(TagLib_File *file, const char *prop);
|
||||
|
||||
/*!
|
||||
* Frees the NULL terminated array \a props and the C-strings it contains.
|
||||
*/
|
||||
TAGLIB_C_EXPORT void taglib_property_free(char **props);
|
||||
|
||||
/******************************************************************************
|
||||
* Complex Properties API
|
||||
******************************************************************************/
|
||||
|
||||
/*!
|
||||
* Types which can be stored in a TagLib_Variant.
|
||||
*
|
||||
* \related TagLib::Variant::Type
|
||||
* These correspond to TagLib::Variant::Type, but ByteVectorList, VariantList,
|
||||
* VariantMap are not supported and will be returned as their string
|
||||
* representation.
|
||||
*/
|
||||
typedef enum {
|
||||
TagLib_Variant_Void,
|
||||
TagLib_Variant_Bool,
|
||||
TagLib_Variant_Int,
|
||||
TagLib_Variant_UInt,
|
||||
TagLib_Variant_LongLong,
|
||||
TagLib_Variant_ULongLong,
|
||||
TagLib_Variant_Double,
|
||||
TagLib_Variant_String,
|
||||
TagLib_Variant_StringList,
|
||||
TagLib_Variant_ByteVector
|
||||
} TagLib_Variant_Type;
|
||||
|
||||
/*!
|
||||
* Discriminated union used in complex property attributes.
|
||||
*
|
||||
* \e type must be set according to the \e value union used.
|
||||
* \e size is only required for TagLib_Variant_ByteVector and must contain
|
||||
* the number of bytes.
|
||||
*
|
||||
* \related TagLib::Variant.
|
||||
*/
|
||||
typedef struct {
|
||||
TagLib_Variant_Type type;
|
||||
unsigned int size;
|
||||
union {
|
||||
char *stringValue;
|
||||
char *byteVectorValue;
|
||||
char **stringListValue;
|
||||
BOOL boolValue;
|
||||
int intValue;
|
||||
unsigned int uIntValue;
|
||||
long long longLongValue;
|
||||
unsigned long long uLongLongValue;
|
||||
double doubleValue;
|
||||
} value;
|
||||
} TagLib_Variant;
|
||||
|
||||
/*!
|
||||
* Attribute of a complex property.
|
||||
* Complex properties consist of a NULL-terminated array of pointers to
|
||||
* this structure with \e key and \e value.
|
||||
*/
|
||||
typedef struct {
|
||||
char *key;
|
||||
TagLib_Variant value;
|
||||
} TagLib_Complex_Property_Attribute;
|
||||
|
||||
/*!
|
||||
* Picture data extracted from a complex property by the convenience function
|
||||
* taglib_picture_from_complex_property().
|
||||
*/
|
||||
typedef struct {
|
||||
char *mimeType;
|
||||
char *description;
|
||||
char *pictureType;
|
||||
char *data;
|
||||
unsigned int size;
|
||||
} TagLib_Complex_Property_Picture_Data;
|
||||
|
||||
/*!
|
||||
* Declare complex property attributes to set a picture.
|
||||
* Can be used to define a variable \a var which can be used with
|
||||
* taglib_complex_property_set() and a "PICTURE" key to set an
|
||||
* embedded picture with the picture data \a dat of size \a siz
|
||||
* and description \a desc, mime type \a mime and picture type
|
||||
* \a typ (size is unsigned int, the other input parameters char *).
|
||||
*/
|
||||
#define TAGLIB_COMPLEX_PROPERTY_PICTURE(var, dat, siz, desc, mime, typ) \
|
||||
const TagLib_Complex_Property_Attribute \
|
||||
var##Attrs[] = { \
|
||||
{ \
|
||||
(char *)"data", \
|
||||
{ \
|
||||
TagLib_Variant_ByteVector, \
|
||||
(siz), \
|
||||
{ \
|
||||
(char *)(dat) \
|
||||
} \
|
||||
} \
|
||||
}, \
|
||||
{ \
|
||||
(char *)"mimeType", \
|
||||
{ \
|
||||
TagLib_Variant_String, \
|
||||
0U, \
|
||||
{ \
|
||||
(char *)(mime) \
|
||||
} \
|
||||
} \
|
||||
}, \
|
||||
{ \
|
||||
(char *)"description", \
|
||||
{ \
|
||||
TagLib_Variant_String, \
|
||||
0U, \
|
||||
{ \
|
||||
(char *)(desc) \
|
||||
} \
|
||||
} \
|
||||
}, \
|
||||
{ \
|
||||
(char *)"pictureType", \
|
||||
{ \
|
||||
TagLib_Variant_String, \
|
||||
0U, \
|
||||
{ \
|
||||
(char *)(typ) \
|
||||
} \
|
||||
} \
|
||||
} \
|
||||
}; \
|
||||
const TagLib_Complex_Property_Attribute *var[] = { \
|
||||
&var##Attrs[0], &var##Attrs[1], &var##Attrs[2], \
|
||||
&var##Attrs[3], NULL \
|
||||
}
|
||||
|
||||
/*!
|
||||
* Sets the complex property \a key with \a value. Use \a value = NULL to
|
||||
* remove the property, otherwise it will be replaced with the NULL
|
||||
* terminated array of attributes in \a value.
|
||||
*
|
||||
* A picture can be set with the TAGLIB_COMPLEX_PROPERTY_PICTURE macro:
|
||||
*
|
||||
* \code {.c}
|
||||
* TagLib_File *file = taglib_file_new("myfile.mp3");
|
||||
* FILE *fh = fopen("mypicture.jpg", "rb");
|
||||
* if(fh) {
|
||||
* fseek(fh, 0L, SEEK_END);
|
||||
* long size = ftell(fh);
|
||||
* fseek(fh, 0L, SEEK_SET);
|
||||
* char *data = (char *)malloc(size);
|
||||
* fread(data, size, 1, fh);
|
||||
* TAGLIB_COMPLEX_PROPERTY_PICTURE(props, data, size, "Written by TagLib",
|
||||
* "image/jpeg", "Front Cover");
|
||||
* taglib_complex_property_set(file, "PICTURE", props);
|
||||
* taglib_file_save(file);
|
||||
* free(data);
|
||||
* fclose(fh);
|
||||
* }
|
||||
* \endcode
|
||||
*/
|
||||
TAGLIB_C_EXPORT BOOL taglib_complex_property_set(
|
||||
TagLib_File *file, const char *key,
|
||||
const TagLib_Complex_Property_Attribute **value);
|
||||
|
||||
/*!
|
||||
* Appends \a value to the complex property \a key (sets it if non-existing).
|
||||
* Use \a value = NULL to remove all values associated with the \a key.
|
||||
*/
|
||||
TAGLIB_C_EXPORT BOOL taglib_complex_property_set_append(
|
||||
TagLib_File *file, const char *key,
|
||||
const TagLib_Complex_Property_Attribute **value);
|
||||
|
||||
/*!
|
||||
* Get the keys of the complex properties.
|
||||
*
|
||||
* \return NULL terminated array of C-strings (char *), only NULL if empty.
|
||||
* It must be freed by the client using taglib_complex_property_free_keys().
|
||||
*/
|
||||
TAGLIB_C_EXPORT char** taglib_complex_property_keys(TagLib_File *file);
|
||||
|
||||
/*!
|
||||
* Get value(s) of complex property \a key.
|
||||
*
|
||||
* \return NULL terminated array of property values, which are themselves an
|
||||
* array of property attributes, only NULL if empty.
|
||||
* It must be freed by the client using taglib_complex_property_free().
|
||||
*/
|
||||
TAGLIB_C_EXPORT TagLib_Complex_Property_Attribute*** taglib_complex_property_get(
|
||||
TagLib_File *file, const char *key);
|
||||
|
||||
/*!
|
||||
* Extract the complex property values of a picture.
|
||||
*
|
||||
* This function can be used to get the data from a "PICTURE" complex property
|
||||
* without having to traverse the whole variant map. A picture can be
|
||||
* retrieved like this:
|
||||
*
|
||||
* \code {.c}
|
||||
* TagLib_File *file = taglib_file_new("myfile.mp3");
|
||||
* TagLib_Complex_Property_Attribute*** properties =
|
||||
* taglib_complex_property_get(file, "PICTURE");
|
||||
* TagLib_Complex_Property_Picture_Data picture;
|
||||
* taglib_picture_from_complex_property(properties, &picture);
|
||||
* // Do something with picture.mimeType, picture.description,
|
||||
* // picture.pictureType, picture.data, picture.size, e.g. extract it.
|
||||
* FILE *fh = fopen("mypicture.jpg", "wb");
|
||||
* if(fh) {
|
||||
* fwrite(picture.data, picture.size, 1, fh);
|
||||
* fclose(fh);
|
||||
* }
|
||||
* taglib_complex_property_free(properties);
|
||||
* \endcode
|
||||
*
|
||||
* Note that the data in \a picture contains pointers to data in \a properties,
|
||||
* i.e. it only lives as long as the properties, until they are freed with
|
||||
* taglib_complex_property_free().
|
||||
* If you want to access multiple pictures or additional properties of FLAC
|
||||
* pictures ("width", "height", "numColors", "colorDepth" int values), you
|
||||
* have to traverse the \a properties yourself.
|
||||
*/
|
||||
TAGLIB_C_EXPORT void taglib_picture_from_complex_property(
|
||||
TagLib_Complex_Property_Attribute*** properties,
|
||||
TagLib_Complex_Property_Picture_Data *picture);
|
||||
|
||||
/*!
|
||||
* Frees the NULL terminated array \a keys (as returned by
|
||||
* taglib_complex_property_keys()) and the C-strings it contains.
|
||||
*/
|
||||
TAGLIB_C_EXPORT void taglib_complex_property_free_keys(char **keys);
|
||||
|
||||
/*!
|
||||
* Frees the NULL terminated array \a props of property attribute arrays
|
||||
* (as returned by taglib_complex_property_get()) and the data such as
|
||||
* C-strings and byte vectors contained in these attributes.
|
||||
*/
|
||||
TAGLIB_C_EXPORT void taglib_complex_property_free(
|
||||
TagLib_Complex_Property_Attribute ***props);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
prefix=${CMAKE_INSTALL_PREFIX}
|
||||
exec_prefix=${CMAKE_INSTALL_PREFIX}
|
||||
libdir=${LIB_INSTALL_DIR}
|
||||
includedir=${INCLUDE_INSTALL_DIR}
|
||||
|
||||
prefix=@CMAKE_INSTALL_PREFIX@
|
||||
exec_prefix=${prefix}
|
||||
libdir=@CMAKE_PC_LIBDIR@
|
||||
includedir=@CMAKE_PC_INCLUDEDIR@
|
||||
|
||||
Name: TagLib C Bindings
|
||||
Description: Audio meta-data library (C bindings)
|
||||
Requires: taglib
|
||||
Version: ${TAGLIB_LIB_VERSION_STRING}
|
||||
Libs: -L${LIB_INSTALL_DIR} -ltag_c
|
||||
Cflags: -I${INCLUDE_INSTALL_DIR}/taglib
|
||||
Version: @TAGLIB_LIB_VERSION_STRING@
|
||||
Libs: -L${libdir} -ltag_c@TAGLIB_INSTALL_SUFFIX@
|
||||
Cflags: -I${includedir}/taglib@TAGLIB_INSTALL_SUFFIX@
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
include (MacroEnsureVersion)
|
||||
|
||||
if(NOT CPPUNIT_MIN_VERSION)
|
||||
SET(CPPUNIT_MIN_VERSION 1.12.0)
|
||||
SET(CPPUNIT_MIN_VERSION 1.14.0)
|
||||
endif(NOT CPPUNIT_MIN_VERSION)
|
||||
|
||||
FIND_PROGRAM(CPPUNIT_CONFIG_EXECUTABLE cppunit-config )
|
||||
@@ -33,7 +33,7 @@ ELSE(CPPUNIT_INCLUDE_DIR AND CPPUNIT_LIBRARIES)
|
||||
FIND_PATH(CPPUNIT_CFLAGS cppunit/TestRunner.h PATHS /usr/include /usr/local/include )
|
||||
FIND_LIBRARY(CPPUNIT_LIBRARIES NAMES cppunit PATHS /usr/lib /usr/local/lib )
|
||||
# how can we find cppunit version?
|
||||
MESSAGE (STATUS "Ensure you cppunit installed version is at least ${CPPUNIT_MIN_VERSION}")
|
||||
MESSAGE (STATUS "Ensure your cppunit installed version is at least ${CPPUNIT_MIN_VERSION}")
|
||||
SET (CPPUNIT_INSTALLED_VERSION ${CPPUNIT_MIN_VERSION})
|
||||
ENDIF(CPPUNIT_CONFIG_EXECUTABLE)
|
||||
|
||||
@@ -56,7 +56,7 @@ IF(CPPUNIT_INCLUDE_DIR AND CPPUNIT_LIBRARIES)
|
||||
macro_ensure_version( ${CPPUNIT_MIN_VERSION} ${CPPUNIT_INSTALLED_VERSION} CPPUNIT_INSTALLED_VERSION_OK )
|
||||
|
||||
IF(NOT CPPUNIT_INSTALLED_VERSION_OK)
|
||||
MESSAGE ("** CppUnit version is too old: found ${CPPUNIT_INSTALLED_VERSION} installed, ${CPPUNIT_MIN_VERSION} or major is required")
|
||||
MESSAGE ("** CppUnit version is too old: found ${CPPUNIT_INSTALLED_VERSION} installed, ${CPPUNIT_MIN_VERSION} or newer is required")
|
||||
SET(CppUnit_FOUND FALSE)
|
||||
ENDIF(NOT CPPUNIT_INSTALLED_VERSION_OK)
|
||||
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
/* config.h. Generated by cmake from config.h.cmake */
|
||||
|
||||
#ifndef TAGLIB_CONFIG_H
|
||||
#define TAGLIB_CONFIG_H
|
||||
|
||||
/* Defined if your compiler supports some byte swap functions */
|
||||
#cmakedefine HAVE_GCC_BYTESWAP 1
|
||||
#cmakedefine HAVE_GLIBC_BYTESWAP 1
|
||||
@@ -7,22 +10,15 @@
|
||||
#cmakedefine HAVE_MAC_BYTESWAP 1
|
||||
#cmakedefine HAVE_OPENBSD_BYTESWAP 1
|
||||
|
||||
/* 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 ISO _strdup */
|
||||
#cmakedefine HAVE_ISO_STRDUP 1
|
||||
|
||||
/* Defined if your compiler supports some safer version of vsprintf */
|
||||
#cmakedefine HAVE_VSNPRINTF 1
|
||||
#cmakedefine HAVE_VSPRINTF_S 1
|
||||
|
||||
/* Defined if you have libz */
|
||||
/* Defined if zlib is installed */
|
||||
#cmakedefine HAVE_ZLIB 1
|
||||
|
||||
/* Indicates whether debug messages are shown even in release mode */
|
||||
#cmakedefine TRACE_IN_RELEASE 1
|
||||
|
||||
#cmakedefine TESTS_DIR "@TESTS_DIR@"
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,4 +1,2 @@
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,41 +1,38 @@
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
|
||||
<html>
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=9"/>
|
||||
<meta name="generator" content="Doxygen $doxygenversion"/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1"/>
|
||||
<title>$title ($projectname)</title>
|
||||
<link href="taglib-api.css" rel="stylesheet" type="text/css">
|
||||
<link href="$relpath^tabs.css" rel="stylesheet" type="text/css"/>
|
||||
<script type="text/javascript" src="$relpath^jquery.js"></script>
|
||||
<script type="text/javascript" src="$relpath^dynsections.js"></script>
|
||||
$treeview
|
||||
$search
|
||||
$mathjax
|
||||
<link href="$relpath^$stylesheet" rel="stylesheet" type="text/css" />
|
||||
$extrastylesheet
|
||||
</head>
|
||||
<body>
|
||||
|
||||
|
||||
<div id="container">
|
||||
|
||||
<table border="0" width="100%">
|
||||
<tr>
|
||||
<td width="1">
|
||||
<img src="../taglib.png">
|
||||
</td>
|
||||
<td>
|
||||
<div id="intro">
|
||||
<table border="0" height="119" cellpadding="0" cellspacing="0" width="100%">
|
||||
<tr><td valign="top"><h1>TagLib $projectnumber ($title)</h1></td></tr>
|
||||
<tr>
|
||||
<td valign="bottom">
|
||||
<div id="links">
|
||||
<a href="index.html">Home</a>
|
||||
<a href="inherits.html">Class Hierarchy</a>
|
||||
<a href="namespaces.html">Namespaces</a>
|
||||
<a href="annotated.html">Classes</a>
|
||||
<a href="files.html">Headers</a>
|
||||
<a href="namespacemembers.html">Namespace Members</a>
|
||||
<a href="functions.html">Class Members</a>
|
||||
<a href="globals.html">File Members</a>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<div id="text">
|
||||
<div id="top">
|
||||
<div id="titlearea">
|
||||
<table cellspacing="0" cellpadding="0">
|
||||
<tbody>
|
||||
<tr style="height: 56px;">
|
||||
<td id="projectlogo"><img alt="Logo" src="$relpath^$projectlogo" width="200px"/></td>
|
||||
<td id="projectalign" style="padding-left: 0.5em;">
|
||||
<div id="projectname">$projectname
|
||||
 <span id="projectnumber">$projectnumber</span>
|
||||
</div>
|
||||
<div id="projectbrief">$projectbrief</div>
|
||||
</td>
|
||||
<td style="padding-left: 0.5em;">
|
||||
<div id="projectbrief">$projectbrief</div>
|
||||
</td>
|
||||
<td>$searchbox</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
@@ -1,395 +0,0 @@
|
||||
body {
|
||||
font-family: sans-serif;
|
||||
background: white;
|
||||
color: black;
|
||||
margin: 0px;
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
a:link {
|
||||
font-weight: bold;
|
||||
text-decoration: none;
|
||||
color: gray;
|
||||
}
|
||||
|
||||
a:visited {
|
||||
font-weight: bold;
|
||||
text-decoration: none;
|
||||
color: gray;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
color: #cccccc;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
a:active {
|
||||
color: #cccccc;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
img {
|
||||
border-style: none;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-family: sans-serif;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-family: sans-serif;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-family: sans-serif;
|
||||
}
|
||||
|
||||
/* container */
|
||||
|
||||
#container {
|
||||
position: absolute;
|
||||
border-width: thin;
|
||||
border-style: solid;
|
||||
width: 95%;
|
||||
}
|
||||
|
||||
/* intro */
|
||||
|
||||
#intro {
|
||||
padding: 5px;
|
||||
margin: 0px;
|
||||
background: #cccccc;
|
||||
border-width: medium;
|
||||
border-style: solid;
|
||||
}
|
||||
|
||||
#intro h1 {
|
||||
margin: 5px;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
/* links */
|
||||
|
||||
#links {
|
||||
font-size: x-small;
|
||||
vertical-align: bottom;
|
||||
}
|
||||
|
||||
#links a {
|
||||
border-width: thin;
|
||||
border-style: dotted;
|
||||
border-color: white;
|
||||
/* margin: 0px 10px 0px 0px; */
|
||||
margin: 1px;
|
||||
padding: 3px;
|
||||
line-height: 230%
|
||||
}
|
||||
|
||||
#links a:hover {
|
||||
color: black;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
#links h3 {
|
||||
outline-width: thin;
|
||||
border-style: solid;
|
||||
padding: 2px;
|
||||
margin: 3px 0px 3px 0px;
|
||||
}
|
||||
|
||||
/* menu */
|
||||
|
||||
#menu h3 {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* text */
|
||||
|
||||
#text {
|
||||
margin: 0px;
|
||||
padding: 5px 5px 0px 5px;
|
||||
float: left;
|
||||
}
|
||||
|
||||
#text h3 {
|
||||
border-width: thin;
|
||||
border-style: solid;
|
||||
padding: 2px;
|
||||
margin: 3px 0px 3px 0px;
|
||||
}
|
||||
|
||||
#text li {
|
||||
margin: 0px 0px 10px 0px;
|
||||
}
|
||||
|
||||
#text ul {
|
||||
margin: 5px;
|
||||
padding: 0px 0px 0px 20px;
|
||||
}
|
||||
|
||||
#leftcolumn {
|
||||
float: left;
|
||||
width: 300px;
|
||||
margin: 0px 10px 0px 0px;
|
||||
padding: 0px;
|
||||
}
|
||||
|
||||
#rightcolumn {
|
||||
float: right;
|
||||
width: 210px;
|
||||
margin: 0px;
|
||||
padding: 0px;
|
||||
}
|
||||
|
||||
/* vspacer */
|
||||
|
||||
.vspacer {
|
||||
height: 10px;
|
||||
}
|
||||
|
||||
.silver {
|
||||
border-width: thin;
|
||||
border-color: black;
|
||||
border-style: solid;
|
||||
background: #cccccc;
|
||||
}
|
||||
|
||||
a.code {
|
||||
text-decoration: none;
|
||||
font-weight: normal;
|
||||
color: #4444ee
|
||||
}
|
||||
|
||||
a.codeRef {
|
||||
font-weight: normal;
|
||||
color: #4444ee
|
||||
}
|
||||
|
||||
div.fragment {
|
||||
width: 98%;
|
||||
border: 1px solid #CCCCCC;
|
||||
background-color: #f5f5f5;
|
||||
padding-left: 4px;
|
||||
margin: 4px;
|
||||
}
|
||||
|
||||
div.ah {
|
||||
background-color: black;
|
||||
font-weight: bold; color: #ffffff;
|
||||
margin-bottom: 3px;
|
||||
margin-top: 3px
|
||||
}
|
||||
|
||||
#text td {
|
||||
width: auto;
|
||||
}
|
||||
|
||||
div.memdoc {
|
||||
margin-top: 0px;
|
||||
margin-bottom: 20px;
|
||||
padding: 10px 10px 10px 40px;
|
||||
}
|
||||
|
||||
div.memproto {
|
||||
border: thin solid black;
|
||||
background-color: #f2f2ff;
|
||||
width: 100%;
|
||||
margin-top: 20px;
|
||||
padding-top: 10px;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
td.paramtype {
|
||||
color: #602020;
|
||||
}
|
||||
|
||||
table.memname {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
div.groupHeader {
|
||||
margin-left: 16px;
|
||||
margin-top: 12px;
|
||||
margin-bottom: 6px;
|
||||
font-weight: bold
|
||||
}
|
||||
|
||||
div.groupText {
|
||||
margin-left: 16px;
|
||||
font-style: italic;
|
||||
font-size: smaller
|
||||
}
|
||||
|
||||
body {
|
||||
background: white;
|
||||
color: black;
|
||||
margin-right: 20px;
|
||||
margin-left: 20px;
|
||||
}
|
||||
|
||||
td.indexkey {
|
||||
background-color: #eeeeff;
|
||||
font-weight: bold;
|
||||
padding-right : 10px;
|
||||
padding-top : 2px;
|
||||
padding-left : 10px;
|
||||
padding-bottom : 2px;
|
||||
margin-left : 0px;
|
||||
margin-right : 0px;
|
||||
margin-top : 2px;
|
||||
margin-bottom : 2px
|
||||
}
|
||||
|
||||
td.indexvalue {
|
||||
background-color: #eeeeff;
|
||||
font-style: italic;
|
||||
padding-right : 10px;
|
||||
padding-top : 2px;
|
||||
padding-left : 10px;
|
||||
padding-bottom : 2px;
|
||||
margin-left : 0px;
|
||||
margin-right : 0px;
|
||||
margin-top : 2px;
|
||||
margin-bottom : 2px
|
||||
}
|
||||
|
||||
tr.memlist {
|
||||
background-color: #f0f0f0;
|
||||
}
|
||||
|
||||
p.formulaDsp {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
img.formulaDsp {
|
||||
|
||||
}
|
||||
|
||||
img.formulaInl {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
span.keyword {
|
||||
color: #008000
|
||||
}
|
||||
|
||||
span.keywordtype {
|
||||
color: #604020
|
||||
}
|
||||
|
||||
span.keywordflow {
|
||||
color: #e08000
|
||||
}
|
||||
|
||||
span.comment {
|
||||
color: #800000
|
||||
}
|
||||
|
||||
span.preprocessor {
|
||||
color: #806020
|
||||
}
|
||||
|
||||
span.stringliteral {
|
||||
color: #002080
|
||||
}
|
||||
|
||||
span.charliteral {
|
||||
color: #008080
|
||||
}
|
||||
|
||||
.mdTable {
|
||||
border: 1px solid #868686;
|
||||
background-color: #f2f2ff;
|
||||
}
|
||||
|
||||
.mdRow {
|
||||
padding: 8px 20px;
|
||||
}
|
||||
|
||||
.mdescLeft {
|
||||
font-size: smaller;
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
background-color: #FAFAFA;
|
||||
padding-left: 8px;
|
||||
border-top: 1px none #E0E0E0;
|
||||
border-right: 1px none #E0E0E0;
|
||||
border-bottom: 1px none #E0E0E0;
|
||||
border-left: 1px none #E0E0E0;
|
||||
margin: 0px;
|
||||
}
|
||||
|
||||
.mdescRight {
|
||||
font-size: smaller;
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
font-style: italic;
|
||||
background-color: #FAFAFA;
|
||||
padding-left: 4px;
|
||||
border-top: 1px none #E0E0E0;
|
||||
border-right: 1px none #E0E0E0;
|
||||
border-bottom: 1px none #E0E0E0;
|
||||
border-left: 1px none #E0E0E0;
|
||||
margin: 0px;
|
||||
padding-bottom: 0px;
|
||||
padding-right: 8px;
|
||||
}
|
||||
|
||||
.memItemLeft {
|
||||
padding: 1px 0px 0px 8px;
|
||||
margin: 4px;
|
||||
border-top-width: 1px;
|
||||
border-right-width: 1px;
|
||||
border-bottom-width: 1px;
|
||||
border-left-width: 1px;
|
||||
border-top-style: solid;
|
||||
border-top-color: #E0E0E0;
|
||||
border-right-color: #E0E0E0;
|
||||
border-bottom-color: #E0E0E0;
|
||||
border-left-color: #E0E0E0;
|
||||
border-right-style: none;
|
||||
border-bottom-style: none;
|
||||
border-left-style: none;
|
||||
background-color: #FAFAFA;
|
||||
font-family: Geneva, Arial, Helvetica, sans-serif;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.memItemRight {
|
||||
padding: 1px 0px 0px 8px;
|
||||
margin: 4px;
|
||||
border-top-width: 1px;
|
||||
border-right-width: 1px;
|
||||
border-bottom-width: 1px;
|
||||
border-left-width: 1px;
|
||||
border-top-style: solid;
|
||||
border-top-color: #E0E0E0;
|
||||
border-right-color: #E0E0E0;
|
||||
border-bottom-color: #E0E0E0;
|
||||
border-left-color: #E0E0E0;
|
||||
border-right-style: none;
|
||||
border-bottom-style: none;
|
||||
border-left-style: none;
|
||||
background-color: #FAFAFA;
|
||||
font-family: Geneva, Arial, Helvetica, sans-serif;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.search {
|
||||
color: #0000ee;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
form.search {
|
||||
margin-bottom: 0px;
|
||||
margin-top: 0px;
|
||||
}
|
||||
|
||||
input.search {
|
||||
font-size: 75%;
|
||||
color: #000080;
|
||||
font-weight: normal;
|
||||
background-color: #eeeeff;
|
||||
}
|
||||
|
||||
td.tiny {
|
||||
font-size: 75%;
|
||||
}
|
||||
BIN
doc/taglib.png
BIN
doc/taglib.png
Binary file not shown.
|
Before Width: | Height: | Size: 2.7 KiB |
58
doc/taglib.svg
Normal file
58
doc/taglib.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 11 KiB |
@@ -1,50 +1,45 @@
|
||||
if(BUILD_EXAMPLES)
|
||||
INCLUDE_DIRECTORIES( ${CMAKE_CURRENT_SOURCE_DIR}/../taglib
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../taglib/toolkit
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../taglib/ape
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../taglib/mpeg
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../taglib/mpeg/id3v1
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../taglib/mpeg/id3v2
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../bindings/c/ )
|
||||
include_directories(
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../taglib
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../taglib/toolkit
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../taglib/ape
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../taglib/mpeg
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../taglib/mpeg/id3v1
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../taglib/mpeg/id3v2
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../taglib/mpeg/id3v2/frames
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../bindings/c/
|
||||
)
|
||||
|
||||
if(ENABLE_STATIC)
|
||||
add_definitions(-DTAGLIB_STATIC)
|
||||
endif(ENABLE_STATIC)
|
||||
if(NOT BUILD_SHARED_LIBS)
|
||||
add_definitions(-DTAGLIB_STATIC)
|
||||
endif()
|
||||
|
||||
########### next target ###############
|
||||
|
||||
ADD_EXECUTABLE(tagreader tagreader.cpp)
|
||||
|
||||
TARGET_LINK_LIBRARIES(tagreader tag )
|
||||
|
||||
add_executable(tagreader tagreader.cpp)
|
||||
target_link_libraries(tagreader tag)
|
||||
|
||||
########### next target ###############
|
||||
|
||||
ADD_EXECUTABLE(tagreader_c tagreader_c.c)
|
||||
|
||||
TARGET_LINK_LIBRARIES(tagreader_c tag_c )
|
||||
|
||||
add_executable(tagreader_c tagreader_c.c)
|
||||
target_link_libraries(tagreader_c tag_c)
|
||||
|
||||
########### next target ###############
|
||||
|
||||
ADD_EXECUTABLE(tagwriter tagwriter.cpp)
|
||||
|
||||
TARGET_LINK_LIBRARIES(tagwriter tag )
|
||||
|
||||
add_executable(tagwriter tagwriter.cpp)
|
||||
target_link_libraries(tagwriter tag)
|
||||
|
||||
########### next target ###############
|
||||
|
||||
ADD_EXECUTABLE(framelist framelist.cpp)
|
||||
|
||||
TARGET_LINK_LIBRARIES(framelist tag )
|
||||
|
||||
add_executable(framelist framelist.cpp)
|
||||
target_link_libraries(framelist tag)
|
||||
|
||||
########### next target ###############
|
||||
|
||||
ADD_EXECUTABLE(strip-id3v1 strip-id3v1.cpp)
|
||||
|
||||
TARGET_LINK_LIBRARIES(strip-id3v1 tag )
|
||||
|
||||
|
||||
endif(BUILD_EXAMPLES)
|
||||
add_executable(strip-id3v1 strip-id3v1.cpp)
|
||||
target_link_libraries(strip-id3v1 tag)
|
||||
|
||||
install(TARGETS tagreader tagreader_c tagwriter framelist strip-id3v1
|
||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
|
||||
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
)
|
||||
|
||||
@@ -23,19 +23,16 @@
|
||||
*/
|
||||
|
||||
#include <iostream>
|
||||
#include <stdlib.h>
|
||||
#include <cstdlib>
|
||||
|
||||
#include <tbytevector.h>
|
||||
|
||||
#include <mpegfile.h>
|
||||
|
||||
#include <id3v2tag.h>
|
||||
#include <id3v2frame.h>
|
||||
#include <id3v2header.h>
|
||||
|
||||
#include <id3v1tag.h>
|
||||
|
||||
#include <apetag.h>
|
||||
#include "tbytevector.h"
|
||||
#include "mpegfile.h"
|
||||
#include "id3v2tag.h"
|
||||
#include "id3v2frame.h"
|
||||
#include "id3v2header.h"
|
||||
#include "commentsframe.h"
|
||||
#include "id3v1tag.h"
|
||||
#include "apetag.h"
|
||||
|
||||
using namespace std;
|
||||
using namespace TagLib;
|
||||
@@ -64,9 +61,16 @@ int main(int argc, char *argv[])
|
||||
<< " bytes in tag"
|
||||
<< endl;
|
||||
|
||||
ID3v2::FrameList::ConstIterator it = id3v2tag->frameList().begin();
|
||||
for(; it != id3v2tag->frameList().end(); it++)
|
||||
cout << (*it)->frameID() << " - \"" << (*it)->toString() << "\"" << endl;
|
||||
const auto &frames = id3v2tag->frameList();
|
||||
for(auto it = frames.begin(); it != frames.end(); it++) {
|
||||
cout << (*it)->frameID();
|
||||
|
||||
if(auto comment = dynamic_cast<ID3v2::CommentsFrame *>(*it))
|
||||
if(!comment->description().isEmpty())
|
||||
cout << " [" << comment->description() << "]";
|
||||
|
||||
cout << " - \"" << (*it)->toString() << "\"" << endl;
|
||||
}
|
||||
}
|
||||
else
|
||||
cout << "file does not have a valid id3v2 tag" << endl;
|
||||
@@ -92,8 +96,8 @@ int main(int argc, char *argv[])
|
||||
cout << endl << "APE" << endl;
|
||||
|
||||
if(ape) {
|
||||
for(APE::ItemListMap::ConstIterator it = ape->itemListMap().begin();
|
||||
it != ape->itemListMap().end(); ++it)
|
||||
const auto &items = ape->itemListMap();
|
||||
for(auto it = items.begin(); it != items.end(); ++it)
|
||||
{
|
||||
if((*it).second.type() != APE::Item::Binary)
|
||||
cout << (*it).first << " - \"" << (*it).second.toString() << "\"" << endl;
|
||||
|
||||
@@ -23,8 +23,9 @@
|
||||
*/
|
||||
|
||||
#include <iostream>
|
||||
#include <mpegfile.h>
|
||||
#include <tstring.h>
|
||||
|
||||
#include "tstring.h"
|
||||
#include "mpegfile.h"
|
||||
|
||||
using namespace TagLib;
|
||||
|
||||
|
||||
@@ -24,11 +24,13 @@
|
||||
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include <stdio.h>
|
||||
#include <cstdio>
|
||||
|
||||
#include <fileref.h>
|
||||
#include <tag.h>
|
||||
#include <tpropertymap.h>
|
||||
#include "tpropertymap.h"
|
||||
#include "tstringlist.h"
|
||||
#include "tvariant.h"
|
||||
#include "fileref.h"
|
||||
#include "tag.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
@@ -53,36 +55,66 @@ int main(int argc, char *argv[])
|
||||
cout << "track - \"" << tag->track() << "\"" << endl;
|
||||
cout << "genre - \"" << tag->genre() << "\"" << endl;
|
||||
|
||||
TagLib::PropertyMap tags = f.file()->properties();
|
||||
TagLib::PropertyMap tags = f.properties();
|
||||
if(!tags.isEmpty()) {
|
||||
unsigned int longest = 0;
|
||||
for(auto j = tags.cbegin(); j != tags.cend(); ++j) {
|
||||
if (j->first.size() > longest) {
|
||||
longest = j->first.size();
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int longest = 0;
|
||||
for(TagLib::PropertyMap::ConstIterator i = tags.begin(); i != tags.end(); ++i) {
|
||||
if (i->first.size() > longest) {
|
||||
longest = i->first.size();
|
||||
cout << "-- TAG (properties) --" << endl;
|
||||
for(auto j = tags.cbegin(); j != tags.cend(); ++j) {
|
||||
for(auto k = j->second.begin(); k != j->second.end(); ++k) {
|
||||
cout << left << std::setfill(' ') << std::setw(longest) << j->first << " - " << '"' << *k << '"' << endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cout << "-- TAG (properties) --" << endl;
|
||||
for(TagLib::PropertyMap::ConstIterator i = tags.begin(); i != tags.end(); ++i) {
|
||||
for(TagLib::StringList::ConstIterator j = i->second.begin(); j != i->second.end(); ++j) {
|
||||
cout << left << std::setw(longest) << i->first << " - " << '"' << *j << '"' << endl;
|
||||
TagLib::StringList names = f.complexPropertyKeys();
|
||||
for(const auto &name : names) {
|
||||
const auto& properties = f.complexProperties(name);
|
||||
for(const auto &property : properties) {
|
||||
cout << name << ":" << endl;
|
||||
for(const auto &[key, value] : property) {
|
||||
cout << " " << left << std::setfill(' ') << std::setw(11) << key << " - ";
|
||||
if(value.type() == TagLib::Variant::ByteVector) {
|
||||
cout << "(" << value.value<TagLib::ByteVector>().size() << " bytes)" << endl;
|
||||
/* The picture could be extracted using:
|
||||
ofstream picture;
|
||||
TagLib::String fn(argv[i]);
|
||||
int slashPos = fn.rfind('/');
|
||||
int dotPos = fn.rfind('.');
|
||||
if(slashPos >= 0 && dotPos > slashPos) {
|
||||
fn = fn.substr(slashPos + 1, dotPos - slashPos - 1);
|
||||
}
|
||||
fn += ".jpg";
|
||||
picture.open(fn.toCString(), ios_base::out | ios_base::binary);
|
||||
picture << value.value<TagLib::ByteVector>();
|
||||
picture.close();
|
||||
*/
|
||||
}
|
||||
else {
|
||||
cout << value << endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if(!f.isNull() && f.audioProperties()) {
|
||||
|
||||
TagLib::AudioProperties *properties = f.audioProperties();
|
||||
|
||||
int seconds = properties->length() % 60;
|
||||
int minutes = (properties->length() - seconds) / 60;
|
||||
int seconds = properties->lengthInSeconds() % 60;
|
||||
int minutes = (properties->lengthInSeconds() - seconds) / 60;
|
||||
|
||||
cout << "-- AUDIO --" << endl;
|
||||
cout << "bitrate - " << properties->bitrate() << endl;
|
||||
cout << "sample rate - " << properties->sampleRate() << endl;
|
||||
cout << "channels - " << properties->channels() << endl;
|
||||
cout << "length - " << minutes << ":" << setfill('0') << setw(2) << seconds << endl;
|
||||
cout << "length - " << minutes << ":" << setfill('0') << setw(2) << right << seconds << endl;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
|
||||
@@ -23,7 +23,9 @@
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <tag_c.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "tag_c.h"
|
||||
|
||||
#ifndef FALSE
|
||||
#define FALSE 0
|
||||
@@ -32,15 +34,16 @@
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
int i;
|
||||
int seconds;
|
||||
int minutes;
|
||||
TagLib_File *file;
|
||||
TagLib_Tag *tag;
|
||||
const TagLib_AudioProperties *properties;
|
||||
|
||||
taglib_set_strings_unicode(FALSE);
|
||||
taglib_set_strings_unicode(1);
|
||||
|
||||
for(i = 1; i < argc; i++) {
|
||||
TagLib_File *file;
|
||||
TagLib_Tag *tag;
|
||||
const TagLib_AudioProperties *properties;
|
||||
char **propertiesMap;
|
||||
char **complexKeys;
|
||||
|
||||
printf("******************** \"%s\" ********************\n", argv[i]);
|
||||
|
||||
file = taglib_file_new(argv[i]);
|
||||
@@ -50,21 +53,115 @@ int main(int argc, char *argv[])
|
||||
|
||||
tag = taglib_file_tag(file);
|
||||
properties = taglib_file_audioproperties(file);
|
||||
propertiesMap = taglib_property_keys(file);
|
||||
complexKeys = taglib_complex_property_keys(file);
|
||||
|
||||
if(tag != NULL) {
|
||||
printf("-- TAG --\n");
|
||||
printf("-- TAG (basic) --\n");
|
||||
printf("title - \"%s\"\n", taglib_tag_title(tag));
|
||||
printf("artist - \"%s\"\n", taglib_tag_artist(tag));
|
||||
printf("album - \"%s\"\n", taglib_tag_album(tag));
|
||||
printf("year - \"%i\"\n", taglib_tag_year(tag));
|
||||
printf("year - \"%u\"\n", taglib_tag_year(tag));
|
||||
printf("comment - \"%s\"\n", taglib_tag_comment(tag));
|
||||
printf("track - \"%i\"\n", taglib_tag_track(tag));
|
||||
printf("track - \"%u\"\n", taglib_tag_track(tag));
|
||||
printf("genre - \"%s\"\n", taglib_tag_genre(tag));
|
||||
}
|
||||
|
||||
|
||||
if(propertiesMap != NULL) {
|
||||
char **keyPtr = propertiesMap;
|
||||
int longest = 0;
|
||||
while(*keyPtr) {
|
||||
int len = (int)strlen(*keyPtr++);
|
||||
if(len > longest) {
|
||||
longest = len;
|
||||
}
|
||||
}
|
||||
keyPtr = propertiesMap;
|
||||
|
||||
printf("-- TAG (properties) --\n");
|
||||
while(*keyPtr) {
|
||||
char **valPtr;
|
||||
char **propertyValues = valPtr = taglib_property_get(file, *keyPtr);
|
||||
while(valPtr && *valPtr)
|
||||
{
|
||||
printf("%-*s - \"%s\"\n", longest, *keyPtr, *valPtr++);
|
||||
}
|
||||
taglib_property_free(propertyValues);
|
||||
++keyPtr;
|
||||
}
|
||||
}
|
||||
|
||||
if(complexKeys != NULL) {
|
||||
char **keyPtr = complexKeys;
|
||||
while(*keyPtr) {
|
||||
TagLib_Complex_Property_Attribute*** props =
|
||||
taglib_complex_property_get(file, *keyPtr);
|
||||
if(props != NULL) {
|
||||
TagLib_Complex_Property_Attribute*** propPtr = props;
|
||||
while(*propPtr) {
|
||||
TagLib_Complex_Property_Attribute** attrPtr = *propPtr;
|
||||
printf("%s:\n", *keyPtr);
|
||||
while(*attrPtr) {
|
||||
TagLib_Complex_Property_Attribute *attr = *attrPtr;
|
||||
TagLib_Variant_Type type = attr->value.type;
|
||||
printf(" %-11s - ", attr->key);
|
||||
switch(type) {
|
||||
case TagLib_Variant_Void:
|
||||
printf("null\n");
|
||||
break;
|
||||
case TagLib_Variant_Bool:
|
||||
printf("%s\n", attr->value.value.boolValue ? "true" : "false");
|
||||
break;
|
||||
case TagLib_Variant_Int:
|
||||
printf("%d\n", attr->value.value.intValue);
|
||||
break;
|
||||
case TagLib_Variant_UInt:
|
||||
printf("%u\n", attr->value.value.uIntValue);
|
||||
break;
|
||||
case TagLib_Variant_LongLong:
|
||||
printf("%lld\n", attr->value.value.longLongValue);
|
||||
break;
|
||||
case TagLib_Variant_ULongLong:
|
||||
printf("%llu\n", attr->value.value.uLongLongValue);
|
||||
break;
|
||||
case TagLib_Variant_Double:
|
||||
printf("%f\n", attr->value.value.doubleValue);
|
||||
break;
|
||||
case TagLib_Variant_String:
|
||||
printf("\"%s\"\n", attr->value.value.stringValue);
|
||||
break;
|
||||
case TagLib_Variant_StringList:
|
||||
if(attr->value.value.stringListValue) {
|
||||
char **strs = attr->value.value.stringListValue;
|
||||
char **s = strs;
|
||||
while(*s) {
|
||||
if(s != strs) {
|
||||
printf(" ");
|
||||
}
|
||||
printf("%s", *s++);
|
||||
}
|
||||
}
|
||||
printf("\n");
|
||||
break;
|
||||
case TagLib_Variant_ByteVector:
|
||||
printf("(%u bytes)\n", attr->value.size);
|
||||
break;
|
||||
}
|
||||
++attrPtr;
|
||||
}
|
||||
++propPtr;
|
||||
}
|
||||
taglib_complex_property_free(props);
|
||||
}
|
||||
++keyPtr;
|
||||
}
|
||||
taglib_complex_property_free_keys(complexKeys);
|
||||
}
|
||||
|
||||
if(properties != NULL) {
|
||||
seconds = taglib_audioproperties_length(properties) % 60;
|
||||
minutes = (taglib_audioproperties_length(properties) - seconds) / 60;
|
||||
int seconds = taglib_audioproperties_length(properties) % 60;
|
||||
int minutes = (taglib_audioproperties_length(properties) - seconds) / 60;
|
||||
|
||||
printf("-- AUDIO --\n");
|
||||
printf("bitrate - %i\n", taglib_audioproperties_bitrate(properties));
|
||||
@@ -73,6 +170,7 @@ int main(int argc, char *argv[])
|
||||
printf("length - %i:%02i\n", minutes, seconds);
|
||||
}
|
||||
|
||||
taglib_property_free(propertiesMap);
|
||||
taglib_tag_free_strings();
|
||||
taglib_file_free(file);
|
||||
}
|
||||
|
||||
@@ -24,18 +24,20 @@
|
||||
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include <string.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <cstring>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <tlist.h>
|
||||
#include <fileref.h>
|
||||
#include <tfile.h>
|
||||
#include <tag.h>
|
||||
#include <tpropertymap.h>
|
||||
#include "tlist.h"
|
||||
#include "tfile.h"
|
||||
#include "tpropertymap.h"
|
||||
#include "tvariant.h"
|
||||
#include "fileref.h"
|
||||
#include "tag.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
@@ -70,6 +72,7 @@ void usage()
|
||||
cout << " -R <tagname> <tagvalue>" << endl;
|
||||
cout << " -I <tagname> <tagvalue>" << endl;
|
||||
cout << " -D <tagname>" << endl;
|
||||
cout << " -p <picturefile> <description> (\"\" \"\" to remove)" << endl;
|
||||
cout << endl;
|
||||
|
||||
exit(1);
|
||||
@@ -79,14 +82,14 @@ void checkForRejectedProperties(const TagLib::PropertyMap &tags)
|
||||
{ // stolen from tagreader.cpp
|
||||
if(tags.size() > 0) {
|
||||
unsigned int longest = 0;
|
||||
for(TagLib::PropertyMap::ConstIterator i = tags.begin(); i != tags.end(); ++i) {
|
||||
for(auto i = tags.begin(); i != tags.end(); ++i) {
|
||||
if(i->first.size() > longest) {
|
||||
longest = i->first.size();
|
||||
}
|
||||
}
|
||||
cout << "-- rejected TAGs (properties) --" << endl;
|
||||
for(TagLib::PropertyMap::ConstIterator i = tags.begin(); i != tags.end(); ++i) {
|
||||
for(TagLib::StringList::ConstIterator j = i->second.begin(); j != i->second.end(); ++j) {
|
||||
for(auto i = tags.begin(); i != tags.end(); ++i) {
|
||||
for(auto j = i->second.begin(); j != i->second.end(); ++j) {
|
||||
cout << left << std::setw(longest) << i->first << " - " << '"' << *j << '"' << endl;
|
||||
}
|
||||
}
|
||||
@@ -110,17 +113,18 @@ int main(int argc, char *argv[])
|
||||
if(fileList.isEmpty())
|
||||
usage();
|
||||
|
||||
for(int i = 1; i < argc - 1; i += 2) {
|
||||
int i = 1;
|
||||
while(i < argc - 1) {
|
||||
|
||||
if(isArgument(argv[i]) && i + 1 < argc && !isArgument(argv[i + 1])) {
|
||||
|
||||
char field = argv[i][1];
|
||||
TagLib::String value = argv[i + 1];
|
||||
int numArgsConsumed = 2;
|
||||
|
||||
TagLib::List<TagLib::FileRef>::ConstIterator it;
|
||||
for(it = fileList.begin(); it != fileList.end(); ++it) {
|
||||
for(auto &f : fileList) {
|
||||
|
||||
TagLib::Tag *t = (*it).tag();
|
||||
TagLib::Tag *t = f.tag();
|
||||
|
||||
switch (field) {
|
||||
case 't':
|
||||
@@ -147,24 +151,61 @@ int main(int argc, char *argv[])
|
||||
case 'R':
|
||||
case 'I':
|
||||
if(i + 2 < argc) {
|
||||
TagLib::PropertyMap map = (*it).file()->properties ();
|
||||
TagLib::PropertyMap map = f.properties();
|
||||
if(field == 'R') {
|
||||
map.replace(value, TagLib::String(argv[i + 2]));
|
||||
}
|
||||
else {
|
||||
map.insert(value, TagLib::String(argv[i + 2]));
|
||||
}
|
||||
++i;
|
||||
checkForRejectedProperties((*it).file()->setProperties(map));
|
||||
numArgsConsumed = 3;
|
||||
checkForRejectedProperties(f.setProperties(map));
|
||||
}
|
||||
else {
|
||||
usage();
|
||||
}
|
||||
break;
|
||||
case 'D': {
|
||||
TagLib::PropertyMap map = (*it).file()->properties();
|
||||
TagLib::PropertyMap map = f.properties();
|
||||
map.erase(value);
|
||||
checkForRejectedProperties((*it).file()->setProperties(map));
|
||||
checkForRejectedProperties(f.setProperties(map));
|
||||
break;
|
||||
}
|
||||
case 'p': {
|
||||
if(i + 2 < argc) {
|
||||
numArgsConsumed = 3;
|
||||
if(!value.isEmpty()) {
|
||||
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));
|
||||
TagLib::String mimeType = data.startsWith("\x89PNG\x0d\x0a\x1a\x0a")
|
||||
? "image/png" : "image/jpeg";
|
||||
TagLib::String description(argv[i + 2]);
|
||||
f.setComplexProperties("PICTURE", {
|
||||
{
|
||||
{"data", data},
|
||||
{"pictureType", "Front Cover"},
|
||||
{"mimeType", mimeType},
|
||||
{"description", description}
|
||||
}
|
||||
});
|
||||
}
|
||||
else {
|
||||
// empty value, remove pictures
|
||||
f.setComplexProperties("PICTURE", {});
|
||||
}
|
||||
}
|
||||
else {
|
||||
usage();
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
@@ -172,14 +213,14 @@ int main(int argc, char *argv[])
|
||||
break;
|
||||
}
|
||||
}
|
||||
i += numArgsConsumed;
|
||||
}
|
||||
else
|
||||
usage();
|
||||
}
|
||||
|
||||
TagLib::List<TagLib::FileRef>::ConstIterator it;
|
||||
for(it = fileList.begin(); it != fileList.end(); ++it)
|
||||
(*it).file()->save();
|
||||
for(auto &f : fileList)
|
||||
f.save();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -2,22 +2,32 @@
|
||||
|
||||
usage()
|
||||
{
|
||||
echo "usage: $0 [OPTIONS]"
|
||||
echo "usage: $0 [OPTIONS]"
|
||||
cat << EOH
|
||||
|
||||
options:
|
||||
[--libs]
|
||||
[--cflags]
|
||||
[--version]
|
||||
[--prefix]
|
||||
[--libs]
|
||||
[--cflags]
|
||||
[--version]
|
||||
[--prefix]
|
||||
EOH
|
||||
exit 1;
|
||||
exit 1
|
||||
}
|
||||
|
||||
prefix=${CMAKE_INSTALL_PREFIX}
|
||||
exec_prefix=${CMAKE_INSTALL_PREFIX}
|
||||
libdir=${LIB_INSTALL_DIR}
|
||||
includedir=${INCLUDE_INSTALL_DIR}
|
||||
# Looks useless as it is, but could be replaced with a "pcfiledir" by Buildroot.
|
||||
prefix=
|
||||
exec_prefix=
|
||||
|
||||
if test -z "$prefix"; then
|
||||
includedir=@CMAKE_INSTALL_FULL_INCLUDEDIR@
|
||||
else
|
||||
includedir=${prefix}/@CMAKE_INSTALL_INCLUDEDIR@
|
||||
fi
|
||||
if test -z "$exec_prefix"; then
|
||||
libdir=@CMAKE_INSTALL_FULL_LIBDIR@
|
||||
else
|
||||
libdir=${exec_prefix}/@CMAKE_INSTALL_LIBDIR@
|
||||
fi
|
||||
|
||||
flags=""
|
||||
|
||||
@@ -29,22 +39,22 @@ while test $# -gt 0
|
||||
do
|
||||
case $1 in
|
||||
--libs)
|
||||
flags="$flags -L$libdir -ltag"
|
||||
;;
|
||||
flags="$flags -L$libdir -ltag@TAGLIB_INSTALL_SUFFIX@ @ZLIB_LIBRARIES_FLAGS@"
|
||||
;;
|
||||
--cflags)
|
||||
flags="$flags -I$includedir/taglib"
|
||||
;;
|
||||
flags="$flags -I$includedir -I$includedir/taglib@TAGLIB_INSTALL_SUFFIX@"
|
||||
;;
|
||||
--version)
|
||||
echo ${TAGLIB_LIB_VERSION_STRING}
|
||||
;;
|
||||
echo @TAGLIB_LIB_VERSION_STRING@
|
||||
;;
|
||||
--prefix)
|
||||
echo $prefix
|
||||
;;
|
||||
*)
|
||||
echo "$0: unknown option $1"
|
||||
echo
|
||||
usage
|
||||
;;
|
||||
echo ${prefix:-@CMAKE_INSTALL_PREFIX@}
|
||||
;;
|
||||
*)
|
||||
echo "$0: unknown option $1"
|
||||
echo
|
||||
usage
|
||||
;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
12
taglib-config.cmake.in
Normal file
12
taglib-config.cmake.in
Normal file
@@ -0,0 +1,12 @@
|
||||
@PACKAGE_INIT@
|
||||
|
||||
include("${CMAKE_CURRENT_LIST_DIR}/taglib-targets.cmake")
|
||||
|
||||
set(TAGLIB_FOUND ${TagLib_FOUND})
|
||||
set(TAGLIB_VERSION ${TagLib_VERSION})
|
||||
|
||||
check_required_components("TagLib")
|
||||
|
||||
if(NOT TARGET TagLib::TagLib)
|
||||
add_library(TagLib::TagLib ALIAS TagLib::tag)
|
||||
endif()
|
||||
@@ -14,11 +14,11 @@ if /i "%1#" == "--cflags#" goto doit
|
||||
if /i "%1#" == "--version#" goto doit
|
||||
if /i "%1#" == "--prefix#" goto doit
|
||||
|
||||
echo "usage: %0 [OPTIONS]"
|
||||
echo [--libs]
|
||||
echo [--cflags]
|
||||
echo [--version]
|
||||
echo [--prefix]
|
||||
echo usage: %0 [OPTIONS]
|
||||
echo [--libs]
|
||||
echo [--cflags]
|
||||
echo [--version]
|
||||
echo [--prefix]
|
||||
goto theend
|
||||
|
||||
*
|
||||
@@ -27,10 +27,9 @@ goto theend
|
||||
* to allow for static, shared or debug builds.
|
||||
* It would be preferable if the top level CMakeLists.txt provided the library name during config. ??
|
||||
:doit
|
||||
if /i "%1#" == "--libs#" echo -L${LIB_INSTALL_DIR} -llibtag
|
||||
if /i "%1#" == "--cflags#" echo -I${INCLUDE_INSTALL_DIR}/taglib
|
||||
if /i "%1#" == "--libs#" echo -L${CMAKE_INSTALL_FULL_LIBDIR} -llibtag${TAGLIB_INSTALL_SUFFIX}
|
||||
if /i "%1#" == "--cflags#" echo -I${CMAKE_INSTALL_FULL_INCLUDEDIR} -I${CMAKE_INSTALL_FULL_INCLUDEDIR}/taglib${TAGLIB_INSTALL_SUFFIX}
|
||||
if /i "%1#" == "--version#" echo ${TAGLIB_LIB_VERSION_STRING}
|
||||
if /i "%1#" == "--prefix#" echo ${CMAKE_INSTALL_PREFIX}
|
||||
|
||||
:theend
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
prefix=${CMAKE_INSTALL_PREFIX}
|
||||
exec_prefix=${CMAKE_INSTALL_PREFIX}
|
||||
libdir=${LIB_INSTALL_DIR}
|
||||
includedir=${INCLUDE_INSTALL_DIR}
|
||||
prefix=@CMAKE_INSTALL_PREFIX@
|
||||
exec_prefix=${prefix}
|
||||
libdir=@CMAKE_PC_LIBDIR@
|
||||
includedir=@CMAKE_PC_INCLUDEDIR@
|
||||
|
||||
Name: TagLib
|
||||
Description: Audio meta-data library
|
||||
Requires:
|
||||
Version: ${TAGLIB_LIB_VERSION_STRING}
|
||||
Libs: -L${LIB_INSTALL_DIR} -ltag
|
||||
Cflags: -I${INCLUDE_INSTALL_DIR}/taglib
|
||||
Requires:
|
||||
Version: @TAGLIB_LIB_VERSION_STRING@
|
||||
Libs: -L${libdir} -ltag@TAGLIB_INSTALL_SUFFIX@ @ZLIB_LIBRARIES_FLAGS@
|
||||
Cflags: -I${includedir} -I${includedir}/taglib@TAGLIB_INSTALL_SUFFIX@
|
||||
|
||||
@@ -24,14 +24,10 @@ include_directories(
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/s3m
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/it
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/xm
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/dsf
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/dsdiff
|
||||
)
|
||||
|
||||
if(ZLIB_FOUND)
|
||||
include_directories(${ZLIB_INCLUDE_DIR})
|
||||
elseif(HAVE_ZLIB_SOURCE)
|
||||
include_directories(${ZLIB_SOURCE})
|
||||
endif()
|
||||
|
||||
set(tag_HDRS
|
||||
tag.h
|
||||
fileref.h
|
||||
@@ -45,21 +41,24 @@ set(tag_HDRS
|
||||
toolkit/tstringlist.h
|
||||
toolkit/tbytevector.h
|
||||
toolkit/tbytevectorlist.h
|
||||
toolkit/tvariant.h
|
||||
toolkit/tbytevectorstream.h
|
||||
toolkit/tiostream.h
|
||||
toolkit/tfile.h
|
||||
toolkit/tfilestream.h
|
||||
toolkit/tmap.h
|
||||
toolkit/tmap.tcc
|
||||
toolkit/tpicturetype.h
|
||||
toolkit/tpropertymap.h
|
||||
toolkit/trefcounter.h
|
||||
toolkit/tdebuglistener.h
|
||||
toolkit/tversionnumber.h
|
||||
mpeg/mpegfile.h
|
||||
mpeg/mpegproperties.h
|
||||
mpeg/mpegheader.h
|
||||
mpeg/xingheader.h
|
||||
mpeg/id3v1/id3v1tag.h
|
||||
mpeg/id3v1/id3v1genres.h
|
||||
mpeg/id3v2/id3v2.h
|
||||
mpeg/id3v2/id3v2extendedheader.h
|
||||
mpeg/id3v2/id3v2frame.h
|
||||
mpeg/id3v2/id3v2header.h
|
||||
@@ -83,6 +82,7 @@ set(tag_HDRS
|
||||
mpeg/id3v2/frames/urllinkframe.h
|
||||
mpeg/id3v2/frames/chapterframe.h
|
||||
mpeg/id3v2/frames/tableofcontentsframe.h
|
||||
mpeg/id3v2/frames/podcastframe.h
|
||||
ogg/oggfile.h
|
||||
ogg/oggpage.h
|
||||
ogg/oggpageheader.h
|
||||
@@ -126,6 +126,7 @@ set(tag_HDRS
|
||||
mp4/mp4item.h
|
||||
mp4/mp4properties.h
|
||||
mp4/mp4coverart.h
|
||||
mp4/mp4itemfactory.h
|
||||
mod/modfilebase.h
|
||||
mod/modfile.h
|
||||
mod/modtag.h
|
||||
@@ -136,6 +137,11 @@ set(tag_HDRS
|
||||
s3m/s3mproperties.h
|
||||
xm/xmfile.h
|
||||
xm/xmproperties.h
|
||||
dsf/dsffile.h
|
||||
dsf/dsfproperties.h
|
||||
dsdiff/dsdifffile.h
|
||||
dsdiff/dsdiffproperties.h
|
||||
dsdiff/dsdiffdiintag.h
|
||||
)
|
||||
|
||||
set(mpeg_SRCS
|
||||
@@ -177,6 +183,7 @@ set(frames_SRCS
|
||||
mpeg/id3v2/frames/urllinkframe.cpp
|
||||
mpeg/id3v2/frames/chapterframe.cpp
|
||||
mpeg/id3v2/frames/tableofcontentsframe.cpp
|
||||
mpeg/id3v2/frames/podcastframe.cpp
|
||||
)
|
||||
|
||||
set(ogg_SRCS
|
||||
@@ -215,6 +222,7 @@ set(mp4_SRCS
|
||||
mp4/mp4item.cpp
|
||||
mp4/mp4properties.cpp
|
||||
mp4/mp4coverart.cpp
|
||||
mp4/mp4itemfactory.cpp
|
||||
)
|
||||
|
||||
set(ape_SRCS
|
||||
@@ -290,72 +298,120 @@ set(xm_SRCS
|
||||
xm/xmproperties.cpp
|
||||
)
|
||||
|
||||
set(dsf_SRCS
|
||||
dsf/dsffile.cpp
|
||||
dsf/dsfproperties.cpp
|
||||
)
|
||||
|
||||
set(dsdiff_SRCS
|
||||
dsdiff/dsdifffile.cpp
|
||||
dsdiff/dsdiffproperties.cpp
|
||||
dsdiff/dsdiffdiintag.cpp
|
||||
)
|
||||
|
||||
set(toolkit_SRCS
|
||||
toolkit/tstring.cpp
|
||||
toolkit/tstringlist.cpp
|
||||
toolkit/tbytevector.cpp
|
||||
toolkit/tbytevectorlist.cpp
|
||||
toolkit/tvariant.cpp
|
||||
toolkit/tbytevectorstream.cpp
|
||||
toolkit/tiostream.cpp
|
||||
toolkit/tfile.cpp
|
||||
toolkit/tfilestream.cpp
|
||||
toolkit/tdebug.cpp
|
||||
toolkit/tpicturetype.cpp
|
||||
toolkit/tpropertymap.cpp
|
||||
toolkit/trefcounter.cpp
|
||||
toolkit/tdebuglistener.cpp
|
||||
toolkit/tzlib.cpp
|
||||
toolkit/tversionnumber.cpp
|
||||
)
|
||||
|
||||
if(NOT WIN32)
|
||||
set(unicode_SRCS
|
||||
toolkit/unicode.cpp
|
||||
)
|
||||
endif()
|
||||
|
||||
if(HAVE_ZLIB_SOURCE)
|
||||
set(zlib_SRCS
|
||||
${ZLIB_SOURCE}/adler32.c
|
||||
${ZLIB_SOURCE}/crc32.c
|
||||
${ZLIB_SOURCE}/inffast.c
|
||||
${ZLIB_SOURCE}/inflate.c
|
||||
${ZLIB_SOURCE}/inftrees.c
|
||||
${ZLIB_SOURCE}/zutil.c
|
||||
)
|
||||
endif()
|
||||
|
||||
set(tag_LIB_SRCS
|
||||
${mpeg_SRCS} ${id3v1_SRCS} ${id3v2_SRCS} ${frames_SRCS} ${ogg_SRCS}
|
||||
${vorbis_SRCS} ${oggflacs_SRCS} ${mpc_SRCS} ${ape_SRCS} ${toolkit_SRCS} ${flacs_SRCS}
|
||||
${wavpack_SRCS} ${speex_SRCS} ${trueaudio_SRCS} ${riff_SRCS} ${aiff_SRCS} ${wav_SRCS}
|
||||
${asf_SRCS} ${mp4_SRCS} ${mod_SRCS} ${s3m_SRCS} ${it_SRCS} ${xm_SRCS} ${opus_SRCS}
|
||||
${unicode_SRCS} ${zlib_SRCS}
|
||||
${dsf_SRCS} ${dsdiff_SRCS}
|
||||
tag.cpp
|
||||
tagunion.cpp
|
||||
fileref.cpp
|
||||
audioproperties.cpp
|
||||
tagutils.cpp
|
||||
)
|
||||
|
||||
add_library(tag ${tag_LIB_SRCS} ${tag_HDRS})
|
||||
|
||||
if(ZLIB_FOUND)
|
||||
target_link_libraries(tag ${ZLIB_LIBRARIES})
|
||||
endif()
|
||||
target_include_directories(tag INTERFACE
|
||||
$<INSTALL_INTERFACE:include>
|
||||
$<INSTALL_INTERFACE:include/taglib${TAGLIB_INSTALL_SUFFIX}>
|
||||
)
|
||||
|
||||
target_link_libraries(tag
|
||||
PRIVATE $<$<TARGET_EXISTS:utf8::cpp>:utf8::cpp>
|
||||
$<$<TARGET_EXISTS:ZLIB::ZLIB>:ZLIB::ZLIB>
|
||||
)
|
||||
|
||||
set_target_properties(tag PROPERTIES
|
||||
VERSION ${TAGLIB_SOVERSION_MAJOR}.${TAGLIB_SOVERSION_MINOR}.${TAGLIB_SOVERSION_PATCH}
|
||||
SOVERSION ${TAGLIB_SOVERSION_MAJOR}
|
||||
INSTALL_NAME_DIR ${LIB_INSTALL_DIR}
|
||||
INSTALL_NAME_DIR ${CMAKE_INSTALL_FULL_LIBDIR}
|
||||
DEFINE_SYMBOL MAKE_TAGLIB_LIB
|
||||
LINK_INTERFACE_LIBRARIES ""
|
||||
INTERFACE_LINK_LIBRARIES "${ZLIB_INTERFACE_LINK_LIBRARIES}"
|
||||
PUBLIC_HEADER "${tag_HDRS}"
|
||||
)
|
||||
if(VISIBILITY_HIDDEN)
|
||||
set_target_properties(tag PROPERTIES C_VISIBILITY_PRESET hidden)
|
||||
set_target_properties(tag PROPERTIES CXX_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()
|
||||
|
||||
if(TAGLIB_INSTALL_SUFFIX)
|
||||
if(BUILD_SHARED_LIBS)
|
||||
set(TAGLIB_LIBRARY_SUFFIX "${TAGLIB_INSTALL_SUFFIX}${CMAKE_SHARED_LIBRARY_SUFFIX}")
|
||||
else()
|
||||
set(TAGLIB_LIBRARY_SUFFIX "${TAGLIB_INSTALL_SUFFIX}${CMAKE_STATIC_LIBRARY_SUFFIX}")
|
||||
endif()
|
||||
set_target_properties(tag PROPERTIES SUFFIX ${TAGLIB_LIBRARY_SUFFIX})
|
||||
endif()
|
||||
|
||||
install(TARGETS tag
|
||||
EXPORT taglibTargets
|
||||
FRAMEWORK DESTINATION ${FRAMEWORK_INSTALL_DIR}
|
||||
LIBRARY DESTINATION ${LIB_INSTALL_DIR}
|
||||
RUNTIME DESTINATION ${BIN_INSTALL_DIR}
|
||||
ARCHIVE DESTINATION ${LIB_INSTALL_DIR}
|
||||
PUBLIC_HEADER DESTINATION ${INCLUDE_INSTALL_DIR}/taglib
|
||||
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
|
||||
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||
PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/taglib${TAGLIB_INSTALL_SUFFIX}
|
||||
)
|
||||
|
||||
configure_package_config_file(
|
||||
"${PROJECT_SOURCE_DIR}/taglib-config.cmake.in"
|
||||
"${PROJECT_BINARY_DIR}/taglib-config.cmake"
|
||||
INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/taglib${TAGLIB_INSTALL_SUFFIX}
|
||||
)
|
||||
|
||||
write_basic_package_version_file(
|
||||
"${PROJECT_BINARY_DIR}/taglib-config-version.cmake"
|
||||
VERSION "${TAGLIB_LIB_VERSION_STRING}"
|
||||
COMPATIBILITY AnyNewerVersion
|
||||
)
|
||||
|
||||
install(EXPORT taglibTargets
|
||||
FILE taglib-targets.cmake
|
||||
NAMESPACE TagLib::
|
||||
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/taglib${TAGLIB_INSTALL_SUFFIX}
|
||||
)
|
||||
|
||||
install(FILES "${PROJECT_BINARY_DIR}/taglib-config.cmake"
|
||||
"${PROJECT_BINARY_DIR}/taglib-config-version.cmake"
|
||||
DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/taglib${TAGLIB_INSTALL_SUFFIX}
|
||||
)
|
||||
|
||||
@@ -49,8 +49,8 @@ APE Tag Version 2.000 (with header, recommended):
|
||||
|
||||
APE tag items should be sorted ascending by size. When streaming, parts of the
|
||||
APE tag may be dropped to reduce the danger of drop outs between tracks. This
|
||||
is not required, but is strongly recommended. It would be desirable for the i
|
||||
tems to be sorted by importance / size, but this is not feasible. This
|
||||
is not required, but is strongly recommended. It would be desirable for the
|
||||
items to be sorted by importance / size, but this is not feasible. This
|
||||
convention should only be broken when adding less important small items and it
|
||||
is not desirable to rewrite the entire tag. An APE tag at the end of a file
|
||||
(the recommended location) must have at least a footer; an APE tag at the
|
||||
|
||||
@@ -31,16 +31,14 @@
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#include <tbytevector.h>
|
||||
#include <tstring.h>
|
||||
#include <tdebug.h>
|
||||
#include <tagunion.h>
|
||||
#include <id3v1tag.h>
|
||||
#include <id3v2header.h>
|
||||
#include <tpropertymap.h>
|
||||
|
||||
#include "apefile.h"
|
||||
|
||||
#include "tdebug.h"
|
||||
#include "tpropertymap.h"
|
||||
#include "id3v1tag.h"
|
||||
#include "id3v2header.h"
|
||||
#include "tagunion.h"
|
||||
#include "tagutils.h"
|
||||
#include "apetag.h"
|
||||
#include "apefooter.h"
|
||||
|
||||
@@ -49,57 +47,44 @@ using namespace TagLib;
|
||||
namespace
|
||||
{
|
||||
enum { ApeAPEIndex = 0, ApeID3v1Index = 1 };
|
||||
}
|
||||
} // namespace
|
||||
|
||||
class APE::File::FilePrivate
|
||||
{
|
||||
public:
|
||||
FilePrivate() :
|
||||
APELocation(-1),
|
||||
APESize(0),
|
||||
ID3v1Location(-1),
|
||||
ID3v2Header(0),
|
||||
ID3v2Location(-1),
|
||||
ID3v2Size(0),
|
||||
properties(0),
|
||||
hasAPE(false),
|
||||
hasID3v1(false),
|
||||
hasID3v2(false) {}
|
||||
offset_t APELocation { -1 };
|
||||
long APESize { 0 };
|
||||
|
||||
~FilePrivate()
|
||||
{
|
||||
delete ID3v2Header;
|
||||
delete properties;
|
||||
}
|
||||
offset_t ID3v1Location { -1 };
|
||||
|
||||
long APELocation;
|
||||
uint APESize;
|
||||
|
||||
long ID3v1Location;
|
||||
|
||||
ID3v2::Header *ID3v2Header;
|
||||
long ID3v2Location;
|
||||
uint ID3v2Size;
|
||||
std::unique_ptr<ID3v2::Header> ID3v2Header;
|
||||
offset_t ID3v2Location { -1 };
|
||||
long ID3v2Size { 0 };
|
||||
|
||||
TagUnion tag;
|
||||
|
||||
Properties *properties;
|
||||
|
||||
// These indicate whether the file *on disk* has these tags, not if
|
||||
// this data structure does. This is used in computing offsets.
|
||||
|
||||
bool hasAPE;
|
||||
bool hasID3v1;
|
||||
bool hasID3v2;
|
||||
std::unique_ptr<Properties> 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 ") >= 0);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// public members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
APE::File::File(FileName file, bool readProperties, Properties::ReadStyle) :
|
||||
TagLib::File(file),
|
||||
d(new FilePrivate())
|
||||
d(std::make_unique<FilePrivate>())
|
||||
{
|
||||
if(isOpen())
|
||||
read(readProperties);
|
||||
@@ -107,16 +92,13 @@ APE::File::File(FileName file, bool readProperties, Properties::ReadStyle) :
|
||||
|
||||
APE::File::File(IOStream *stream, bool readProperties, Properties::ReadStyle) :
|
||||
TagLib::File(stream),
|
||||
d(new FilePrivate())
|
||||
d(std::make_unique<FilePrivate>())
|
||||
{
|
||||
if(isOpen())
|
||||
read(readProperties);
|
||||
}
|
||||
|
||||
APE::File::~File()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
APE::File::~File() = default;
|
||||
|
||||
TagLib::Tag *APE::File::tag() const
|
||||
{
|
||||
@@ -125,31 +107,25 @@ TagLib::Tag *APE::File::tag() const
|
||||
|
||||
PropertyMap APE::File::properties() const
|
||||
{
|
||||
if(d->hasAPE)
|
||||
return d->tag.access<APE::Tag>(ApeAPEIndex, false)->properties();
|
||||
if(d->hasID3v1)
|
||||
return d->tag.access<ID3v1::Tag>(ApeID3v1Index, false)->properties();
|
||||
return PropertyMap();
|
||||
return d->tag.properties();
|
||||
}
|
||||
|
||||
void APE::File::removeUnsupportedProperties(const StringList &properties)
|
||||
{
|
||||
if(d->hasAPE)
|
||||
d->tag.access<APE::Tag>(ApeAPEIndex, false)->removeUnsupportedProperties(properties);
|
||||
if(d->hasID3v1)
|
||||
d->tag.access<ID3v1::Tag>(ApeID3v1Index, false)->removeUnsupportedProperties(properties);
|
||||
d->tag.removeUnsupportedProperties(properties);
|
||||
}
|
||||
|
||||
PropertyMap APE::File::setProperties(const PropertyMap &properties)
|
||||
{
|
||||
if(d->hasID3v1)
|
||||
d->tag.access<ID3v1::Tag>(ApeID3v1Index, false)->setProperties(properties);
|
||||
return d->tag.access<APE::Tag>(ApeAPEIndex, true)->setProperties(properties);
|
||||
if(ID3v1Tag())
|
||||
ID3v1Tag()->setProperties(properties);
|
||||
|
||||
return APETag(true)->setProperties(properties);
|
||||
}
|
||||
|
||||
APE::Properties *APE::File::audioProperties() const
|
||||
{
|
||||
return d->properties;
|
||||
return d->properties.get();
|
||||
}
|
||||
|
||||
bool APE::File::save()
|
||||
@@ -161,64 +137,67 @@ bool APE::File::save()
|
||||
|
||||
// Update ID3v1 tag
|
||||
|
||||
if(ID3v1Tag()) {
|
||||
if(d->hasID3v1) {
|
||||
if(ID3v1Tag() && !ID3v1Tag()->isEmpty()) {
|
||||
|
||||
// ID3v1 tag is not empty. Update the old one or create a new one.
|
||||
|
||||
if(d->ID3v1Location >= 0) {
|
||||
seek(d->ID3v1Location);
|
||||
writeBlock(ID3v1Tag()->render());
|
||||
}
|
||||
else {
|
||||
seek(0, End);
|
||||
d->ID3v1Location = tell();
|
||||
writeBlock(ID3v1Tag()->render());
|
||||
d->hasID3v1 = true;
|
||||
}
|
||||
|
||||
writeBlock(ID3v1Tag()->render());
|
||||
}
|
||||
else {
|
||||
if(d->hasID3v1) {
|
||||
removeBlock(d->ID3v1Location, 128);
|
||||
d->hasID3v1 = false;
|
||||
if(d->hasAPE) {
|
||||
if(d->APELocation > d->ID3v1Location)
|
||||
d->APELocation -= 128;
|
||||
}
|
||||
|
||||
// ID3v1 tag is empty. Remove the old one.
|
||||
|
||||
if(d->ID3v1Location >= 0) {
|
||||
truncate(d->ID3v1Location);
|
||||
d->ID3v1Location = -1;
|
||||
}
|
||||
}
|
||||
|
||||
// Update APE tag
|
||||
|
||||
if(APETag()) {
|
||||
if(d->hasAPE)
|
||||
insert(APETag()->render(), d->APELocation, d->APESize);
|
||||
else {
|
||||
if(d->hasID3v1) {
|
||||
insert(APETag()->render(), d->ID3v1Location, 0);
|
||||
d->APESize = APETag()->footer()->completeTagSize();
|
||||
d->hasAPE = true;
|
||||
if(APETag() && !APETag()->isEmpty()) {
|
||||
|
||||
// APE tag is not empty. Update the old one or create a new one.
|
||||
|
||||
if(d->APELocation < 0) {
|
||||
if(d->ID3v1Location >= 0)
|
||||
d->APELocation = d->ID3v1Location;
|
||||
d->ID3v1Location += d->APESize;
|
||||
}
|
||||
else {
|
||||
seek(0, End);
|
||||
d->APELocation = tell();
|
||||
writeBlock(APETag()->render());
|
||||
d->APESize = APETag()->footer()->completeTagSize();
|
||||
d->hasAPE = true;
|
||||
}
|
||||
else
|
||||
d->APELocation = length();
|
||||
}
|
||||
|
||||
const ByteVector data = APETag()->render();
|
||||
insert(data, d->APELocation, d->APESize);
|
||||
|
||||
if(d->ID3v1Location >= 0)
|
||||
d->ID3v1Location += (static_cast<long>(data.size()) - d->APESize);
|
||||
|
||||
d->APESize = data.size();
|
||||
}
|
||||
else {
|
||||
if(d->hasAPE) {
|
||||
|
||||
// APE tag is empty. Remove the old one.
|
||||
|
||||
if(d->APELocation >= 0) {
|
||||
removeBlock(d->APELocation, d->APESize);
|
||||
d->hasAPE = false;
|
||||
if(d->hasID3v1) {
|
||||
if(d->ID3v1Location > d->APELocation) {
|
||||
d->ID3v1Location -= d->APESize;
|
||||
}
|
||||
}
|
||||
|
||||
if(d->ID3v1Location >= 0)
|
||||
d->ID3v1Location -= d->APESize;
|
||||
|
||||
d->APELocation = -1;
|
||||
d->APESize = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
|
||||
ID3v1::Tag *APE::File::ID3v1Tag(bool create)
|
||||
@@ -233,27 +212,24 @@ APE::Tag *APE::File::APETag(bool create)
|
||||
|
||||
void APE::File::strip(int tags)
|
||||
{
|
||||
if(tags & ID3v1) {
|
||||
d->tag.set(ApeID3v1Index, 0);
|
||||
if(tags & ID3v1)
|
||||
d->tag.set(ApeID3v1Index, nullptr);
|
||||
|
||||
if(tags & APE)
|
||||
d->tag.set(ApeAPEIndex, nullptr);
|
||||
|
||||
if(!ID3v1Tag())
|
||||
APETag(true);
|
||||
}
|
||||
|
||||
if(tags & APE) {
|
||||
d->tag.set(ApeAPEIndex, 0);
|
||||
|
||||
if(!ID3v1Tag())
|
||||
APETag(true);
|
||||
}
|
||||
}
|
||||
|
||||
bool APE::File::hasAPETag() const
|
||||
{
|
||||
return d->hasAPE;
|
||||
return (d->APELocation >= 0);
|
||||
}
|
||||
|
||||
bool APE::File::hasID3v1Tag() const
|
||||
{
|
||||
return d->hasID3v1;
|
||||
return (d->ID3v1Location >= 0);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -264,52 +240,48 @@ void APE::File::read(bool readProperties)
|
||||
{
|
||||
// Look for an ID3v2 tag
|
||||
|
||||
d->ID3v2Location = findID3v2();
|
||||
d->ID3v2Location = Utils::findID3v2(this);
|
||||
|
||||
if(d->ID3v2Location >= 0) {
|
||||
seek(d->ID3v2Location);
|
||||
d->ID3v2Header = new ID3v2::Header(readBlock(ID3v2::Header::size()));
|
||||
d->ID3v2Header = std::make_unique<ID3v2::Header>(readBlock(ID3v2::Header::size()));
|
||||
d->ID3v2Size = d->ID3v2Header->completeTagSize();
|
||||
d->hasID3v2 = true;
|
||||
}
|
||||
|
||||
// Look for an ID3v1 tag
|
||||
|
||||
d->ID3v1Location = findID3v1();
|
||||
d->ID3v1Location = Utils::findID3v1(this);
|
||||
|
||||
if(d->ID3v1Location >= 0) {
|
||||
if(d->ID3v1Location >= 0)
|
||||
d->tag.set(ApeID3v1Index, new ID3v1::Tag(this, d->ID3v1Location));
|
||||
d->hasID3v1 = true;
|
||||
}
|
||||
|
||||
// Look for an APE tag
|
||||
|
||||
d->APELocation = findAPE();
|
||||
d->APELocation = Utils::findAPE(this, d->ID3v1Location);
|
||||
|
||||
if(d->APELocation >= 0) {
|
||||
d->tag.set(ApeAPEIndex, new APE::Tag(this, d->APELocation));
|
||||
d->APESize = APETag()->footer()->completeTagSize();
|
||||
d->APELocation = d->APELocation + APETag()->footer()->size() - d->APESize;
|
||||
d->hasAPE = true;
|
||||
d->APELocation = d->APELocation + APE::Footer::size() - d->APESize;
|
||||
}
|
||||
|
||||
if(!d->hasID3v1)
|
||||
if(d->ID3v1Location < 0)
|
||||
APETag(true);
|
||||
|
||||
// Look for APE audio properties
|
||||
|
||||
if(readProperties) {
|
||||
|
||||
long streamLength;
|
||||
offset_t streamLength;
|
||||
|
||||
if(d->hasAPE)
|
||||
if(d->APELocation >= 0)
|
||||
streamLength = d->APELocation;
|
||||
else if(d->hasID3v1)
|
||||
else if(d->ID3v1Location >= 0)
|
||||
streamLength = d->ID3v1Location;
|
||||
else
|
||||
streamLength = length();
|
||||
|
||||
if(d->hasID3v2) {
|
||||
if(d->ID3v2Location >= 0) {
|
||||
seek(d->ID3v2Location + d->ID3v2Size);
|
||||
streamLength -= (d->ID3v2Location + d->ID3v2Size);
|
||||
}
|
||||
@@ -317,51 +289,6 @@ void APE::File::read(bool readProperties)
|
||||
seek(0);
|
||||
}
|
||||
|
||||
d->properties = new Properties(this, streamLength);
|
||||
d->properties = std::make_unique<Properties>(this, streamLength);
|
||||
}
|
||||
}
|
||||
|
||||
long APE::File::findAPE()
|
||||
{
|
||||
if(!isValid())
|
||||
return -1;
|
||||
|
||||
if(d->hasID3v1)
|
||||
seek(-160, End);
|
||||
else
|
||||
seek(-32, End);
|
||||
|
||||
long p = tell();
|
||||
|
||||
if(readBlock(8) == APE::Tag::fileIdentifier())
|
||||
return p;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
long APE::File::findID3v1()
|
||||
{
|
||||
if(!isValid())
|
||||
return -1;
|
||||
|
||||
seek(-128, End);
|
||||
long p = tell();
|
||||
|
||||
if(readBlock(3) == ID3v1::Tag::fileIdentifier())
|
||||
return p;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
long APE::File::findID3v2()
|
||||
{
|
||||
if(!isValid())
|
||||
return -1;
|
||||
|
||||
seek(0);
|
||||
|
||||
if(readBlock(3) == ID3v2::Header::fileIdentifier())
|
||||
return 0;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -107,50 +107,50 @@ namespace TagLib {
|
||||
/*!
|
||||
* Destroys this instance of the File.
|
||||
*/
|
||||
virtual ~File();
|
||||
~File() override;
|
||||
|
||||
File(const File &) = delete;
|
||||
File &operator=(const File &) = delete;
|
||||
|
||||
/*!
|
||||
* Returns the Tag for this file. This will be an APE tag, an ID3v1 tag
|
||||
* or a combination of the two.
|
||||
*/
|
||||
virtual TagLib::Tag *tag() const;
|
||||
TagLib::Tag *tag() const override;
|
||||
|
||||
/*!
|
||||
* Implements the unified property interface -- export function.
|
||||
* If the file contains both an APE and an ID3v1 tag, only APE
|
||||
* will be converted to the PropertyMap.
|
||||
*/
|
||||
PropertyMap properties() const;
|
||||
PropertyMap properties() const override;
|
||||
|
||||
/*!
|
||||
* Removes unsupported properties. Forwards to the actual Tag's
|
||||
* removeUnsupportedProperties() function.
|
||||
*/
|
||||
void removeUnsupportedProperties(const StringList &properties);
|
||||
void removeUnsupportedProperties(const StringList &properties) override;
|
||||
|
||||
/*!
|
||||
* Implements the unified property interface -- import function.
|
||||
* Creates an APEv2 tag if necessary. A potentially existing ID3v1
|
||||
* tag will be updated as well.
|
||||
*/
|
||||
PropertyMap setProperties(const PropertyMap &);
|
||||
PropertyMap setProperties(const PropertyMap &) override;
|
||||
|
||||
/*!
|
||||
* Returns the APE::Properties for this file. If no audio properties
|
||||
* were read then this will return a null pointer.
|
||||
*/
|
||||
virtual Properties *audioProperties() const;
|
||||
Properties *audioProperties() const override;
|
||||
|
||||
/*!
|
||||
* Saves the file.
|
||||
*
|
||||
* \note According to the official Monkey's Audio SDK, an APE file
|
||||
* can only have either ID3V1 or APE tags, so a parameter is used here.
|
||||
*
|
||||
* \warning In the current implementation, it's dangerous to call save()
|
||||
* repeatedly. At worst it will corrupt the file.
|
||||
*/
|
||||
virtual bool save();
|
||||
bool save() override;
|
||||
|
||||
/*!
|
||||
* Returns a pointer to the ID3v1 tag of the file.
|
||||
@@ -214,19 +214,23 @@ namespace TagLib {
|
||||
*/
|
||||
bool hasID3v1Tag() const;
|
||||
|
||||
private:
|
||||
File(const File &);
|
||||
File &operator=(const File &);
|
||||
/*!
|
||||
* 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:
|
||||
void read(bool readProperties);
|
||||
long findAPE();
|
||||
long findID3v1();
|
||||
long findID3v2();
|
||||
|
||||
class FilePrivate;
|
||||
FilePrivate *d;
|
||||
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
|
||||
std::unique_ptr<FilePrivate> d;
|
||||
};
|
||||
}
|
||||
}
|
||||
} // namespace APE
|
||||
} // namespace TagLib
|
||||
|
||||
#endif
|
||||
|
||||
@@ -24,77 +24,59 @@
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#include <iostream>
|
||||
#include <bitset>
|
||||
|
||||
#include <tstring.h>
|
||||
#include <tdebug.h>
|
||||
|
||||
#include "apefooter.h"
|
||||
|
||||
#include <bitset>
|
||||
|
||||
using namespace TagLib;
|
||||
using namespace APE;
|
||||
|
||||
class APE::Footer::FooterPrivate
|
||||
{
|
||||
public:
|
||||
FooterPrivate() : version(0),
|
||||
footerPresent(true),
|
||||
headerPresent(false),
|
||||
isHeader(false),
|
||||
itemCount(0),
|
||||
tagSize(0) {}
|
||||
unsigned int version { 0 };
|
||||
|
||||
~FooterPrivate() {}
|
||||
bool footerPresent { true };
|
||||
bool headerPresent { false };
|
||||
|
||||
uint version;
|
||||
bool isHeader { false };
|
||||
|
||||
bool footerPresent;
|
||||
bool headerPresent;
|
||||
|
||||
bool isHeader;
|
||||
|
||||
uint itemCount;
|
||||
uint tagSize;
|
||||
|
||||
static const uint size = 32;
|
||||
unsigned int itemCount { 0 };
|
||||
unsigned int tagSize { 0 };
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// static members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
TagLib::uint APE::Footer::size()
|
||||
unsigned int APE::Footer::size()
|
||||
{
|
||||
return FooterPrivate::size;
|
||||
return 32;
|
||||
}
|
||||
|
||||
ByteVector APE::Footer::fileIdentifier()
|
||||
{
|
||||
return ByteVector::fromCString("APETAGEX");
|
||||
return ByteVector("APETAGEX");
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// public members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
APE::Footer::Footer()
|
||||
APE::Footer::Footer() :
|
||||
d(std::make_unique<FooterPrivate>())
|
||||
{
|
||||
d = new FooterPrivate;
|
||||
}
|
||||
|
||||
APE::Footer::Footer(const ByteVector &data)
|
||||
APE::Footer::Footer(const ByteVector &data) :
|
||||
d(std::make_unique<FooterPrivate>())
|
||||
{
|
||||
d = new FooterPrivate;
|
||||
parse(data);
|
||||
}
|
||||
|
||||
APE::Footer::~Footer()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
APE::Footer::~Footer() = default;
|
||||
|
||||
TagLib::uint APE::Footer::version() const
|
||||
unsigned int APE::Footer::version() const
|
||||
{
|
||||
return d->version;
|
||||
}
|
||||
@@ -119,30 +101,29 @@ void APE::Footer::setHeaderPresent(bool b) const
|
||||
d->headerPresent = b;
|
||||
}
|
||||
|
||||
TagLib::uint APE::Footer::itemCount() const
|
||||
unsigned int APE::Footer::itemCount() const
|
||||
{
|
||||
return d->itemCount;
|
||||
}
|
||||
|
||||
void APE::Footer::setItemCount(uint s)
|
||||
void APE::Footer::setItemCount(unsigned int s)
|
||||
{
|
||||
d->itemCount = s;
|
||||
}
|
||||
|
||||
TagLib::uint APE::Footer::tagSize() const
|
||||
unsigned int APE::Footer::tagSize() const
|
||||
{
|
||||
return d->tagSize;
|
||||
}
|
||||
|
||||
TagLib::uint APE::Footer::completeTagSize() const
|
||||
unsigned int APE::Footer::completeTagSize() const
|
||||
{
|
||||
if(d->headerPresent)
|
||||
return d->tagSize + d->size;
|
||||
else
|
||||
return d->tagSize;
|
||||
return d->tagSize + size();
|
||||
return d->tagSize;
|
||||
}
|
||||
|
||||
void APE::Footer::setTagSize(uint s)
|
||||
void APE::Footer::setTagSize(unsigned int s)
|
||||
{
|
||||
d->tagSize = s;
|
||||
}
|
||||
@@ -154,14 +135,14 @@ void APE::Footer::setData(const ByteVector &data)
|
||||
|
||||
ByteVector APE::Footer::renderFooter() const
|
||||
{
|
||||
return render(false);
|
||||
return render(false);
|
||||
}
|
||||
|
||||
ByteVector APE::Footer::renderHeader() const
|
||||
{
|
||||
if (!d->headerPresent) return ByteVector();
|
||||
|
||||
return render(true);
|
||||
if(!d->headerPresent)
|
||||
return ByteVector();
|
||||
return render(true);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -226,7 +207,7 @@ ByteVector APE::Footer::render(bool isHeader) const
|
||||
flags[30] = false; // footer is always present
|
||||
flags[29] = isHeader;
|
||||
|
||||
v.append(ByteVector::fromUInt(flags.to_ulong(), false));
|
||||
v.append(ByteVector::fromUInt(static_cast<unsigned int>(flags.to_ulong()), false));
|
||||
|
||||
// add the reserved 64bit
|
||||
|
||||
|
||||
@@ -61,10 +61,13 @@ namespace TagLib {
|
||||
*/
|
||||
virtual ~Footer();
|
||||
|
||||
Footer(const Footer &) = delete;
|
||||
Footer &operator=(const Footer &) = delete;
|
||||
|
||||
/*!
|
||||
* Returns the version number. (Note: This is the 1000 or 2000.)
|
||||
*/
|
||||
uint version() const;
|
||||
unsigned int version() const;
|
||||
|
||||
/*!
|
||||
* Returns true if a header is present in the tag.
|
||||
@@ -89,13 +92,13 @@ namespace TagLib {
|
||||
/*!
|
||||
* Returns the number of items in the tag.
|
||||
*/
|
||||
uint itemCount() const;
|
||||
unsigned int itemCount() const;
|
||||
|
||||
/*!
|
||||
* Set the item count to \a s.
|
||||
* \see itemCount()
|
||||
*/
|
||||
void setItemCount(uint s);
|
||||
void setItemCount(unsigned int s);
|
||||
|
||||
/*!
|
||||
* Returns the tag size in bytes. This is the size of the frame content and footer.
|
||||
@@ -103,7 +106,7 @@ namespace TagLib {
|
||||
*
|
||||
* \see completeTagSize()
|
||||
*/
|
||||
uint tagSize() const;
|
||||
unsigned int tagSize() const;
|
||||
|
||||
/*!
|
||||
* Returns the tag size, including if present, the header
|
||||
@@ -111,18 +114,18 @@ namespace TagLib {
|
||||
*
|
||||
* \see tagSize()
|
||||
*/
|
||||
uint completeTagSize() const;
|
||||
unsigned int completeTagSize() const;
|
||||
|
||||
/*!
|
||||
* Set the tag size to \a s.
|
||||
* \see tagSize()
|
||||
*/
|
||||
void setTagSize(uint s);
|
||||
void setTagSize(unsigned int s);
|
||||
|
||||
/*!
|
||||
* Returns the size of the footer. Presently this is always 32 bytes.
|
||||
*/
|
||||
static uint size();
|
||||
static unsigned int size();
|
||||
|
||||
/*!
|
||||
* Returns the string used to identify an APE tag inside of a file.
|
||||
@@ -160,14 +163,12 @@ namespace TagLib {
|
||||
ByteVector render(bool isHeader) const;
|
||||
|
||||
private:
|
||||
Footer(const Footer &);
|
||||
Footer &operator=(const Footer &);
|
||||
|
||||
class FooterPrivate;
|
||||
FooterPrivate *d;
|
||||
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
|
||||
std::unique_ptr<FooterPrivate> d;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
} // namespace APE
|
||||
} // namespace TagLib
|
||||
|
||||
#endif
|
||||
|
||||
@@ -23,76 +23,75 @@
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#include <tbytevectorlist.h>
|
||||
#include <tdebug.h>
|
||||
|
||||
#include "apeitem.h"
|
||||
|
||||
#include <utility>
|
||||
#include <numeric>
|
||||
|
||||
#include "tdebug.h"
|
||||
|
||||
using namespace TagLib;
|
||||
using namespace APE;
|
||||
|
||||
class APE::Item::ItemPrivate
|
||||
{
|
||||
public:
|
||||
ItemPrivate() : type(Text), readOnly(false) {}
|
||||
|
||||
Item::ItemTypes type;
|
||||
Item::ItemTypes type { Text };
|
||||
String key;
|
||||
ByteVector value;
|
||||
StringList text;
|
||||
bool readOnly;
|
||||
bool readOnly { false };
|
||||
};
|
||||
|
||||
APE::Item::Item()
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// public members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
APE::Item::Item() :
|
||||
d(std::make_unique<ItemPrivate>())
|
||||
{
|
||||
d = new ItemPrivate;
|
||||
}
|
||||
|
||||
APE::Item::Item(const String &key, const String &value)
|
||||
APE::Item::Item(const String &key, const StringList &values) :
|
||||
d(std::make_unique<ItemPrivate>())
|
||||
{
|
||||
d = new ItemPrivate;
|
||||
d->key = key;
|
||||
d->text.append(value);
|
||||
}
|
||||
|
||||
APE::Item::Item(const String &key, const StringList &values)
|
||||
{
|
||||
d = new ItemPrivate;
|
||||
d->key = key;
|
||||
d->text = values;
|
||||
}
|
||||
|
||||
APE::Item::Item(const String &key, const ByteVector &value, bool binary)
|
||||
APE::Item::Item(const String &key, const ByteVector &value, bool binary) :
|
||||
d(std::make_unique<ItemPrivate>())
|
||||
{
|
||||
d = new ItemPrivate;
|
||||
d->key = key;
|
||||
if(binary) {
|
||||
d->type = Binary;
|
||||
d->value = value;
|
||||
}
|
||||
else
|
||||
else {
|
||||
d->text.append(value);
|
||||
}
|
||||
}
|
||||
|
||||
APE::Item::Item(const Item &item)
|
||||
APE::Item::Item(const Item &item) :
|
||||
d(std::make_unique<ItemPrivate>(*item.d))
|
||||
{
|
||||
d = new ItemPrivate(*item.d);
|
||||
}
|
||||
|
||||
APE::Item::~Item()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
APE::Item::~Item() = default;
|
||||
|
||||
Item &APE::Item::operator=(const Item &item)
|
||||
{
|
||||
if(&item != this) {
|
||||
delete d;
|
||||
d = new ItemPrivate(*item.d);
|
||||
}
|
||||
Item(item).swap(*this);
|
||||
return *this;
|
||||
}
|
||||
|
||||
void APE::Item::swap(Item &item) noexcept
|
||||
{
|
||||
using std::swap;
|
||||
|
||||
swap(d, item.d);
|
||||
}
|
||||
|
||||
void APE::Item::setReadOnly(bool readOnly)
|
||||
{
|
||||
d->readOnly = readOnly;
|
||||
@@ -130,14 +129,6 @@ void APE::Item::setBinaryData(const ByteVector &value)
|
||||
d->text.clear();
|
||||
}
|
||||
|
||||
ByteVector APE::Item::value() const
|
||||
{
|
||||
// This seems incorrect as it won't be actually rendering the value to keep it
|
||||
// up to date.
|
||||
|
||||
return d->value;
|
||||
}
|
||||
|
||||
void APE::Item::setKey(const String &key)
|
||||
{
|
||||
d->key = key;
|
||||
@@ -173,17 +164,14 @@ void APE::Item::appendValues(const StringList &values)
|
||||
|
||||
int APE::Item::size() const
|
||||
{
|
||||
// SFB: Why is d->key.size() used when size() returns the length in UniChars and not UTF-8?
|
||||
int result = 8 + d->key.size() /* d->key.data(String::UTF8).size() */ + 1;
|
||||
switch (d->type) {
|
||||
int result = 8 + d->key.size() + 1;
|
||||
switch(d->type) {
|
||||
case Text:
|
||||
if(d->text.size()) {
|
||||
StringList::ConstIterator it = d->text.begin();
|
||||
|
||||
result += it->data(String::UTF8).size();
|
||||
it++;
|
||||
for(; it != d->text.end(); ++it)
|
||||
result += 1 + it->data(String::UTF8).size();
|
||||
if(!d->text.isEmpty()) {
|
||||
result = std::accumulate(d->text.cbegin(), d->text.cend(), result,
|
||||
[](int sz, const String &t) {
|
||||
return sz + 1 + t.data(String::UTF8).size();
|
||||
}) - 1;
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -195,11 +183,6 @@ int APE::Item::size() const
|
||||
return result;
|
||||
}
|
||||
|
||||
StringList APE::Item::toStringList() const
|
||||
{
|
||||
return d->text;
|
||||
}
|
||||
|
||||
StringList APE::Item::values() const
|
||||
{
|
||||
return d->text;
|
||||
@@ -209,8 +192,7 @@ String APE::Item::toString() const
|
||||
{
|
||||
if(d->type == Text && !isEmpty())
|
||||
return d->text.front();
|
||||
else
|
||||
return String::null;
|
||||
return String();
|
||||
}
|
||||
|
||||
bool APE::Item::isEmpty() const
|
||||
@@ -219,9 +201,7 @@ bool APE::Item::isEmpty() const
|
||||
case Text:
|
||||
if(d->text.isEmpty())
|
||||
return true;
|
||||
if(d->text.size() == 1 && d->text.front().isEmpty())
|
||||
return true;
|
||||
return false;
|
||||
return d->text.size() == 1 && d->text.front().isEmpty();
|
||||
case Binary:
|
||||
case Locator:
|
||||
return d->value.isEmpty();
|
||||
@@ -239,50 +219,52 @@ void APE::Item::parse(const ByteVector &data)
|
||||
return;
|
||||
}
|
||||
|
||||
const uint valueLength = data.toUInt(0, false);
|
||||
const uint flags = data.toUInt(4, false);
|
||||
const unsigned int valueLength = data.toUInt(0, false);
|
||||
const unsigned int flags = data.toUInt(4, false);
|
||||
|
||||
d->key = String(data.mid(8), String::UTF8);
|
||||
// An item key can contain ASCII characters from 0x20 up to 0x7E, not UTF-8.
|
||||
// We assume that the validity of the given key has been checked.
|
||||
|
||||
const ByteVector value = data.mid(8 + d->key.size() + 1, valueLength);
|
||||
d->key = String(&data[8], String::Latin1);
|
||||
|
||||
const ByteVector val = data.mid(8 + d->key.size() + 1, valueLength);
|
||||
|
||||
setReadOnly(flags & 1);
|
||||
setType(ItemTypes((flags >> 1) & 3));
|
||||
setType(static_cast<ItemTypes>((flags >> 1) & 3));
|
||||
|
||||
if(Text == d->type)
|
||||
d->text = StringList(ByteVectorList::split(value, '\0'), String::UTF8);
|
||||
if(Text == d->type)
|
||||
d->text = StringList(ByteVectorList::split(val, '\0'), String::UTF8);
|
||||
else
|
||||
d->value = value;
|
||||
d->value = val;
|
||||
}
|
||||
|
||||
ByteVector APE::Item::render() const
|
||||
{
|
||||
ByteVector data;
|
||||
TagLib::uint flags = ((d->readOnly) ? 1 : 0) | (d->type << 1);
|
||||
ByteVector value;
|
||||
unsigned int flags = ((d->readOnly) ? 1 : 0) | (d->type << 1);
|
||||
ByteVector val;
|
||||
|
||||
if(isEmpty())
|
||||
return data;
|
||||
|
||||
if(d->type == Text) {
|
||||
StringList::ConstIterator it = d->text.begin();
|
||||
auto it = d->text.cbegin();
|
||||
|
||||
value.append(it->data(String::UTF8));
|
||||
it++;
|
||||
for(; it != d->text.end(); ++it) {
|
||||
value.append('\0');
|
||||
value.append(it->data(String::UTF8));
|
||||
val.append(it->data(String::UTF8));
|
||||
for(it = std::next(it); it != d->text.cend(); ++it) {
|
||||
val.append('\0');
|
||||
val.append(it->data(String::UTF8));
|
||||
}
|
||||
d->value = value;
|
||||
d->value = val;
|
||||
}
|
||||
else
|
||||
value.append(d->value);
|
||||
val.append(d->value);
|
||||
|
||||
data.append(ByteVector::fromUInt(value.size(), false));
|
||||
data.append(ByteVector::fromUInt(val.size(), false));
|
||||
data.append(ByteVector::fromUInt(flags, false));
|
||||
data.append(d->key.data(String::UTF8));
|
||||
data.append(d->key.data(String::Latin1));
|
||||
data.append(ByteVector('\0'));
|
||||
data.append(value);
|
||||
data.append(val);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
@@ -31,9 +31,7 @@
|
||||
#include "tstringlist.h"
|
||||
|
||||
namespace TagLib {
|
||||
|
||||
namespace APE {
|
||||
|
||||
//! An implementation of APE-items
|
||||
|
||||
/*!
|
||||
@@ -58,12 +56,6 @@ namespace TagLib {
|
||||
*/
|
||||
Item();
|
||||
|
||||
/*!
|
||||
* Constructs a text item with \a key and \a value.
|
||||
*/
|
||||
// BIC: Remove this, StringList has a constructor from a single string
|
||||
Item(const String &key, const String &value);
|
||||
|
||||
/*!
|
||||
* Constructs a text item with \a key and \a values.
|
||||
*/
|
||||
@@ -90,6 +82,11 @@ namespace TagLib {
|
||||
*/
|
||||
Item &operator=(const Item &item);
|
||||
|
||||
/*!
|
||||
* Exchanges the content of this item by the content of \a item.
|
||||
*/
|
||||
void swap(Item &item) noexcept;
|
||||
|
||||
/*!
|
||||
* Returns the key.
|
||||
*/
|
||||
@@ -107,11 +104,6 @@ namespace TagLib {
|
||||
*/
|
||||
void setBinaryData(const ByteVector &value);
|
||||
|
||||
#ifndef DO_NOT_DOCUMENT
|
||||
/* Remove in next binary incompatible release */
|
||||
ByteVector value() const;
|
||||
#endif
|
||||
|
||||
/*!
|
||||
* Sets the key for the item to \a key.
|
||||
*/
|
||||
@@ -128,7 +120,7 @@ namespace TagLib {
|
||||
* Sets the text value of the item to the list of values in \a value and clears
|
||||
* any previous contents.
|
||||
*
|
||||
* \see toStringList()
|
||||
* \see values()
|
||||
*/
|
||||
void setValues(const StringList &values);
|
||||
|
||||
@@ -142,7 +134,7 @@ namespace TagLib {
|
||||
/*!
|
||||
* Appends \a values to extend the current list of text values.
|
||||
*
|
||||
* \see toStringList()
|
||||
* \see values()
|
||||
*/
|
||||
void appendValues(const StringList &values);
|
||||
|
||||
@@ -158,11 +150,6 @@ namespace TagLib {
|
||||
*/
|
||||
String toString() const;
|
||||
|
||||
#ifndef DO_NOT_DOCUMENT
|
||||
/* Remove in next binary incompatible release */
|
||||
StringList toStringList() const;
|
||||
#endif
|
||||
|
||||
/*!
|
||||
* Returns the list of text values. If the data type is not \a Text, always
|
||||
* returns an empty StringList.
|
||||
@@ -190,11 +177,11 @@ namespace TagLib {
|
||||
bool isReadOnly() const;
|
||||
|
||||
/*!
|
||||
* Sets the type of the item to \a type.
|
||||
* Sets the type of the item to \a val.
|
||||
*
|
||||
* \see ItemTypes
|
||||
*/
|
||||
void setType(ItemTypes type);
|
||||
void setType(ItemTypes val);
|
||||
|
||||
/*!
|
||||
* Returns the type of the item.
|
||||
@@ -208,12 +195,10 @@ namespace TagLib {
|
||||
|
||||
private:
|
||||
class ItemPrivate;
|
||||
ItemPrivate *d;
|
||||
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
|
||||
std::unique_ptr<ItemPrivate> d;
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace APE
|
||||
} // namespace TagLib
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
@@ -27,70 +27,39 @@
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#include <tstring.h>
|
||||
#include <tdebug.h>
|
||||
#include <bitset>
|
||||
#include "id3v2tag.h"
|
||||
#include "apeproperties.h"
|
||||
|
||||
#include "tdebug.h"
|
||||
#include "apefile.h"
|
||||
#include "apetag.h"
|
||||
#include "apefooter.h"
|
||||
#include "apetag.h"
|
||||
|
||||
using namespace TagLib;
|
||||
|
||||
class APE::Properties::PropertiesPrivate
|
||||
{
|
||||
public:
|
||||
PropertiesPrivate() :
|
||||
length(0),
|
||||
bitrate(0),
|
||||
sampleRate(0),
|
||||
channels(0),
|
||||
version(0),
|
||||
bitsPerSample(0),
|
||||
sampleFrames(0) {}
|
||||
|
||||
int length;
|
||||
int bitrate;
|
||||
int sampleRate;
|
||||
int channels;
|
||||
int version;
|
||||
int bitsPerSample;
|
||||
uint sampleFrames;
|
||||
int length { 0 };
|
||||
int bitrate { 0 };
|
||||
int sampleRate { 0 };
|
||||
int channels { 0 };
|
||||
int version { 0 };
|
||||
int bitsPerSample { 0 };
|
||||
unsigned int sampleFrames { 0 };
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// public members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
APE::Properties::Properties(File *, ReadStyle style) :
|
||||
APE::Properties::Properties(File *file, offset_t streamLength, ReadStyle style) :
|
||||
AudioProperties(style),
|
||||
d(new PropertiesPrivate())
|
||||
{
|
||||
debug("APE::Properties::Properties() -- This constructor is no longer used.");
|
||||
}
|
||||
|
||||
APE::Properties::Properties(File *file, long streamLength, ReadStyle style) :
|
||||
AudioProperties(style),
|
||||
d(new PropertiesPrivate())
|
||||
d(std::make_unique<PropertiesPrivate>())
|
||||
{
|
||||
read(file, streamLength);
|
||||
}
|
||||
|
||||
APE::Properties::~Properties()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
|
||||
int APE::Properties::length() const
|
||||
{
|
||||
return lengthInSeconds();
|
||||
}
|
||||
|
||||
int APE::Properties::lengthInSeconds() const
|
||||
{
|
||||
return d->length / 1000;
|
||||
}
|
||||
APE::Properties::~Properties() = default;
|
||||
|
||||
int APE::Properties::lengthInMilliseconds() const
|
||||
{
|
||||
@@ -122,7 +91,7 @@ int APE::Properties::bitsPerSample() const
|
||||
return d->bitsPerSample;
|
||||
}
|
||||
|
||||
TagLib::uint APE::Properties::sampleFrames() const
|
||||
unsigned int APE::Properties::sampleFrames() const
|
||||
{
|
||||
return d->sampleFrames;
|
||||
}
|
||||
@@ -133,34 +102,34 @@ TagLib::uint APE::Properties::sampleFrames() const
|
||||
|
||||
namespace
|
||||
{
|
||||
inline int headerVersion(const ByteVector &header)
|
||||
int headerVersion(const ByteVector &header)
|
||||
{
|
||||
if(header.size() < 6 || !header.startsWith("MAC "))
|
||||
return -1;
|
||||
|
||||
return header.toUShort(4, false);
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void APE::Properties::read(File *file, long streamLength)
|
||||
void APE::Properties::read(File *file, offset_t streamLength)
|
||||
{
|
||||
// First, we assume that the file pointer is set at the first descriptor.
|
||||
long offset = file->tell();
|
||||
int version = headerVersion(file->readBlock(6));
|
||||
offset_t offset = file->tell();
|
||||
int vers = headerVersion(file->readBlock(6));
|
||||
|
||||
// Next, we look for the descriptor.
|
||||
if(version < 0) {
|
||||
if(vers < 0) {
|
||||
offset = file->find("MAC ", offset);
|
||||
file->seek(offset);
|
||||
version = headerVersion(file->readBlock(6));
|
||||
vers = headerVersion(file->readBlock(6));
|
||||
}
|
||||
|
||||
if(version < 0) {
|
||||
if(vers < 0) {
|
||||
debug("APE::Properties::read() -- APE descriptor not found");
|
||||
return;
|
||||
}
|
||||
|
||||
d->version = version;
|
||||
d->version = vers;
|
||||
|
||||
if(d->version >= 3980)
|
||||
analyzeCurrent(file);
|
||||
@@ -184,7 +153,7 @@ void APE::Properties::analyzeCurrent(File *file)
|
||||
return;
|
||||
}
|
||||
|
||||
const uint descriptorBytes = descriptor.toUInt(0, false);
|
||||
const unsigned int descriptorBytes = descriptor.toUInt(0, false);
|
||||
|
||||
if((descriptorBytes - 52) > 0)
|
||||
file->seek(descriptorBytes - 52, File::Current);
|
||||
@@ -201,12 +170,12 @@ void APE::Properties::analyzeCurrent(File *file)
|
||||
d->sampleRate = header.toUInt(20, false);
|
||||
d->bitsPerSample = header.toShort(16, false);
|
||||
|
||||
const uint totalFrames = header.toUInt(12, false);
|
||||
const unsigned int totalFrames = header.toUInt(12, false);
|
||||
if(totalFrames == 0)
|
||||
return;
|
||||
|
||||
const uint blocksPerFrame = header.toUInt(4, false);
|
||||
const uint finalFrameBlocks = header.toUInt(8, false);
|
||||
const unsigned int blocksPerFrame = header.toUInt(4, false);
|
||||
const unsigned int finalFrameBlocks = header.toUInt(8, false);
|
||||
d->sampleFrames = (totalFrames - 1) * blocksPerFrame + finalFrameBlocks;
|
||||
}
|
||||
|
||||
@@ -218,14 +187,14 @@ void APE::Properties::analyzeOld(File *file)
|
||||
return;
|
||||
}
|
||||
|
||||
const uint totalFrames = header.toUInt(18, false);
|
||||
const unsigned int totalFrames = header.toUInt(18, false);
|
||||
|
||||
// Fail on 0 length APE files (catches non-finalized APE files)
|
||||
if(totalFrames == 0)
|
||||
return;
|
||||
|
||||
const short compressionLevel = header.toShort(0, false);
|
||||
uint blocksPerFrame;
|
||||
unsigned int blocksPerFrame;
|
||||
if(d->version >= 3950)
|
||||
blocksPerFrame = 73728 * 4;
|
||||
else if(d->version >= 3900 || (d->version >= 3800 && compressionLevel == 4000))
|
||||
@@ -237,7 +206,7 @@ void APE::Properties::analyzeOld(File *file)
|
||||
d->channels = header.toShort(4, false);
|
||||
d->sampleRate = header.toUInt(6, false);
|
||||
|
||||
const uint finalFrameBlocks = header.toUInt(22, false);
|
||||
const unsigned int finalFrameBlocks = header.toUInt(22, false);
|
||||
d->sampleFrames = (totalFrames - 1) * blocksPerFrame + finalFrameBlocks;
|
||||
|
||||
// Get the bit depth from the RIFF-fmt chunk.
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
#ifndef TAGLIB_APEPROPERTIES_H
|
||||
#define TAGLIB_APEPROPERTIES_H
|
||||
|
||||
#include "taglib.h"
|
||||
#include "taglib_export.h"
|
||||
#include "audioproperties.h"
|
||||
|
||||
@@ -52,63 +53,38 @@ namespace TagLib {
|
||||
/*!
|
||||
* Create an instance of APE::Properties with the data read from the
|
||||
* APE::File \a file.
|
||||
*
|
||||
* \deprecated
|
||||
*/
|
||||
Properties(File *file, ReadStyle style = Average);
|
||||
|
||||
/*!
|
||||
* Create an instance of APE::Properties with the data read from the
|
||||
* APE::File \a file.
|
||||
*/
|
||||
Properties(File *file, long streamLength, ReadStyle style = Average);
|
||||
Properties(File *file, offset_t streamLength, ReadStyle style = Average);
|
||||
|
||||
/*!
|
||||
* Destroys this APE::Properties instance.
|
||||
*/
|
||||
virtual ~Properties();
|
||||
~Properties() override;
|
||||
|
||||
/*!
|
||||
* Returns the length of the file in seconds. The length is rounded down to
|
||||
* the nearest whole second.
|
||||
*
|
||||
* \note This method is just an alias of lengthInSeconds().
|
||||
*
|
||||
* \deprecated
|
||||
*/
|
||||
virtual int length() const;
|
||||
|
||||
/*!
|
||||
* Returns the length of the file in seconds. The length is rounded down to
|
||||
* the nearest whole second.
|
||||
*
|
||||
* \see lengthInMilliseconds()
|
||||
*/
|
||||
// BIC: make virtual
|
||||
int lengthInSeconds() const;
|
||||
Properties(const Properties &) = delete;
|
||||
Properties &operator=(const Properties &) = delete;
|
||||
|
||||
/*!
|
||||
* Returns the length of the file in milliseconds.
|
||||
*
|
||||
* \see lengthInSeconds()
|
||||
*/
|
||||
// BIC: make virtual
|
||||
int lengthInMilliseconds() const;
|
||||
int lengthInMilliseconds() const override;
|
||||
|
||||
/*!
|
||||
* Returns the average bit rate of the file in kb/s.
|
||||
*/
|
||||
virtual int bitrate() const;
|
||||
int bitrate() const override;
|
||||
|
||||
/*!
|
||||
* Returns the sample rate in Hz.
|
||||
*/
|
||||
virtual int sampleRate() const;
|
||||
int sampleRate() const override;
|
||||
|
||||
/*!
|
||||
* Returns the number of audio channels.
|
||||
*/
|
||||
virtual int channels() const;
|
||||
int channels() const override;
|
||||
|
||||
/*!
|
||||
* Returns the number of bits per audio sample.
|
||||
@@ -118,7 +94,7 @@ namespace TagLib {
|
||||
/*!
|
||||
* Returns the total number of audio samples in file.
|
||||
*/
|
||||
uint sampleFrames() const;
|
||||
unsigned int sampleFrames() const;
|
||||
|
||||
/*!
|
||||
* Returns APE version.
|
||||
@@ -126,18 +102,16 @@ namespace TagLib {
|
||||
int version() const;
|
||||
|
||||
private:
|
||||
Properties(const Properties &);
|
||||
Properties &operator=(const Properties &);
|
||||
|
||||
void read(File *file, long streamLength);
|
||||
void read(File *file, offset_t streamLength);
|
||||
|
||||
void analyzeCurrent(File *file);
|
||||
void analyzeOld(File *file);
|
||||
|
||||
class PropertiesPrivate;
|
||||
PropertiesPrivate *d;
|
||||
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
|
||||
std::unique_ptr<PropertiesPrivate> d;
|
||||
};
|
||||
}
|
||||
}
|
||||
} // namespace APE
|
||||
} // namespace TagLib
|
||||
|
||||
#endif
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#ifdef __SUNPRO_CC
|
||||
#if defined(__SUNPRO_CC) && (__SUNPRO_CC < 0x5130)
|
||||
// Sun Studio finds multiple specializations of Map because
|
||||
// it considers specializations with and without class types
|
||||
// to be different; this define forces Map to use only the
|
||||
@@ -31,30 +31,47 @@
|
||||
#define WANT_CLASS_INSTANTIATION_OF_MAP (1)
|
||||
#endif
|
||||
|
||||
#include <tfile.h>
|
||||
#include <tstring.h>
|
||||
#include <tmap.h>
|
||||
#include <tpropertymap.h>
|
||||
|
||||
#include "apetag.h"
|
||||
|
||||
#include <array>
|
||||
#include <utility>
|
||||
|
||||
#include "tdebug.h"
|
||||
#include "tfile.h"
|
||||
#include "tpropertymap.h"
|
||||
#include "apefooter.h"
|
||||
#include "apeitem.h"
|
||||
|
||||
using namespace TagLib;
|
||||
using namespace APE;
|
||||
|
||||
namespace
|
||||
{
|
||||
const unsigned int MinKeyLength = 2;
|
||||
const unsigned int MaxKeyLength = 255;
|
||||
|
||||
const String FRONT_COVER("COVER ART (FRONT)");
|
||||
const String BACK_COVER("COVER ART (BACK)");
|
||||
|
||||
bool isKeyValid(const ByteVector &key)
|
||||
{
|
||||
static constexpr std::array invalidKeys { "ID3", "TAG", "OGGS", "MP+" };
|
||||
|
||||
// only allow printable ASCII including space (32..126)
|
||||
return std::none_of(key.begin(), key.end(),
|
||||
[](unsigned char c) { return c < 32 || c > 126; })
|
||||
&& std::none_of(invalidKeys.begin(), invalidKeys.end(),
|
||||
[upperKey = String(key).upper()](auto k) { return upperKey == k; });
|
||||
}
|
||||
} // namespace
|
||||
|
||||
class APE::Tag::TagPrivate
|
||||
{
|
||||
public:
|
||||
TagPrivate() :
|
||||
file(0),
|
||||
footerLocation(-1) {}
|
||||
|
||||
TagLib::File *file;
|
||||
long footerLocation;
|
||||
File *file { nullptr };
|
||||
offset_t footerLocation { 0 };
|
||||
|
||||
Footer footer;
|
||||
|
||||
ItemListMap itemListMap;
|
||||
};
|
||||
|
||||
@@ -63,14 +80,12 @@ public:
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
APE::Tag::Tag() :
|
||||
TagLib::Tag(),
|
||||
d(new TagPrivate())
|
||||
d(std::make_unique<TagPrivate>())
|
||||
{
|
||||
}
|
||||
|
||||
APE::Tag::Tag(TagLib::File *file, long footerLocation) :
|
||||
TagLib::Tag(),
|
||||
d(new TagPrivate())
|
||||
APE::Tag::Tag(TagLib::File *file, offset_t footerLocation) :
|
||||
d(std::make_unique<TagPrivate>())
|
||||
{
|
||||
d->file = file;
|
||||
d->footerLocation = footerLocation;
|
||||
@@ -78,10 +93,7 @@ APE::Tag::Tag(TagLib::File *file, long footerLocation) :
|
||||
read();
|
||||
}
|
||||
|
||||
APE::Tag::~Tag()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
APE::Tag::~Tag() = default;
|
||||
|
||||
ByteVector APE::Tag::fileIdentifier()
|
||||
{
|
||||
@@ -90,51 +102,44 @@ ByteVector APE::Tag::fileIdentifier()
|
||||
|
||||
String APE::Tag::title() const
|
||||
{
|
||||
if(d->itemListMap["TITLE"].isEmpty())
|
||||
return String::null;
|
||||
return d->itemListMap["TITLE"].values().toString();
|
||||
Item val = d->itemListMap.value("TITLE");
|
||||
return val.isEmpty() ? String() : joinTagValues(val.values());
|
||||
}
|
||||
|
||||
String APE::Tag::artist() const
|
||||
{
|
||||
if(d->itemListMap["ARTIST"].isEmpty())
|
||||
return String::null;
|
||||
return d->itemListMap["ARTIST"].values().toString();
|
||||
Item val = d->itemListMap.value("ARTIST");
|
||||
return val.isEmpty() ? String() : joinTagValues(val.values());
|
||||
}
|
||||
|
||||
String APE::Tag::album() const
|
||||
{
|
||||
if(d->itemListMap["ALBUM"].isEmpty())
|
||||
return String::null;
|
||||
return d->itemListMap["ALBUM"].values().toString();
|
||||
Item val = d->itemListMap.value("ALBUM");
|
||||
return val.isEmpty() ? String() : joinTagValues(val.values());
|
||||
}
|
||||
|
||||
String APE::Tag::comment() const
|
||||
{
|
||||
if(d->itemListMap["COMMENT"].isEmpty())
|
||||
return String::null;
|
||||
return d->itemListMap["COMMENT"].values().toString();
|
||||
Item val = d->itemListMap.value("COMMENT");
|
||||
return val.isEmpty() ? String() : joinTagValues(val.values());
|
||||
}
|
||||
|
||||
String APE::Tag::genre() const
|
||||
{
|
||||
if(d->itemListMap["GENRE"].isEmpty())
|
||||
return String::null;
|
||||
return d->itemListMap["GENRE"].values().toString();
|
||||
Item val = d->itemListMap.value("GENRE");
|
||||
return val.isEmpty() ? String() : joinTagValues(val.values());
|
||||
}
|
||||
|
||||
TagLib::uint APE::Tag::year() const
|
||||
unsigned int APE::Tag::year() const
|
||||
{
|
||||
if(d->itemListMap["YEAR"].isEmpty())
|
||||
return 0;
|
||||
return d->itemListMap["YEAR"].toString().toInt();
|
||||
Item val = d->itemListMap.value("YEAR");
|
||||
return val.isEmpty() ? 0 : val.toString().toInt();
|
||||
}
|
||||
|
||||
TagLib::uint APE::Tag::track() const
|
||||
unsigned int APE::Tag::track() const
|
||||
{
|
||||
if(d->itemListMap["TRACK"].isEmpty())
|
||||
return 0;
|
||||
return d->itemListMap["TRACK"].toString().toInt();
|
||||
Item val = d->itemListMap.value("TRACK");
|
||||
return val.isEmpty() ? 0 : val.toString().toInt();
|
||||
}
|
||||
|
||||
void APE::Tag::setTitle(const String &s)
|
||||
@@ -162,47 +167,55 @@ void APE::Tag::setGenre(const String &s)
|
||||
addValue("GENRE", s, true);
|
||||
}
|
||||
|
||||
void APE::Tag::setYear(uint i)
|
||||
void APE::Tag::setYear(unsigned int i)
|
||||
{
|
||||
if(i <= 0)
|
||||
if(i == 0)
|
||||
removeItem("YEAR");
|
||||
else
|
||||
addValue("YEAR", String::number(i), true);
|
||||
}
|
||||
|
||||
void APE::Tag::setTrack(uint i)
|
||||
void APE::Tag::setTrack(unsigned int i)
|
||||
{
|
||||
if(i <= 0)
|
||||
if(i == 0)
|
||||
removeItem("TRACK");
|
||||
else
|
||||
addValue("TRACK", String::number(i), true);
|
||||
}
|
||||
|
||||
// conversions of tag keys between what we use in PropertyMap and what's usual
|
||||
// for APE tags
|
||||
static const TagLib::uint keyConversionsSize = 5; //usual, APE
|
||||
static const char *keyConversions[][2] = {{"TRACKNUMBER", "TRACK" },
|
||||
{"DATE", "YEAR" },
|
||||
{"ALBUMARTIST", "ALBUM ARTIST"},
|
||||
{"DISCNUMBER", "DISC" },
|
||||
{"REMIXER", "MIXARTIST" }};
|
||||
namespace
|
||||
{
|
||||
// conversions of tag keys between what we use in PropertyMap and what's usual
|
||||
// for APE tags
|
||||
// usual, APE
|
||||
constexpr std::array keyConversions {
|
||||
std::pair("TRACKNUMBER", "TRACK"),
|
||||
std::pair("DATE", "YEAR"),
|
||||
std::pair("ALBUMARTIST", "ALBUM ARTIST"),
|
||||
std::pair("DISCNUMBER", "DISC"),
|
||||
std::pair("REMIXER", "MIXARTIST"),
|
||||
std::pair("RELEASESTATUS", "MUSICBRAINZ_ALBUMSTATUS"),
|
||||
std::pair("RELEASETYPE", "MUSICBRAINZ_ALBUMTYPE"),
|
||||
};
|
||||
} // namespace
|
||||
|
||||
PropertyMap APE::Tag::properties() const
|
||||
{
|
||||
PropertyMap properties;
|
||||
ItemListMap::ConstIterator it = itemListMap().begin();
|
||||
for(; it != itemListMap().end(); ++it) {
|
||||
String tagName = it->first.upper();
|
||||
for(const auto &[tag, item] : std::as_const(itemListMap())) {
|
||||
String tagName = tag.upper();
|
||||
// if the item is Binary or Locator, or if the key is an invalid string,
|
||||
// add to unsupportedData
|
||||
if(it->second.type() != Item::Text || tagName.isNull())
|
||||
properties.unsupportedData().append(it->first);
|
||||
if(item.type() != Item::Text || tagName.isEmpty()) {
|
||||
properties.addUnsupportedData(tag);
|
||||
}
|
||||
else {
|
||||
// Some tags need to be handled specially
|
||||
for(uint i = 0; i < keyConversionsSize; ++i)
|
||||
if(tagName == keyConversions[i][1])
|
||||
tagName = keyConversions[i][0];
|
||||
properties[tagName].append(it->second.toStringList());
|
||||
for(const auto &[k, t] : keyConversions) {
|
||||
if(tagName == t)
|
||||
tagName = k;
|
||||
}
|
||||
properties[tagName].append(item.values());
|
||||
}
|
||||
}
|
||||
return properties;
|
||||
@@ -210,69 +223,147 @@ PropertyMap APE::Tag::properties() const
|
||||
|
||||
void APE::Tag::removeUnsupportedProperties(const StringList &properties)
|
||||
{
|
||||
StringList::ConstIterator it = properties.begin();
|
||||
for(; it != properties.end(); ++it)
|
||||
removeItem(*it);
|
||||
for(const auto &property : properties)
|
||||
removeItem(property);
|
||||
}
|
||||
|
||||
PropertyMap APE::Tag::setProperties(const PropertyMap &origProps)
|
||||
{
|
||||
PropertyMap properties(origProps); // make a local copy that can be modified
|
||||
PropertyMap props(origProps); // make a local copy that can be modified
|
||||
|
||||
// see comment in properties()
|
||||
for(uint i = 0; i < keyConversionsSize; ++i)
|
||||
if(properties.contains(keyConversions[i][0])) {
|
||||
properties.insert(keyConversions[i][1], properties[keyConversions[i][0]]);
|
||||
properties.erase(keyConversions[i][0]);
|
||||
for(const auto &[k, t] : keyConversions)
|
||||
if(props.contains(k)) {
|
||||
props.insert(t, props[k]);
|
||||
props.erase(k);
|
||||
}
|
||||
|
||||
// first check if tags need to be removed completely
|
||||
StringList toRemove;
|
||||
ItemListMap::ConstIterator remIt = itemListMap().begin();
|
||||
for(; remIt != itemListMap().end(); ++remIt) {
|
||||
String key = remIt->first.upper();
|
||||
for(const auto &[k, t] : std::as_const(itemListMap())) {
|
||||
String key = k.upper();
|
||||
// only remove if a) key is valid, b) type is text, c) key not contained in new properties
|
||||
if(!key.isNull() && remIt->second.type() == APE::Item::Text && !properties.contains(key))
|
||||
toRemove.append(remIt->first);
|
||||
if(!key.isEmpty() && t.type() == APE::Item::Text && !props.contains(key))
|
||||
toRemove.append(k);
|
||||
}
|
||||
|
||||
for(StringList::ConstIterator removeIt = toRemove.begin(); removeIt != toRemove.end(); removeIt++)
|
||||
removeItem(*removeIt);
|
||||
for(const auto &item : std::as_const(toRemove))
|
||||
removeItem(item);
|
||||
|
||||
// now sync in the "forward direction"
|
||||
PropertyMap::ConstIterator it = properties.begin();
|
||||
PropertyMap invalid;
|
||||
for(; it != properties.end(); ++it) {
|
||||
const String &tagName = it->first;
|
||||
for(const auto &[tagName, val] : std::as_const(props)) {
|
||||
if(!checkKey(tagName))
|
||||
invalid.insert(it->first, it->second);
|
||||
else if(!(itemListMap().contains(tagName)) || !(itemListMap()[tagName].values() == it->second)) {
|
||||
if(it->second.size() == 0)
|
||||
invalid.insert(tagName, val);
|
||||
else if(!(itemListMap().contains(tagName)) || !(itemListMap()[tagName].values() == val)) {
|
||||
if(val.isEmpty())
|
||||
removeItem(tagName);
|
||||
else {
|
||||
StringList::ConstIterator valueIt = it->second.begin();
|
||||
addValue(tagName, *valueIt, true);
|
||||
++valueIt;
|
||||
for(; valueIt != it->second.end(); ++valueIt)
|
||||
addValue(tagName, *valueIt, false);
|
||||
addValue(tagName, *val.begin(), true);
|
||||
for(auto it = std::next(val.begin()); it != val.end(); ++it)
|
||||
addValue(tagName, *it, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
return invalid;
|
||||
}
|
||||
|
||||
StringList APE::Tag::complexPropertyKeys() const
|
||||
{
|
||||
StringList keys;
|
||||
if(d->itemListMap.contains(FRONT_COVER) ||
|
||||
d->itemListMap.contains(BACK_COVER)) {
|
||||
keys.append("PICTURE");
|
||||
}
|
||||
return keys;
|
||||
}
|
||||
|
||||
List<VariantMap> APE::Tag::complexProperties(const String &key) const
|
||||
{
|
||||
List<VariantMap> props;
|
||||
const String uppercaseKey = key.upper();
|
||||
if(uppercaseKey == "PICTURE") {
|
||||
const StringList itemNames = StringList(FRONT_COVER).append(BACK_COVER);
|
||||
for(const auto &itemName: itemNames) {
|
||||
if(d->itemListMap.contains(itemName)) {
|
||||
Item picture = d->itemListMap.value(itemName);
|
||||
if(picture.type() == Item::Binary) {
|
||||
ByteVector data = picture.binaryData();
|
||||
// Do not search for a description if the first byte could start JPG or PNG
|
||||
// data.
|
||||
int index = data.isEmpty() || data.at(0) == '\xff' || data.at(0) == '\x89'
|
||||
? -1 : data.find('\0');
|
||||
String description;
|
||||
if(index >= 0) {
|
||||
description = String(data.mid(0, index), String::UTF8);
|
||||
data = data.mid(index + 1);
|
||||
}
|
||||
|
||||
VariantMap property;
|
||||
property.insert("data", data);
|
||||
if(!description.isEmpty()) {
|
||||
property.insert("description", description);
|
||||
}
|
||||
property.insert("pictureType",
|
||||
itemName == BACK_COVER ? "Back Cover" : "Front Cover");
|
||||
props.append(property);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return props;
|
||||
}
|
||||
|
||||
bool APE::Tag::setComplexProperties(const String &key, const List<VariantMap> &value)
|
||||
{
|
||||
const String uppercaseKey = key.upper();
|
||||
if(uppercaseKey == "PICTURE") {
|
||||
removeItem(FRONT_COVER);
|
||||
removeItem(BACK_COVER);
|
||||
|
||||
auto frontItems = List<Item>();
|
||||
auto backItems = List<Item>();
|
||||
for(const auto &property : value) {
|
||||
auto data = property.value("description").value<String>().data(String::UTF8)
|
||||
.append('\0')
|
||||
.append(property.value("data").value<ByteVector>());
|
||||
auto pictureType = property.value("pictureType").value<String>();
|
||||
Item item;
|
||||
item.setType(Item::Binary);
|
||||
item.setBinaryData(data);
|
||||
if(pictureType == "Back Cover") {
|
||||
item.setKey(BACK_COVER);
|
||||
backItems.append(item);
|
||||
}
|
||||
else if(pictureType == "Front Cover") {
|
||||
item.setKey(FRONT_COVER);
|
||||
// prioritize pictures with correct type
|
||||
frontItems.prepend(item);
|
||||
}
|
||||
else {
|
||||
item.setKey(FRONT_COVER);
|
||||
frontItems.append(item);
|
||||
}
|
||||
}
|
||||
if(!frontItems.isEmpty()) {
|
||||
setItem(FRONT_COVER, frontItems.front());
|
||||
}
|
||||
if(!backItems.isEmpty()) {
|
||||
setItem(BACK_COVER, backItems.front());
|
||||
}
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool APE::Tag::checkKey(const String &key)
|
||||
{
|
||||
if(key.size() < 2 || key.size() > 16)
|
||||
return false;
|
||||
for(String::ConstIterator it = key.begin(); it != key.end(); it++)
|
||||
// only allow printable ASCII including space (32..127)
|
||||
if (*it < 32 || *it >= 128)
|
||||
return false;
|
||||
String upperKey = key.upper();
|
||||
if (upperKey=="ID3" || upperKey=="TAG" || upperKey=="OGGS" || upperKey=="MP+")
|
||||
return false;
|
||||
return true;
|
||||
if(key.size() < MinKeyLength || key.size() > MaxKeyLength)
|
||||
return false;
|
||||
|
||||
return isKeyValid(key.data(String::UTF8));
|
||||
}
|
||||
|
||||
APE::Footer *APE::Tag::footer() const
|
||||
@@ -294,31 +385,39 @@ void APE::Tag::addValue(const String &key, const String &value, bool replace)
|
||||
{
|
||||
if(replace)
|
||||
removeItem(key);
|
||||
if(!key.isEmpty() && !value.isEmpty()) {
|
||||
if(!replace && d->itemListMap.contains(key)) {
|
||||
// Text items may contain more than one value
|
||||
if(APE::Item::Text == d->itemListMap.begin()->second.type())
|
||||
d->itemListMap[key.upper()].appendValue(value);
|
||||
// Binary or locator items may have only one value
|
||||
else
|
||||
setItem(key, Item(key, value));
|
||||
}
|
||||
else
|
||||
setItem(key, Item(key, value));
|
||||
}
|
||||
|
||||
if(value.isEmpty())
|
||||
return;
|
||||
|
||||
// Text items may contain more than one value.
|
||||
// Binary or locator items may have only one value, hence always replaced.
|
||||
|
||||
auto it = d->itemListMap.find(key.upper());
|
||||
|
||||
if(it != d->itemListMap.end() && it->second.type() == Item::Text)
|
||||
it->second.appendValue(value);
|
||||
else
|
||||
setItem(key, Item(key, value));
|
||||
}
|
||||
|
||||
void APE::Tag::setData(const String &key, const ByteVector &value)
|
||||
{
|
||||
removeItem(key);
|
||||
if(!key.isEmpty() && !value.isEmpty())
|
||||
setItem(key, Item(key, value, true));
|
||||
|
||||
if(value.isEmpty())
|
||||
return;
|
||||
|
||||
setItem(key, Item(key, value, true));
|
||||
}
|
||||
|
||||
void APE::Tag::setItem(const String &key, const Item &item)
|
||||
{
|
||||
if(!key.isEmpty())
|
||||
d->itemListMap.insert(key.upper(), item);
|
||||
if(!checkKey(key)) {
|
||||
debug("APE::Tag::setItem() - Couldn't set an item due to an invalid key.");
|
||||
return;
|
||||
}
|
||||
|
||||
d->itemListMap[key.upper()] = item;
|
||||
}
|
||||
|
||||
bool APE::Tag::isEmpty() const
|
||||
@@ -338,7 +437,7 @@ void APE::Tag::read()
|
||||
d->footer.setData(d->file->readBlock(Footer::size()));
|
||||
|
||||
if(d->footer.tagSize() <= Footer::size() ||
|
||||
d->footer.tagSize() > uint(d->file->length()))
|
||||
d->footer.tagSize() > static_cast<unsigned long>(d->file->length()))
|
||||
return;
|
||||
|
||||
d->file->seek(d->footerLocation + Footer::size() - d->footer.tagSize());
|
||||
@@ -349,15 +448,11 @@ void APE::Tag::read()
|
||||
ByteVector APE::Tag::render() const
|
||||
{
|
||||
ByteVector data;
|
||||
uint itemCount = 0;
|
||||
unsigned int itemCount = 0;
|
||||
|
||||
{
|
||||
for(Map<const String, Item>::ConstIterator it = d->itemListMap.begin();
|
||||
it != d->itemListMap.end(); ++it)
|
||||
{
|
||||
data.append(it->second.render());
|
||||
itemCount++;
|
||||
}
|
||||
for(const auto &[_, list] : std::as_const(d->itemListMap)) {
|
||||
data.append(list.render());
|
||||
itemCount++;
|
||||
}
|
||||
|
||||
d->footer.setItemCount(itemCount);
|
||||
@@ -374,14 +469,37 @@ void APE::Tag::parse(const ByteVector &data)
|
||||
if(data.size() < 11)
|
||||
return;
|
||||
|
||||
uint pos = 0;
|
||||
unsigned int pos = 0;
|
||||
|
||||
for(uint i = 0; i < d->footer.itemCount() && pos <= data.size() - 11; i++) {
|
||||
APE::Item item;
|
||||
item.parse(data.mid(pos));
|
||||
for(unsigned int i = 0; i < d->footer.itemCount() && pos <= data.size() - 11; i++) {
|
||||
|
||||
d->itemListMap.insert(item.key().upper(), item);
|
||||
const int nullPos = data.find('\0', pos + 8);
|
||||
if(nullPos < 0) {
|
||||
debug("APE::Tag::parse() - Couldn't find a key/value separator. Stopped parsing.");
|
||||
return;
|
||||
}
|
||||
|
||||
pos += item.size();
|
||||
const unsigned int keyLength = nullPos - pos - 8;
|
||||
const unsigned int valLength = data.toUInt(pos, false);
|
||||
|
||||
if(valLength >= data.size() || pos > data.size() - valLength) {
|
||||
debug("APE::Tag::parse() - Invalid val length. Stopped parsing.");
|
||||
return;
|
||||
}
|
||||
|
||||
if(keyLength >= MinKeyLength
|
||||
&& keyLength <= MaxKeyLength
|
||||
&& isKeyValid(data.mid(pos + 8, keyLength)))
|
||||
{
|
||||
APE::Item item;
|
||||
item.parse(data.mid(pos));
|
||||
|
||||
d->itemListMap.insert(item.key().upper(), item);
|
||||
}
|
||||
else {
|
||||
debug("APE::Tag::parse() - Skipped an item due to an invalid key.");
|
||||
}
|
||||
|
||||
pos += keyLength + valLength + 9;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,12 +26,11 @@
|
||||
#ifndef TAGLIB_APETAG_H
|
||||
#define TAGLIB_APETAG_H
|
||||
|
||||
#include "tag.h"
|
||||
#include "tbytevector.h"
|
||||
#include "tmap.h"
|
||||
#include "tstring.h"
|
||||
#include "taglib_export.h"
|
||||
|
||||
#include "tag.h"
|
||||
#include "apeitem.h"
|
||||
|
||||
namespace TagLib {
|
||||
@@ -49,8 +48,7 @@ namespace TagLib {
|
||||
*
|
||||
* \see APE::Tag::itemListMap()
|
||||
*/
|
||||
typedef Map<const String, Item> ItemListMap;
|
||||
|
||||
using ItemListMap = Map<const String, Item>;
|
||||
|
||||
//! An APE tag implementation
|
||||
|
||||
@@ -66,12 +64,15 @@ namespace TagLib {
|
||||
* Create an APE tag and parse the data in \a file with APE footer at
|
||||
* \a tagOffset.
|
||||
*/
|
||||
Tag(TagLib::File *file, long footerLocation);
|
||||
Tag(TagLib::File *file, offset_t footerLocation);
|
||||
|
||||
/*!
|
||||
* Destroys this Tag instance.
|
||||
*/
|
||||
virtual ~Tag();
|
||||
~Tag() override;
|
||||
|
||||
Tag(const Tag &) = delete;
|
||||
Tag &operator=(const Tag &) = delete;
|
||||
|
||||
/*!
|
||||
* Renders the in memory values to a ByteVector suitable for writing to
|
||||
@@ -87,21 +88,21 @@ namespace TagLib {
|
||||
|
||||
// Reimplementations.
|
||||
|
||||
virtual String title() const;
|
||||
virtual String artist() const;
|
||||
virtual String album() const;
|
||||
virtual String comment() const;
|
||||
virtual String genre() const;
|
||||
virtual uint year() const;
|
||||
virtual uint track() const;
|
||||
String title() const override;
|
||||
String artist() const override;
|
||||
String album() const override;
|
||||
String comment() const override;
|
||||
String genre() const override;
|
||||
unsigned int year() const override;
|
||||
unsigned int track() const override;
|
||||
|
||||
virtual void setTitle(const String &s);
|
||||
virtual void setArtist(const String &s);
|
||||
virtual void setAlbum(const String &s);
|
||||
virtual void setComment(const String &s);
|
||||
virtual void setGenre(const String &s);
|
||||
virtual void setYear(uint i);
|
||||
virtual void setTrack(uint i);
|
||||
void setTitle(const String &s) override;
|
||||
void setArtist(const String &s) override;
|
||||
void setAlbum(const String &s) override;
|
||||
void setComment(const String &s) override;
|
||||
void setGenre(const String &s) override;
|
||||
void setYear(unsigned int i) override;
|
||||
void setTrack(unsigned int i) override;
|
||||
|
||||
/*!
|
||||
* Implements the unified tag dictionary interface -- export function.
|
||||
@@ -117,9 +118,9 @@ namespace TagLib {
|
||||
* TRACK to TRACKNUMBER, YEAR to DATE, and ALBUM ARTIST to ALBUMARTIST, respectively,
|
||||
* in order to be compliant with the names used in other formats.
|
||||
*/
|
||||
PropertyMap properties() const;
|
||||
PropertyMap properties() const override;
|
||||
|
||||
void removeUnsupportedProperties(const StringList &properties);
|
||||
void removeUnsupportedProperties(const StringList &properties) override;
|
||||
|
||||
/*!
|
||||
* Implements the unified tag dictionary interface -- import function. The same
|
||||
@@ -127,7 +128,11 @@ namespace TagLib {
|
||||
* specification requires keys to have between 2 and 16 printable ASCII characters
|
||||
* with the exception of the fixed strings "ID3", "TAG", "OGGS", and "MP+".
|
||||
*/
|
||||
PropertyMap setProperties(const PropertyMap &);
|
||||
PropertyMap setProperties(const PropertyMap &) override;
|
||||
|
||||
StringList complexPropertyKeys() const override;
|
||||
List<VariantMap> complexProperties(const String &key) const override;
|
||||
bool setComplexProperties(const String &key, const List<VariantMap> &value) override;
|
||||
|
||||
/*!
|
||||
* Check if the given String is a valid APE tag key.
|
||||
@@ -181,7 +186,7 @@ namespace TagLib {
|
||||
/*!
|
||||
* Returns true if the tag does not contain any data.
|
||||
*/
|
||||
bool isEmpty() const;
|
||||
bool isEmpty() const override;
|
||||
|
||||
protected:
|
||||
|
||||
@@ -196,13 +201,11 @@ namespace TagLib {
|
||||
void parse(const ByteVector &data);
|
||||
|
||||
private:
|
||||
Tag(const Tag &);
|
||||
Tag &operator=(const Tag &);
|
||||
|
||||
class TagPrivate;
|
||||
TagPrivate *d;
|
||||
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
|
||||
std::unique_ptr<TagPrivate> d;
|
||||
};
|
||||
}
|
||||
}
|
||||
} // namespace APE
|
||||
} // namespace TagLib
|
||||
|
||||
#endif
|
||||
|
||||
@@ -23,119 +23,103 @@
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#include <taglib.h>
|
||||
#include <tdebug.h>
|
||||
#include <trefcounter.h>
|
||||
|
||||
#include "asfattribute.h"
|
||||
|
||||
#include "tdebug.h"
|
||||
|
||||
#include "asffile.h"
|
||||
#include "asfutils.h"
|
||||
|
||||
using namespace TagLib;
|
||||
|
||||
class ASF::Attribute::AttributePrivate : public RefCounter
|
||||
class ASF::Attribute::AttributePrivate
|
||||
{
|
||||
public:
|
||||
AttributePrivate()
|
||||
: pictureValue(ASF::Picture::fromInvalid()),
|
||||
stream(0),
|
||||
language(0) {}
|
||||
AttributePrivate() :
|
||||
pictureValue(ASF::Picture::fromInvalid())
|
||||
{
|
||||
}
|
||||
AttributeTypes type;
|
||||
String stringValue;
|
||||
ByteVector byteVectorValue;
|
||||
ASF::Picture pictureValue;
|
||||
union {
|
||||
unsigned int intValue;
|
||||
unsigned short shortValue;
|
||||
unsigned long long longLongValue;
|
||||
bool boolValue;
|
||||
};
|
||||
int stream;
|
||||
int language;
|
||||
unsigned long long numericValue { 0 };
|
||||
int stream { 0 };
|
||||
int language { 0 };
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// public members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
ASF::Attribute::Attribute()
|
||||
ASF::Attribute::Attribute() :
|
||||
d(std::make_shared<AttributePrivate>())
|
||||
{
|
||||
d = new AttributePrivate;
|
||||
d->type = UnicodeType;
|
||||
}
|
||||
|
||||
ASF::Attribute::Attribute(const ASF::Attribute &other)
|
||||
: d(other.d)
|
||||
{
|
||||
d->ref();
|
||||
}
|
||||
ASF::Attribute::Attribute(const ASF::Attribute &) = default;
|
||||
|
||||
ASF::Attribute &ASF::Attribute::operator=(const ASF::Attribute &other)
|
||||
ASF::Attribute::Attribute(const String &value) :
|
||||
d(std::make_shared<AttributePrivate>())
|
||||
{
|
||||
if(&other != this) {
|
||||
if(d->deref())
|
||||
delete d;
|
||||
d = other.d;
|
||||
d->ref();
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
ASF::Attribute::~Attribute()
|
||||
{
|
||||
if(d->deref())
|
||||
delete d;
|
||||
}
|
||||
|
||||
ASF::Attribute::Attribute(const String &value)
|
||||
{
|
||||
d = new AttributePrivate;
|
||||
d->type = UnicodeType;
|
||||
d->stringValue = value;
|
||||
}
|
||||
|
||||
ASF::Attribute::Attribute(const ByteVector &value)
|
||||
ASF::Attribute::Attribute(const ByteVector &value) :
|
||||
d(std::make_shared<AttributePrivate>())
|
||||
{
|
||||
d = new AttributePrivate;
|
||||
d->type = BytesType;
|
||||
d->byteVectorValue = value;
|
||||
}
|
||||
|
||||
ASF::Attribute::Attribute(const ASF::Picture &value)
|
||||
ASF::Attribute::Attribute(const ASF::Picture &value) :
|
||||
d(std::make_shared<AttributePrivate>())
|
||||
{
|
||||
d = new AttributePrivate;
|
||||
d->type = BytesType;
|
||||
d->pictureValue = value;
|
||||
}
|
||||
|
||||
ASF::Attribute::Attribute(unsigned int value)
|
||||
ASF::Attribute::Attribute(unsigned int value) :
|
||||
d(std::make_shared<AttributePrivate>())
|
||||
{
|
||||
d = new AttributePrivate;
|
||||
d->type = DWordType;
|
||||
d->intValue = value;
|
||||
d->numericValue = value;
|
||||
}
|
||||
|
||||
ASF::Attribute::Attribute(unsigned long long value)
|
||||
ASF::Attribute::Attribute(unsigned long long value) :
|
||||
d(std::make_shared<AttributePrivate>())
|
||||
{
|
||||
d = new AttributePrivate;
|
||||
d->type = QWordType;
|
||||
d->longLongValue = value;
|
||||
d->numericValue = value;
|
||||
}
|
||||
|
||||
ASF::Attribute::Attribute(unsigned short value)
|
||||
ASF::Attribute::Attribute(unsigned short value) :
|
||||
d(std::make_shared<AttributePrivate>())
|
||||
{
|
||||
d = new AttributePrivate;
|
||||
d->type = WordType;
|
||||
d->shortValue = value;
|
||||
d->numericValue = value;
|
||||
}
|
||||
|
||||
ASF::Attribute::Attribute(bool value)
|
||||
ASF::Attribute::Attribute(bool value) :
|
||||
d(std::make_shared<AttributePrivate>())
|
||||
{
|
||||
d = new AttributePrivate;
|
||||
d->type = BoolType;
|
||||
d->boolValue = value;
|
||||
d->numericValue = value;
|
||||
}
|
||||
|
||||
ASF::Attribute &ASF::Attribute::operator=(const ASF::Attribute &) = default;
|
||||
|
||||
void ASF::Attribute::swap(Attribute &other) noexcept
|
||||
{
|
||||
using std::swap;
|
||||
|
||||
swap(d, other.d);
|
||||
}
|
||||
|
||||
ASF::Attribute::~Attribute() = default;
|
||||
|
||||
ASF::Attribute::AttributeTypes ASF::Attribute::type() const
|
||||
{
|
||||
return d->type;
|
||||
@@ -155,22 +139,22 @@ ByteVector ASF::Attribute::toByteVector() const
|
||||
|
||||
unsigned short ASF::Attribute::toBool() const
|
||||
{
|
||||
return d->shortValue;
|
||||
return d->numericValue ? 1 : 0;
|
||||
}
|
||||
|
||||
unsigned short ASF::Attribute::toUShort() const
|
||||
{
|
||||
return d->shortValue;
|
||||
return static_cast<unsigned short>(d->numericValue);
|
||||
}
|
||||
|
||||
unsigned int ASF::Attribute::toUInt() const
|
||||
{
|
||||
return d->intValue;
|
||||
return static_cast<unsigned int>(d->numericValue);
|
||||
}
|
||||
|
||||
unsigned long long ASF::Attribute::toULongLong() const
|
||||
{
|
||||
return d->longLongValue;
|
||||
return static_cast<unsigned long long>(d->numericValue);
|
||||
}
|
||||
|
||||
ASF::Picture ASF::Attribute::toPicture() const
|
||||
@@ -180,14 +164,14 @@ ASF::Picture ASF::Attribute::toPicture() const
|
||||
|
||||
String ASF::Attribute::parse(ASF::File &f, int kind)
|
||||
{
|
||||
uint size, nameLength;
|
||||
unsigned int size, nameLength;
|
||||
String name;
|
||||
d->pictureValue = Picture::fromInvalid();
|
||||
// extended content descriptor
|
||||
if(kind == 0) {
|
||||
nameLength = readWORD(&f);
|
||||
name = readString(&f, nameLength);
|
||||
d->type = ASF::Attribute::AttributeTypes(readWORD(&f));
|
||||
d->type = static_cast<ASF::Attribute::AttributeTypes>(readWORD(&f));
|
||||
size = readWORD(&f);
|
||||
}
|
||||
// metadata & metadata library
|
||||
@@ -199,7 +183,7 @@ String ASF::Attribute::parse(ASF::File &f, int kind)
|
||||
}
|
||||
d->stream = readWORD(&f);
|
||||
nameLength = readWORD(&f);
|
||||
d->type = ASF::Attribute::AttributeTypes(readWORD(&f));
|
||||
d->type = static_cast<ASF::Attribute::AttributeTypes>(readWORD(&f));
|
||||
size = readDWORD(&f);
|
||||
name = readString(&f, nameLength);
|
||||
}
|
||||
@@ -210,24 +194,24 @@ String ASF::Attribute::parse(ASF::File &f, int kind)
|
||||
|
||||
switch(d->type) {
|
||||
case WordType:
|
||||
d->shortValue = readWORD(&f);
|
||||
d->numericValue = readWORD(&f);
|
||||
break;
|
||||
|
||||
case BoolType:
|
||||
if(kind == 0) {
|
||||
d->boolValue = (readDWORD(&f) == 1);
|
||||
d->numericValue = (readDWORD(&f) != 0);
|
||||
}
|
||||
else {
|
||||
d->boolValue = (readWORD(&f) == 1);
|
||||
d->numericValue = (readWORD(&f) != 0);
|
||||
}
|
||||
break;
|
||||
|
||||
case DWordType:
|
||||
d->intValue = readDWORD(&f);
|
||||
d->numericValue = readDWORD(&f);
|
||||
break;
|
||||
|
||||
case QWordType:
|
||||
d->longLongValue = readQWORD(&f);
|
||||
d->numericValue = readQWORD(&f);
|
||||
break;
|
||||
|
||||
case UnicodeType:
|
||||
@@ -264,8 +248,10 @@ int ASF::Attribute::dataSize() const
|
||||
case UnicodeType:
|
||||
return d->stringValue.size() * 2 + 2;
|
||||
case BytesType:
|
||||
if(d->pictureValue.isValid())
|
||||
if(d->pictureValue.isValid()) {
|
||||
return d->pictureValue.dataSize();
|
||||
}
|
||||
return d->byteVectorValue.size();
|
||||
case GuidType:
|
||||
return d->byteVectorValue.size();
|
||||
}
|
||||
@@ -278,24 +264,24 @@ ByteVector ASF::Attribute::render(const String &name, int kind) const
|
||||
|
||||
switch (d->type) {
|
||||
case WordType:
|
||||
data.append(ByteVector::fromShort(d->shortValue, false));
|
||||
data.append(ByteVector::fromShort(toUShort(), false));
|
||||
break;
|
||||
|
||||
case BoolType:
|
||||
if(kind == 0) {
|
||||
data.append(ByteVector::fromUInt(d->boolValue ? 1 : 0, false));
|
||||
data.append(ByteVector::fromUInt(toBool(), false));
|
||||
}
|
||||
else {
|
||||
data.append(ByteVector::fromShort(d->boolValue ? 1 : 0, false));
|
||||
data.append(ByteVector::fromShort(toBool(), false));
|
||||
}
|
||||
break;
|
||||
|
||||
case DWordType:
|
||||
data.append(ByteVector::fromUInt(d->intValue, false));
|
||||
data.append(ByteVector::fromUInt(toUInt(), false));
|
||||
break;
|
||||
|
||||
case QWordType:
|
||||
data.append(ByteVector::fromLongLong(d->longLongValue, false));
|
||||
data.append(ByteVector::fromLongLong(toULongLong(), false));
|
||||
break;
|
||||
|
||||
case UnicodeType:
|
||||
@@ -305,8 +291,11 @@ ByteVector ASF::Attribute::render(const String &name, int kind) const
|
||||
case BytesType:
|
||||
if(d->pictureValue.isValid()) {
|
||||
data.append(d->pictureValue.render());
|
||||
break;
|
||||
}
|
||||
else {
|
||||
data.append(d->byteVectorValue);
|
||||
}
|
||||
break;
|
||||
case GuidType:
|
||||
data.append(d->byteVectorValue);
|
||||
break;
|
||||
@@ -314,7 +303,7 @@ ByteVector ASF::Attribute::render(const String &name, int kind) const
|
||||
|
||||
if(kind == 0) {
|
||||
data = renderString(name, true) +
|
||||
ByteVector::fromShort((int)d->type, false) +
|
||||
ByteVector::fromShort(static_cast<int>(d->type), false) +
|
||||
ByteVector::fromShort(data.size(), false) +
|
||||
data;
|
||||
}
|
||||
@@ -323,7 +312,7 @@ ByteVector ASF::Attribute::render(const String &name, int kind) const
|
||||
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::fromShort(static_cast<int>(d->type), false) +
|
||||
ByteVector::fromUInt(data.size(), false) +
|
||||
nameData +
|
||||
data;
|
||||
@@ -351,4 +340,3 @@ void ASF::Attribute::setStream(int value)
|
||||
{
|
||||
d->stream = value;
|
||||
}
|
||||
|
||||
|
||||
@@ -33,10 +33,8 @@
|
||||
|
||||
namespace TagLib
|
||||
{
|
||||
|
||||
namespace ASF
|
||||
{
|
||||
|
||||
class File;
|
||||
class Picture;
|
||||
|
||||
@@ -108,12 +106,17 @@ namespace TagLib
|
||||
/*!
|
||||
* Construct an attribute as a copy of \a other.
|
||||
*/
|
||||
Attribute(const Attribute &item);
|
||||
Attribute(const Attribute &other);
|
||||
|
||||
/*!
|
||||
* Copies the contents of \a other into this item.
|
||||
*/
|
||||
ASF::Attribute &operator=(const Attribute &other);
|
||||
Attribute &operator=(const Attribute &other);
|
||||
|
||||
/*!
|
||||
* Exchanges the content of the Attribute by the content of \a other.
|
||||
*/
|
||||
void swap(Attribute &other) noexcept;
|
||||
|
||||
/*!
|
||||
* Destroys the attribute.
|
||||
@@ -194,10 +197,10 @@ namespace TagLib
|
||||
ByteVector render(const String &name, int kind = 0) const;
|
||||
|
||||
class AttributePrivate;
|
||||
AttributePrivate *d;
|
||||
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
|
||||
std::shared_ptr<AttributePrivate> d;
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace ASF
|
||||
} // namespace TagLib
|
||||
|
||||
#endif
|
||||
|
||||
@@ -23,12 +23,14 @@
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#include <tdebug.h>
|
||||
#include <tbytevectorlist.h>
|
||||
#include <tpropertymap.h>
|
||||
#include <tstring.h>
|
||||
|
||||
#include "asffile.h"
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include "tdebug.h"
|
||||
#include "tpropertymap.h"
|
||||
#include "tbytevectorlist.h"
|
||||
#include "tagutils.h"
|
||||
#include "asftag.h"
|
||||
#include "asfproperties.h"
|
||||
#include "asfutils.h"
|
||||
@@ -49,37 +51,28 @@ public:
|
||||
class MetadataObject;
|
||||
class MetadataLibraryObject;
|
||||
|
||||
FilePrivate():
|
||||
size(0),
|
||||
tag(0),
|
||||
properties(0),
|
||||
contentDescriptionObject(0),
|
||||
extendedContentDescriptionObject(0),
|
||||
headerExtensionObject(0),
|
||||
metadataObject(0),
|
||||
metadataLibraryObject(0)
|
||||
FilePrivate()
|
||||
{
|
||||
objects.setAutoDelete(true);
|
||||
}
|
||||
|
||||
~FilePrivate()
|
||||
{
|
||||
delete tag;
|
||||
delete properties;
|
||||
}
|
||||
~FilePrivate() = default;
|
||||
|
||||
unsigned long long size;
|
||||
FilePrivate(const FilePrivate &) = delete;
|
||||
FilePrivate &operator=(const FilePrivate &) = delete;
|
||||
|
||||
ASF::Tag *tag;
|
||||
ASF::Properties *properties;
|
||||
unsigned long long headerSize { 0 };
|
||||
|
||||
std::unique_ptr<ASF::Tag> tag;
|
||||
std::unique_ptr<ASF::Properties> properties;
|
||||
|
||||
List<BaseObject *> objects;
|
||||
|
||||
ContentDescriptionObject *contentDescriptionObject;
|
||||
ExtendedContentDescriptionObject *extendedContentDescriptionObject;
|
||||
HeaderExtensionObject *headerExtensionObject;
|
||||
MetadataObject *metadataObject;
|
||||
MetadataLibraryObject *metadataLibraryObject;
|
||||
ContentDescriptionObject *contentDescriptionObject { nullptr };
|
||||
ExtendedContentDescriptionObject *extendedContentDescriptionObject { nullptr };
|
||||
HeaderExtensionObject *headerExtensionObject { nullptr };
|
||||
MetadataObject *metadataObject { nullptr };
|
||||
MetadataLibraryObject *metadataLibraryObject { nullptr };
|
||||
};
|
||||
|
||||
namespace
|
||||
@@ -96,15 +89,15 @@ namespace
|
||||
const ByteVector contentEncryptionGuid("\xFB\xB3\x11\x22\x23\xBD\xD2\x11\xB4\xB7\x00\xA0\xC9\x55\xFC\x6E", 16);
|
||||
const ByteVector extendedContentEncryptionGuid("\x14\xE6\x8A\x29\x22\x26 \x17\x4C\xB9\x35\xDA\xE0\x7E\xE9\x28\x9C", 16);
|
||||
const ByteVector advancedContentEncryptionGuid("\xB6\x9B\x07\x7A\xA4\xDA\x12\x4E\xA5\xCA\x91\xD3\x8D\xC1\x1A\x8D", 16);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
class ASF::File::FilePrivate::BaseObject
|
||||
{
|
||||
public:
|
||||
ByteVector data;
|
||||
virtual ~BaseObject() {}
|
||||
virtual ~BaseObject() = default;
|
||||
virtual ByteVector guid() const = 0;
|
||||
virtual void parse(ASF::File *file, unsigned int size);
|
||||
virtual void parse(ASF::File *file, long long size);
|
||||
virtual ByteVector render(ASF::File *file);
|
||||
};
|
||||
|
||||
@@ -113,56 +106,56 @@ class ASF::File::FilePrivate::UnknownObject : public ASF::File::FilePrivate::Bas
|
||||
ByteVector myGuid;
|
||||
public:
|
||||
UnknownObject(const ByteVector &guid);
|
||||
ByteVector guid() const;
|
||||
ByteVector guid() const override;
|
||||
};
|
||||
|
||||
class ASF::File::FilePrivate::FilePropertiesObject : public ASF::File::FilePrivate::BaseObject
|
||||
{
|
||||
public:
|
||||
ByteVector guid() const;
|
||||
void parse(ASF::File *file, uint size);
|
||||
ByteVector guid() const override;
|
||||
void parse(ASF::File *file, long long size) override;
|
||||
};
|
||||
|
||||
class ASF::File::FilePrivate::StreamPropertiesObject : public ASF::File::FilePrivate::BaseObject
|
||||
{
|
||||
public:
|
||||
ByteVector guid() const;
|
||||
void parse(ASF::File *file, uint size);
|
||||
ByteVector guid() const override;
|
||||
void parse(ASF::File *file, long long size) override;
|
||||
};
|
||||
|
||||
class ASF::File::FilePrivate::ContentDescriptionObject : public ASF::File::FilePrivate::BaseObject
|
||||
{
|
||||
public:
|
||||
ByteVector guid() const;
|
||||
void parse(ASF::File *file, uint size);
|
||||
ByteVector render(ASF::File *file);
|
||||
ByteVector guid() const override;
|
||||
void parse(ASF::File *file, long long size) override;
|
||||
ByteVector render(ASF::File *file) override;
|
||||
};
|
||||
|
||||
class ASF::File::FilePrivate::ExtendedContentDescriptionObject : public ASF::File::FilePrivate::BaseObject
|
||||
{
|
||||
public:
|
||||
ByteVectorList attributeData;
|
||||
ByteVector guid() const;
|
||||
void parse(ASF::File *file, uint size);
|
||||
ByteVector render(ASF::File *file);
|
||||
ByteVector guid() const override;
|
||||
void parse(ASF::File *file, long long size) override;
|
||||
ByteVector render(ASF::File *file) override;
|
||||
};
|
||||
|
||||
class ASF::File::FilePrivate::MetadataObject : public ASF::File::FilePrivate::BaseObject
|
||||
{
|
||||
public:
|
||||
ByteVectorList attributeData;
|
||||
ByteVector guid() const;
|
||||
void parse(ASF::File *file, uint size);
|
||||
ByteVector render(ASF::File *file);
|
||||
ByteVector guid() const override;
|
||||
void parse(ASF::File *file, long long size) override;
|
||||
ByteVector render(ASF::File *file) override;
|
||||
};
|
||||
|
||||
class ASF::File::FilePrivate::MetadataLibraryObject : public ASF::File::FilePrivate::BaseObject
|
||||
{
|
||||
public:
|
||||
ByteVectorList attributeData;
|
||||
ByteVector guid() const;
|
||||
void parse(ASF::File *file, uint size);
|
||||
ByteVector render(ASF::File *file);
|
||||
ByteVector guid() const override;
|
||||
void parse(ASF::File *file, long long size) override;
|
||||
ByteVector render(ASF::File *file) override;
|
||||
};
|
||||
|
||||
class ASF::File::FilePrivate::HeaderExtensionObject : public ASF::File::FilePrivate::BaseObject
|
||||
@@ -170,16 +163,16 @@ class ASF::File::FilePrivate::HeaderExtensionObject : public ASF::File::FilePriv
|
||||
public:
|
||||
List<ASF::File::FilePrivate::BaseObject *> objects;
|
||||
HeaderExtensionObject();
|
||||
ByteVector guid() const;
|
||||
void parse(ASF::File *file, uint size);
|
||||
ByteVector render(ASF::File *file);
|
||||
ByteVector guid() const override;
|
||||
void parse(ASF::File *file, long long size) override;
|
||||
ByteVector render(ASF::File *file) override;
|
||||
};
|
||||
|
||||
class ASF::File::FilePrivate::CodecListObject : public ASF::File::FilePrivate::BaseObject
|
||||
{
|
||||
public:
|
||||
ByteVector guid() const;
|
||||
void parse(ASF::File *file, uint size);
|
||||
ByteVector guid() const override;
|
||||
void parse(ASF::File *file, long long size) override;
|
||||
|
||||
private:
|
||||
enum CodecType
|
||||
@@ -190,13 +183,13 @@ private:
|
||||
};
|
||||
};
|
||||
|
||||
void ASF::File::FilePrivate::BaseObject::parse(ASF::File *file, unsigned int size)
|
||||
void ASF::File::FilePrivate::BaseObject::parse(ASF::File *file, long long size)
|
||||
{
|
||||
data.clear();
|
||||
if(size > 24 && size <= (unsigned int)(file->length()))
|
||||
if(size > 24 && size <= file->length())
|
||||
data = file->readBlock(size - 24);
|
||||
else
|
||||
data = ByteVector::null;
|
||||
data = ByteVector();
|
||||
}
|
||||
|
||||
ByteVector ASF::File::FilePrivate::BaseObject::render(ASF::File * /*file*/)
|
||||
@@ -218,7 +211,7 @@ ByteVector ASF::File::FilePrivate::FilePropertiesObject::guid() const
|
||||
return filePropertiesGuid;
|
||||
}
|
||||
|
||||
void ASF::File::FilePrivate::FilePropertiesObject::parse(ASF::File *file, uint size)
|
||||
void ASF::File::FilePrivate::FilePropertiesObject::parse(ASF::File *file, long long size)
|
||||
{
|
||||
BaseObject::parse(file, size);
|
||||
if(data.size() < 64) {
|
||||
@@ -236,7 +229,7 @@ ByteVector ASF::File::FilePrivate::StreamPropertiesObject::guid() const
|
||||
return streamPropertiesGuid;
|
||||
}
|
||||
|
||||
void ASF::File::FilePrivate::StreamPropertiesObject::parse(ASF::File *file, uint size)
|
||||
void ASF::File::FilePrivate::StreamPropertiesObject::parse(ASF::File *file, long long size)
|
||||
{
|
||||
BaseObject::parse(file, size);
|
||||
if(data.size() < 70) {
|
||||
@@ -256,9 +249,8 @@ ByteVector ASF::File::FilePrivate::ContentDescriptionObject::guid() const
|
||||
return contentDescriptionGuid;
|
||||
}
|
||||
|
||||
void ASF::File::FilePrivate::ContentDescriptionObject::parse(ASF::File *file, uint /*size*/)
|
||||
void ASF::File::FilePrivate::ContentDescriptionObject::parse(ASF::File *file, long long /*size*/)
|
||||
{
|
||||
file->d->contentDescriptionObject = this;
|
||||
const int titleLength = readWORD(file);
|
||||
const int artistLength = readWORD(file);
|
||||
const int copyrightLength = readWORD(file);
|
||||
@@ -297,9 +289,8 @@ ByteVector ASF::File::FilePrivate::ExtendedContentDescriptionObject::guid() cons
|
||||
return extendedContentDescriptionGuid;
|
||||
}
|
||||
|
||||
void ASF::File::FilePrivate::ExtendedContentDescriptionObject::parse(ASF::File *file, uint /*size*/)
|
||||
void ASF::File::FilePrivate::ExtendedContentDescriptionObject::parse(ASF::File *file, long long /*size*/)
|
||||
{
|
||||
file->d->extendedContentDescriptionObject = this;
|
||||
int count = readWORD(file);
|
||||
while(count--) {
|
||||
ASF::Attribute attribute;
|
||||
@@ -312,7 +303,7 @@ ByteVector ASF::File::FilePrivate::ExtendedContentDescriptionObject::render(ASF:
|
||||
{
|
||||
data.clear();
|
||||
data.append(ByteVector::fromShort(attributeData.size(), false));
|
||||
data.append(attributeData.toByteVector(ByteVector::null));
|
||||
data.append(attributeData.toByteVector(""));
|
||||
return BaseObject::render(file);
|
||||
}
|
||||
|
||||
@@ -321,9 +312,8 @@ ByteVector ASF::File::FilePrivate::MetadataObject::guid() const
|
||||
return metadataGuid;
|
||||
}
|
||||
|
||||
void ASF::File::FilePrivate::MetadataObject::parse(ASF::File *file, uint /*size*/)
|
||||
void ASF::File::FilePrivate::MetadataObject::parse(ASF::File *file, long long /*size*/)
|
||||
{
|
||||
file->d->metadataObject = this;
|
||||
int count = readWORD(file);
|
||||
while(count--) {
|
||||
ASF::Attribute attribute;
|
||||
@@ -336,7 +326,7 @@ ByteVector ASF::File::FilePrivate::MetadataObject::render(ASF::File *file)
|
||||
{
|
||||
data.clear();
|
||||
data.append(ByteVector::fromShort(attributeData.size(), false));
|
||||
data.append(attributeData.toByteVector(ByteVector::null));
|
||||
data.append(attributeData.toByteVector(""));
|
||||
return BaseObject::render(file);
|
||||
}
|
||||
|
||||
@@ -345,9 +335,8 @@ ByteVector ASF::File::FilePrivate::MetadataLibraryObject::guid() const
|
||||
return metadataLibraryGuid;
|
||||
}
|
||||
|
||||
void ASF::File::FilePrivate::MetadataLibraryObject::parse(ASF::File *file, uint /*size*/)
|
||||
void ASF::File::FilePrivate::MetadataLibraryObject::parse(ASF::File *file, long long /*size*/)
|
||||
{
|
||||
file->d->metadataLibraryObject = this;
|
||||
int count = readWORD(file);
|
||||
while(count--) {
|
||||
ASF::Attribute attribute;
|
||||
@@ -360,7 +349,7 @@ ByteVector ASF::File::FilePrivate::MetadataLibraryObject::render(ASF::File *file
|
||||
{
|
||||
data.clear();
|
||||
data.append(ByteVector::fromShort(attributeData.size(), false));
|
||||
data.append(attributeData.toByteVector(ByteVector::null));
|
||||
data.append(attributeData.toByteVector(""));
|
||||
return BaseObject::render(file);
|
||||
}
|
||||
|
||||
@@ -374,35 +363,36 @@ ByteVector ASF::File::FilePrivate::HeaderExtensionObject::guid() const
|
||||
return headerExtensionGuid;
|
||||
}
|
||||
|
||||
void ASF::File::FilePrivate::HeaderExtensionObject::parse(ASF::File *file, uint /*size*/)
|
||||
void ASF::File::FilePrivate::HeaderExtensionObject::parse(ASF::File *file, long long /*size*/)
|
||||
{
|
||||
file->d->headerExtensionObject = this;
|
||||
file->seek(18, File::Current);
|
||||
long long dataSize = readDWORD(file);
|
||||
long long dataPos = 0;
|
||||
while(dataPos < dataSize) {
|
||||
ByteVector guid = file->readBlock(16);
|
||||
if(guid.size() != 16) {
|
||||
ByteVector uid = file->readBlock(16);
|
||||
if(uid.size() != 16) {
|
||||
file->setValid(false);
|
||||
break;
|
||||
}
|
||||
bool ok;
|
||||
long long size = readQWORD(file, &ok);
|
||||
if(!ok) {
|
||||
if(!ok || size < 0 || size > dataSize - dataPos) {
|
||||
file->setValid(false);
|
||||
break;
|
||||
}
|
||||
BaseObject *obj;
|
||||
if(guid == metadataGuid) {
|
||||
obj = new MetadataObject();
|
||||
if(uid == metadataGuid) {
|
||||
file->d->metadataObject = new MetadataObject();
|
||||
obj = file->d->metadataObject;
|
||||
}
|
||||
else if(guid == metadataLibraryGuid) {
|
||||
obj = new MetadataLibraryObject();
|
||||
else if(uid == metadataLibraryGuid) {
|
||||
file->d->metadataLibraryObject = new MetadataLibraryObject();
|
||||
obj = file->d->metadataLibraryObject;
|
||||
}
|
||||
else {
|
||||
obj = new UnknownObject(guid);
|
||||
obj = new UnknownObject(uid);
|
||||
}
|
||||
obj->parse(file, (unsigned int)size);
|
||||
obj->parse(file, size);
|
||||
objects.append(obj);
|
||||
dataPos += size;
|
||||
}
|
||||
@@ -411,8 +401,8 @@ void ASF::File::FilePrivate::HeaderExtensionObject::parse(ASF::File *file, uint
|
||||
ByteVector ASF::File::FilePrivate::HeaderExtensionObject::render(ASF::File *file)
|
||||
{
|
||||
data.clear();
|
||||
for(List<BaseObject *>::ConstIterator it = objects.begin(); it != objects.end(); ++it) {
|
||||
data.append((*it)->render(file));
|
||||
for(const auto &object : std::as_const(objects)) {
|
||||
data.append(object->render(file));
|
||||
}
|
||||
data = ByteVector("\x11\xD2\xD3\xAB\xBA\xA9\xcf\x11\x8E\xE6\x00\xC0\x0C\x20\x53\x65\x06\x00", 18) + ByteVector::fromUInt(data.size(), false) + data;
|
||||
return BaseObject::render(file);
|
||||
@@ -423,7 +413,7 @@ ByteVector ASF::File::FilePrivate::CodecListObject::guid() const
|
||||
return codecListGuid;
|
||||
}
|
||||
|
||||
void ASF::File::FilePrivate::CodecListObject::parse(ASF::File *file, uint size)
|
||||
void ASF::File::FilePrivate::CodecListObject::parse(ASF::File *file, long long size)
|
||||
{
|
||||
BaseObject::parse(file, size);
|
||||
if(data.size() <= 20) {
|
||||
@@ -431,7 +421,7 @@ void ASF::File::FilePrivate::CodecListObject::parse(ASF::File *file, uint size)
|
||||
return;
|
||||
}
|
||||
|
||||
uint pos = 16;
|
||||
unsigned int pos = 16;
|
||||
|
||||
const int count = data.toUInt(pos, false);
|
||||
pos += 4;
|
||||
@@ -441,19 +431,19 @@ void ASF::File::FilePrivate::CodecListObject::parse(ASF::File *file, uint size)
|
||||
if(pos >= data.size())
|
||||
break;
|
||||
|
||||
const CodecType type = static_cast<CodecType>(data.toUShort(pos, false));
|
||||
const auto type = static_cast<CodecType>(data.toUShort(pos, false));
|
||||
pos += 2;
|
||||
|
||||
int nameLength = data.toUShort(pos, false);
|
||||
pos += 2;
|
||||
|
||||
const uint namePos = pos;
|
||||
const unsigned int namePos = pos;
|
||||
pos += nameLength * 2;
|
||||
|
||||
const int descLength = data.toUShort(pos, false);
|
||||
pos += 2;
|
||||
|
||||
const uint descPos = pos;
|
||||
const unsigned int descPos = pos;
|
||||
pos += descLength * 2;
|
||||
|
||||
const int infoLength = data.toUShort(pos, false);
|
||||
@@ -473,13 +463,25 @@ void ASF::File::FilePrivate::CodecListObject::parse(ASF::File *file, uint size)
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// 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) :
|
||||
TagLib::File(file),
|
||||
d(new FilePrivate())
|
||||
d(std::make_unique<FilePrivate>())
|
||||
{
|
||||
if(isOpen())
|
||||
read();
|
||||
@@ -487,20 +489,17 @@ ASF::File::File(FileName file, bool, Properties::ReadStyle) :
|
||||
|
||||
ASF::File::File(IOStream *stream, bool, Properties::ReadStyle) :
|
||||
TagLib::File(stream),
|
||||
d(new FilePrivate())
|
||||
d(std::make_unique<FilePrivate>())
|
||||
{
|
||||
if(isOpen())
|
||||
read();
|
||||
}
|
||||
|
||||
ASF::File::~File()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
ASF::File::~File() = default;
|
||||
|
||||
ASF::Tag *ASF::File::tag() const
|
||||
{
|
||||
return d->tag;
|
||||
return d->tag.get();
|
||||
}
|
||||
|
||||
PropertyMap ASF::File::properties() const
|
||||
@@ -520,7 +519,7 @@ PropertyMap ASF::File::setProperties(const PropertyMap &properties)
|
||||
|
||||
ASF::Properties *ASF::File::audioProperties() const
|
||||
{
|
||||
return d->properties;
|
||||
return d->properties.get();
|
||||
}
|
||||
|
||||
bool ASF::File::save()
|
||||
@@ -556,19 +555,15 @@ bool ASF::File::save()
|
||||
d->headerExtensionObject->objects.append(d->metadataLibraryObject);
|
||||
}
|
||||
|
||||
const AttributeListMap allAttributes = d->tag->attributeListMap();
|
||||
|
||||
for(AttributeListMap::ConstIterator it = allAttributes.begin(); it != allAttributes.end(); ++it) {
|
||||
|
||||
const String &name = it->first;
|
||||
const AttributeList &attributes = it->second;
|
||||
d->extendedContentDescriptionObject->attributeData.clear();
|
||||
d->metadataObject->attributeData.clear();
|
||||
d->metadataLibraryObject->attributeData.clear();
|
||||
|
||||
for(const auto &[name, attributes] : std::as_const(d->tag->attributeListMap())) {
|
||||
bool inExtendedContentDescriptionObject = false;
|
||||
bool inMetadataObject = false;
|
||||
|
||||
for(AttributeList::ConstIterator jt = attributes.begin(); jt != attributes.end(); ++jt) {
|
||||
|
||||
const Attribute &attribute = *jt;
|
||||
for(const auto &attribute : attributes) {
|
||||
const bool largeValue = (attribute.dataSize() > 65535);
|
||||
const bool guid = (attribute.type() == Attribute::GuidType);
|
||||
|
||||
@@ -587,12 +582,18 @@ bool ASF::File::save()
|
||||
}
|
||||
|
||||
ByteVector data;
|
||||
for(List<FilePrivate::BaseObject *>::ConstIterator it = d->objects.begin(); it != d->objects.end(); ++it) {
|
||||
data.append((*it)->render(this));
|
||||
for(const auto &object : std::as_const(d->objects)) {
|
||||
data.append(object->render(this));
|
||||
}
|
||||
|
||||
data = headerGuid + ByteVector::fromLongLong(data.size() + 30, false) + ByteVector::fromUInt(d->objects.size(), false) + ByteVector("\x01\x02", 2) + data;
|
||||
insert(data, 0, (TagLib::ulong)d->size);
|
||||
seek(16);
|
||||
writeBlock(ByteVector::fromLongLong(data.size() + 30, false));
|
||||
writeBlock(ByteVector::fromUInt(d->objects.size(), false));
|
||||
writeBlock(ByteVector("\x01\x02", 2));
|
||||
|
||||
insert(data, 30, static_cast<unsigned long>(d->headerSize - 30));
|
||||
|
||||
d->headerSize = data.size() + 30;
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -606,18 +607,17 @@ 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 = std::make_unique<ASF::Tag>();
|
||||
d->properties = std::make_unique<ASF::Properties>();
|
||||
|
||||
bool ok;
|
||||
d->size = readQWORD(this, &ok);
|
||||
d->headerSize = readQWORD(this, &ok);
|
||||
if(!ok) {
|
||||
setValid(false);
|
||||
return;
|
||||
@@ -629,32 +629,39 @@ void ASF::File::read()
|
||||
}
|
||||
seek(2, Current);
|
||||
|
||||
FilePrivate::FilePropertiesObject *filePropertiesObject = nullptr;
|
||||
FilePrivate::StreamPropertiesObject *streamPropertiesObject = nullptr;
|
||||
for(int i = 0; i < numObjects; i++) {
|
||||
guid = readBlock(16);
|
||||
const ByteVector guid = readBlock(16);
|
||||
if(guid.size() != 16) {
|
||||
setValid(false);
|
||||
break;
|
||||
}
|
||||
long size = (long)readQWORD(this, &ok);
|
||||
auto size = readQWORD(this, &ok);
|
||||
if(!ok) {
|
||||
setValid(false);
|
||||
break;
|
||||
}
|
||||
FilePrivate::BaseObject *obj;
|
||||
if(guid == filePropertiesGuid) {
|
||||
obj = new FilePrivate::FilePropertiesObject();
|
||||
filePropertiesObject = new FilePrivate::FilePropertiesObject();
|
||||
obj = filePropertiesObject;
|
||||
}
|
||||
else if(guid == streamPropertiesGuid) {
|
||||
obj = new FilePrivate::StreamPropertiesObject();
|
||||
streamPropertiesObject = new FilePrivate::StreamPropertiesObject();
|
||||
obj = streamPropertiesObject;
|
||||
}
|
||||
else if(guid == contentDescriptionGuid) {
|
||||
obj = new FilePrivate::ContentDescriptionObject();
|
||||
d->contentDescriptionObject = new FilePrivate::ContentDescriptionObject();
|
||||
obj = d->contentDescriptionObject;
|
||||
}
|
||||
else if(guid == extendedContentDescriptionGuid) {
|
||||
obj = new FilePrivate::ExtendedContentDescriptionObject();
|
||||
d->extendedContentDescriptionObject = new FilePrivate::ExtendedContentDescriptionObject();
|
||||
obj = d->extendedContentDescriptionObject;
|
||||
}
|
||||
else if(guid == headerExtensionGuid) {
|
||||
obj = new FilePrivate::HeaderExtensionObject();
|
||||
d->headerExtensionObject = new FilePrivate::HeaderExtensionObject();
|
||||
obj = d->headerExtensionObject;
|
||||
}
|
||||
else if(guid == codecListGuid) {
|
||||
obj = new FilePrivate::CodecListObject();
|
||||
@@ -670,4 +677,10 @@ void ASF::File::read()
|
||||
obj->parse(this, size);
|
||||
d->objects.append(obj);
|
||||
}
|
||||
|
||||
if(!filePropertiesObject || !streamPropertiesObject) {
|
||||
debug("ASF::File::read(): Missing mandatory header objects.");
|
||||
setValid(false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,17 +26,15 @@
|
||||
#ifndef TAGLIB_ASFFILE_H
|
||||
#define TAGLIB_ASFFILE_H
|
||||
|
||||
#include "tag.h"
|
||||
#include "tfile.h"
|
||||
#include "taglib_export.h"
|
||||
#include "tag.h"
|
||||
#include "asfproperties.h"
|
||||
#include "asftag.h"
|
||||
|
||||
namespace TagLib {
|
||||
|
||||
//! An implementation of ASF (WMA) metadata
|
||||
namespace ASF {
|
||||
|
||||
/*!
|
||||
* This implements and provides an interface for ASF files to the
|
||||
* TagLib::Tag and TagLib::AudioProperties interfaces by way of implementing
|
||||
@@ -73,7 +71,10 @@ namespace TagLib {
|
||||
/*!
|
||||
* Destroys this instance of the File.
|
||||
*/
|
||||
virtual ~File();
|
||||
~File() override;
|
||||
|
||||
File(const File &) = delete;
|
||||
File &operator=(const File &) = delete;
|
||||
|
||||
/*!
|
||||
* Returns a pointer to the ASF tag of the file.
|
||||
@@ -85,48 +86,53 @@ namespace TagLib {
|
||||
* deleted by the user. It will be deleted when the file (object) is
|
||||
* destroyed.
|
||||
*/
|
||||
virtual Tag *tag() const;
|
||||
Tag *tag() const override;
|
||||
|
||||
/*!
|
||||
* Implements the unified property interface -- export function.
|
||||
*/
|
||||
PropertyMap properties() const;
|
||||
PropertyMap properties() const override;
|
||||
|
||||
/*!
|
||||
* Removes unsupported properties. Forwards to the actual Tag's
|
||||
* removeUnsupportedProperties() function.
|
||||
*/
|
||||
void removeUnsupportedProperties(const StringList &properties);
|
||||
void removeUnsupportedProperties(const StringList &properties) override;
|
||||
|
||||
/*!
|
||||
* Implements the unified property interface -- import function.
|
||||
*/
|
||||
PropertyMap setProperties(const PropertyMap &);
|
||||
PropertyMap setProperties(const PropertyMap &) override;
|
||||
|
||||
/*!
|
||||
* Returns the ASF audio properties for this file.
|
||||
*/
|
||||
virtual Properties *audioProperties() const;
|
||||
Properties *audioProperties() const override;
|
||||
|
||||
/*!
|
||||
* Save the file.
|
||||
*
|
||||
* This returns true if the save was successful.
|
||||
*
|
||||
* \warning In the current implementation, it's dangerous to call save()
|
||||
* repeatedly. At worst it will corrupt the file.
|
||||
*/
|
||||
virtual bool save();
|
||||
bool save() override;
|
||||
|
||||
/*!
|
||||
* 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();
|
||||
|
||||
class FilePrivate;
|
||||
FilePrivate *d;
|
||||
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
|
||||
std::unique_ptr<FilePrivate> d;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace ASF
|
||||
} // namespace TagLib
|
||||
|
||||
#endif
|
||||
|
||||
@@ -23,18 +23,14 @@
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#include <taglib.h>
|
||||
#include <tdebug.h>
|
||||
#include <trefcounter.h>
|
||||
|
||||
#include "asfattribute.h"
|
||||
#include "asffile.h"
|
||||
#include "asfpicture.h"
|
||||
|
||||
#include "asffile.h"
|
||||
#include "asfutils.h"
|
||||
|
||||
using namespace TagLib;
|
||||
|
||||
class ASF::Picture::PicturePrivate : public RefCounter
|
||||
class ASF::Picture::PicturePrivate
|
||||
{
|
||||
public:
|
||||
bool valid;
|
||||
@@ -48,23 +44,14 @@ public:
|
||||
// Picture class members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
ASF::Picture::Picture()
|
||||
ASF::Picture::Picture() :
|
||||
d(std::make_shared<PicturePrivate>())
|
||||
{
|
||||
d = new PicturePrivate();
|
||||
d->valid = true;
|
||||
}
|
||||
|
||||
ASF::Picture::Picture(const Picture& other)
|
||||
: d(other.d)
|
||||
{
|
||||
d->ref();
|
||||
}
|
||||
|
||||
ASF::Picture::~Picture()
|
||||
{
|
||||
if(d->deref())
|
||||
delete d;
|
||||
}
|
||||
ASF::Picture::Picture(const Picture &) = default;
|
||||
ASF::Picture::~Picture() = default;
|
||||
|
||||
bool ASF::Picture::isValid() const
|
||||
{
|
||||
@@ -118,23 +105,22 @@ int ASF::Picture::dataSize() const
|
||||
d->picture.size();
|
||||
}
|
||||
|
||||
ASF::Picture& ASF::Picture::operator=(const ASF::Picture& other)
|
||||
ASF::Picture &ASF::Picture::operator=(const ASF::Picture &) = default;
|
||||
|
||||
void ASF::Picture::swap(Picture &other) noexcept
|
||||
{
|
||||
if(other.d != d) {
|
||||
if(d->deref())
|
||||
delete d;
|
||||
d = other.d;
|
||||
d->ref();
|
||||
}
|
||||
return *this;
|
||||
using std::swap;
|
||||
|
||||
swap(d, other.d);
|
||||
}
|
||||
|
||||
ByteVector ASF::Picture::render() const
|
||||
{
|
||||
if(!isValid())
|
||||
return ByteVector::null;
|
||||
return ByteVector();
|
||||
|
||||
return
|
||||
ByteVector((char)d->type) +
|
||||
ByteVector(static_cast<char>(d->type)) +
|
||||
ByteVector::fromUInt(d->picture.size(), false) +
|
||||
renderString(d->mimeType) +
|
||||
renderString(d->description) +
|
||||
@@ -147,8 +133,8 @@ void ASF::Picture::parse(const ByteVector& bytes)
|
||||
if(bytes.size() < 9)
|
||||
return;
|
||||
int pos = 0;
|
||||
d->type = (Type)bytes[0]; ++pos;
|
||||
const uint dataLen = bytes.toUInt(pos, false); pos+=4;
|
||||
d->type = static_cast<Type>(bytes[0]); ++pos;
|
||||
const unsigned int dataLen = bytes.toUInt(pos, false); pos+=4;
|
||||
|
||||
const ByteVector nullStringTerminator(2, 0);
|
||||
|
||||
@@ -169,7 +155,6 @@ void ASF::Picture::parse(const ByteVector& bytes)
|
||||
|
||||
d->picture = bytes.mid(pos, dataLen);
|
||||
d->valid = true;
|
||||
return;
|
||||
}
|
||||
|
||||
ASF::Picture ASF::Picture::fromInvalid()
|
||||
@@ -178,4 +163,3 @@ ASF::Picture ASF::Picture::fromInvalid()
|
||||
ret.d->valid = false;
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
|
||||
#include "tstring.h"
|
||||
#include "tbytevector.h"
|
||||
#include "tpicturetype.h"
|
||||
#include "taglib_export.h"
|
||||
#include "attachedpictureframe.h"
|
||||
|
||||
@@ -52,50 +53,7 @@ namespace TagLib
|
||||
/*!
|
||||
* This describes the function or content of the picture.
|
||||
*/
|
||||
enum Type {
|
||||
//! A type not enumerated below
|
||||
Other = 0x00,
|
||||
//! 32x32 PNG image that should be used as the file icon
|
||||
FileIcon = 0x01,
|
||||
//! File icon of a different size or format
|
||||
OtherFileIcon = 0x02,
|
||||
//! Front cover image of the album
|
||||
FrontCover = 0x03,
|
||||
//! Back cover image of the album
|
||||
BackCover = 0x04,
|
||||
//! Inside leaflet page of the album
|
||||
LeafletPage = 0x05,
|
||||
//! Image from the album itself
|
||||
Media = 0x06,
|
||||
//! Picture of the lead artist or soloist
|
||||
LeadArtist = 0x07,
|
||||
//! Picture of the artist or performer
|
||||
Artist = 0x08,
|
||||
//! Picture of the conductor
|
||||
Conductor = 0x09,
|
||||
//! Picture of the band or orchestra
|
||||
Band = 0x0A,
|
||||
//! Picture of the composer
|
||||
Composer = 0x0B,
|
||||
//! Picture of the lyricist or text writer
|
||||
Lyricist = 0x0C,
|
||||
//! Picture of the recording location or studio
|
||||
RecordingLocation = 0x0D,
|
||||
//! Picture of the artists during recording
|
||||
DuringRecording = 0x0E,
|
||||
//! Picture of the artists during performance
|
||||
DuringPerformance = 0x0F,
|
||||
//! Picture from a movie or video related to the track
|
||||
MovieScreenCapture = 0x10,
|
||||
//! Picture of a large, coloured fish
|
||||
ColouredFish = 0x11,
|
||||
//! Illustration related to the track
|
||||
Illustration = 0x12,
|
||||
//! Logo of the band or performer
|
||||
BandLogo = 0x13,
|
||||
//! Logo of the publisher (record company)
|
||||
PublisherLogo = 0x14
|
||||
};
|
||||
DECLARE_PICTURE_TYPE_ENUM(Type)
|
||||
|
||||
/*!
|
||||
* Constructs an empty picture.
|
||||
@@ -117,6 +75,11 @@ namespace TagLib
|
||||
*/
|
||||
Picture& operator=(const Picture& other);
|
||||
|
||||
/*!
|
||||
* Exchanges the content of the Picture by the content of \a other.
|
||||
*/
|
||||
void swap(Picture &other) noexcept;
|
||||
|
||||
/*!
|
||||
* Returns true if Picture stores valid picture
|
||||
*/
|
||||
@@ -209,9 +172,10 @@ namespace TagLib
|
||||
|
||||
private:
|
||||
class PicturePrivate;
|
||||
PicturePrivate *d;
|
||||
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
|
||||
std::shared_ptr<PicturePrivate> d;
|
||||
};
|
||||
}
|
||||
}
|
||||
} // namespace ASF
|
||||
} // namespace TagLib
|
||||
|
||||
#endif // ASFPICTURE_H
|
||||
|
||||
@@ -23,8 +23,6 @@
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#include <tdebug.h>
|
||||
#include <tstring.h>
|
||||
#include "asfproperties.h"
|
||||
|
||||
using namespace TagLib;
|
||||
@@ -32,24 +30,15 @@ using namespace TagLib;
|
||||
class ASF::Properties::PropertiesPrivate
|
||||
{
|
||||
public:
|
||||
PropertiesPrivate() :
|
||||
length(0),
|
||||
bitrate(0),
|
||||
sampleRate(0),
|
||||
channels(0),
|
||||
bitsPerSample(0),
|
||||
codec(ASF::Properties::Unknown),
|
||||
encrypted(false) {}
|
||||
|
||||
int length;
|
||||
int bitrate;
|
||||
int sampleRate;
|
||||
int channels;
|
||||
int bitsPerSample;
|
||||
ASF::Properties::Codec codec;
|
||||
int length { 0 };
|
||||
int bitrate { 0 };
|
||||
int sampleRate { 0 };
|
||||
int channels { 0 };
|
||||
int bitsPerSample { 0 };
|
||||
ASF::Properties::Codec codec { ASF::Properties::Unknown };
|
||||
String codecName;
|
||||
String codecDescription;
|
||||
bool encrypted;
|
||||
bool encrypted { false };
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -58,24 +47,11 @@ public:
|
||||
|
||||
ASF::Properties::Properties() :
|
||||
AudioProperties(AudioProperties::Average),
|
||||
d(new PropertiesPrivate())
|
||||
d(std::make_unique<PropertiesPrivate>())
|
||||
{
|
||||
}
|
||||
|
||||
ASF::Properties::~Properties()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
|
||||
int ASF::Properties::length() const
|
||||
{
|
||||
return lengthInSeconds();
|
||||
}
|
||||
|
||||
int ASF::Properties::lengthInSeconds() const
|
||||
{
|
||||
return d->length / 1000;
|
||||
}
|
||||
ASF::Properties::~Properties() = default;
|
||||
|
||||
int ASF::Properties::lengthInMilliseconds() const
|
||||
{
|
||||
@@ -126,11 +102,6 @@ bool ASF::Properties::isEncrypted() const
|
||||
// private members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void ASF::Properties::setLength(int /*length*/)
|
||||
{
|
||||
debug("ASF::Properties::setLength() -- This method is deprecated. Do not use.");
|
||||
}
|
||||
|
||||
void ASF::Properties::setLengthInMilliseconds(int value)
|
||||
{
|
||||
d->length = value;
|
||||
|
||||
@@ -26,14 +26,12 @@
|
||||
#ifndef TAGLIB_ASFPROPERTIES_H
|
||||
#define TAGLIB_ASFPROPERTIES_H
|
||||
|
||||
#include "audioproperties.h"
|
||||
#include "tstring.h"
|
||||
#include "taglib_export.h"
|
||||
#include "audioproperties.h"
|
||||
|
||||
namespace TagLib {
|
||||
|
||||
namespace ASF {
|
||||
|
||||
//! An implementation of ASF audio properties
|
||||
class TAGLIB_EXPORT Properties : public AudioProperties
|
||||
{
|
||||
@@ -78,49 +76,32 @@ namespace TagLib {
|
||||
/*!
|
||||
* Destroys this ASF::Properties instance.
|
||||
*/
|
||||
virtual ~Properties();
|
||||
~Properties() override;
|
||||
|
||||
/*!
|
||||
* Returns the length of the file in seconds. The length is rounded down to
|
||||
* the nearest whole second.
|
||||
*
|
||||
* \note This method is just an alias of lengthInSeconds().
|
||||
*
|
||||
* \deprecated
|
||||
*/
|
||||
virtual int length() const;
|
||||
|
||||
/*!
|
||||
* Returns the length of the file in seconds. The length is rounded down to
|
||||
* the nearest whole second.
|
||||
*
|
||||
* \see lengthInMilliseconds()
|
||||
*/
|
||||
// BIC: make virtual
|
||||
int lengthInSeconds() const;
|
||||
Properties(const Properties &) = delete;
|
||||
Properties &operator=(const Properties &) = delete;
|
||||
|
||||
/*!
|
||||
* Returns the length of the file in milliseconds.
|
||||
*
|
||||
* \see lengthInSeconds()
|
||||
*/
|
||||
// BIC: make virtual
|
||||
int lengthInMilliseconds() const;
|
||||
int lengthInMilliseconds() const override;
|
||||
|
||||
/*!
|
||||
* Returns the average bit rate of the file in kb/s.
|
||||
*/
|
||||
virtual int bitrate() const;
|
||||
int bitrate() const override;
|
||||
|
||||
/*!
|
||||
* Returns the sample rate in Hz.
|
||||
*/
|
||||
virtual int sampleRate() const;
|
||||
int sampleRate() const override;
|
||||
|
||||
/*!
|
||||
* Returns the number of audio channels.
|
||||
*/
|
||||
virtual int channels() const;
|
||||
int channels() const override;
|
||||
|
||||
/*!
|
||||
* Returns the number of bits per audio sample.
|
||||
@@ -160,9 +141,6 @@ namespace TagLib {
|
||||
bool isEncrypted() const;
|
||||
|
||||
#ifndef DO_NOT_DOCUMENT
|
||||
// deprecated
|
||||
void setLength(int value);
|
||||
|
||||
void setLengthInMilliseconds(int value);
|
||||
void setBitrate(int value);
|
||||
void setSampleRate(int value);
|
||||
@@ -176,11 +154,9 @@ namespace TagLib {
|
||||
|
||||
private:
|
||||
class PropertiesPrivate;
|
||||
PropertiesPrivate *d;
|
||||
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
|
||||
std::unique_ptr<PropertiesPrivate> d;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} // namespace ASF
|
||||
} // namespace TagLib
|
||||
#endif
|
||||
|
||||
@@ -23,11 +23,29 @@
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#include <tpropertymap.h>
|
||||
#include "asftag.h"
|
||||
|
||||
#include <array>
|
||||
#include <utility>
|
||||
|
||||
#include "tpropertymap.h"
|
||||
#include "asfattribute.h"
|
||||
#include "asfpicture.h"
|
||||
|
||||
using namespace TagLib;
|
||||
|
||||
namespace
|
||||
{
|
||||
StringList attributeListToStringList(const ASF::AttributeList &attributes)
|
||||
{
|
||||
StringList strs;
|
||||
for(const auto &attribute : attributes) {
|
||||
strs.append(attribute.toString());
|
||||
}
|
||||
return strs;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
class ASF::Tag::TagPrivate
|
||||
{
|
||||
public:
|
||||
@@ -39,16 +57,12 @@ public:
|
||||
AttributeListMap attributeListMap;
|
||||
};
|
||||
|
||||
ASF::Tag::Tag()
|
||||
: TagLib::Tag()
|
||||
ASF::Tag::Tag() :
|
||||
d(std::make_unique<TagPrivate>())
|
||||
{
|
||||
d = new TagPrivate;
|
||||
}
|
||||
|
||||
ASF::Tag::~Tag()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
ASF::Tag::~Tag() = default;
|
||||
|
||||
String ASF::Tag::title() const
|
||||
{
|
||||
@@ -63,8 +77,9 @@ String ASF::Tag::artist() const
|
||||
String ASF::Tag::album() const
|
||||
{
|
||||
if(d->attributeListMap.contains("WM/AlbumTitle"))
|
||||
return d->attributeListMap["WM/AlbumTitle"][0].toString();
|
||||
return String::null;
|
||||
return joinTagValues(
|
||||
attributeListToStringList(d->attributeListMap.value("WM/AlbumTitle")));
|
||||
return String();
|
||||
}
|
||||
|
||||
String ASF::Tag::copyright() const
|
||||
@@ -95,8 +110,7 @@ unsigned int ASF::Tag::track() const
|
||||
const ASF::Attribute attr = d->attributeListMap["WM/TrackNumber"][0];
|
||||
if(attr.type() == ASF::Attribute::DWordType)
|
||||
return attr.toUInt();
|
||||
else
|
||||
return attr.toString().toInt();
|
||||
return attr.toString().toInt();
|
||||
}
|
||||
if(d->attributeListMap.contains("WM/Track"))
|
||||
return d->attributeListMap["WM/Track"][0].toUInt();
|
||||
@@ -106,8 +120,9 @@ unsigned int ASF::Tag::track() const
|
||||
String ASF::Tag::genre() const
|
||||
{
|
||||
if(d->attributeListMap.contains("WM/Genre"))
|
||||
return d->attributeListMap["WM/Genre"][0].toString();
|
||||
return String::null;
|
||||
return joinTagValues(
|
||||
attributeListToStringList(d->attributeListMap.value("WM/Genre")));
|
||||
return String();
|
||||
}
|
||||
|
||||
void ASF::Tag::setTitle(const String &value)
|
||||
@@ -145,12 +160,12 @@ void ASF::Tag::setGenre(const String &value)
|
||||
setAttribute("WM/Genre", value);
|
||||
}
|
||||
|
||||
void ASF::Tag::setYear(uint value)
|
||||
void ASF::Tag::setYear(unsigned int value)
|
||||
{
|
||||
setAttribute("WM/Year", String::number(value));
|
||||
}
|
||||
|
||||
void ASF::Tag::setTrack(uint value)
|
||||
void ASF::Tag::setTrack(unsigned int value)
|
||||
{
|
||||
setAttribute("WM/TrackNumber", String::number(value));
|
||||
}
|
||||
@@ -182,9 +197,9 @@ ASF::AttributeList ASF::Tag::attribute(const String &name) const
|
||||
|
||||
void ASF::Tag::setAttribute(const String &name, const Attribute &attribute)
|
||||
{
|
||||
AttributeList value;
|
||||
value.append(attribute);
|
||||
d->attributeListMap.insert(name, value);
|
||||
AttributeList val;
|
||||
val.append(attribute);
|
||||
d->attributeListMap.insert(name, val);
|
||||
}
|
||||
|
||||
void ASF::Tag::setAttribute(const String &name, const AttributeList &values)
|
||||
@@ -210,58 +225,78 @@ bool ASF::Tag::isEmpty() const
|
||||
d->attributeListMap.isEmpty();
|
||||
}
|
||||
|
||||
static const char *keyTranslation[][2] = {
|
||||
{ "WM/AlbumTitle", "ALBUM" },
|
||||
{ "WM/AlbumArtist", "ALBUMARTIST" },
|
||||
{ "WM/Composer", "COMPOSER" },
|
||||
{ "WM/Writer", "WRITER" },
|
||||
{ "WM/Conductor", "CONDUCTOR" },
|
||||
{ "WM/ModifiedBy", "REMIXER" },
|
||||
{ "WM/Year", "DATE" },
|
||||
{ "WM/OriginalReleaseYear", "ORIGINALDATE" },
|
||||
{ "WM/Producer", "PRODUCER" },
|
||||
{ "WM/ContentGroupDescription", "GROUPING" },
|
||||
{ "WM/SubTitle", "SUBTITLE" },
|
||||
{ "WM/SetSubTitle", "DISCSUBTITLE" },
|
||||
{ "WM/TrackNumber", "TRACKNUMBER" },
|
||||
{ "WM/PartOfSet", "DISCNUMBER" },
|
||||
{ "WM/Genre", "GENRE" },
|
||||
{ "WM/BeatsPerMinute", "BPM" },
|
||||
{ "WM/Mood", "MOOD" },
|
||||
{ "WM/ISRC", "ISRC" },
|
||||
{ "WM/Lyrics", "LYRICS" },
|
||||
{ "WM/Media", "MEDIA" },
|
||||
{ "WM/Publisher", "LABEL" },
|
||||
{ "WM/CatalogNo", "CATALOGNUMBER" },
|
||||
{ "WM/Barcode", "BARCODE" },
|
||||
{ "WM/EncodedBy", "ENCODEDBY" },
|
||||
{ "WM/AlbumSortOrder", "ALBUMSORT" },
|
||||
{ "WM/AlbumArtistSortOrder", "ALBUMARTISTSORT" },
|
||||
{ "WM/ArtistSortOrder", "ARTISTSORT" },
|
||||
{ "WM/TitleSortOrder", "TITLESORT" },
|
||||
{ "WM/Script", "SCRIPT" },
|
||||
{ "WM/Language", "LANGUAGE" },
|
||||
{ "MusicBrainz/Track Id", "MUSICBRAINZ_TRACKID" },
|
||||
{ "MusicBrainz/Artist Id", "MUSICBRAINZ_ARTISTID" },
|
||||
{ "MusicBrainz/Album Id", "MUSICBRAINZ_ALBUMID" },
|
||||
{ "MusicBrainz/Album Artist Id", "MUSICBRAINZ_ALBUMARTISTID" },
|
||||
{ "MusicBrainz/Release Group Id", "MUSICBRAINZ_RELEASEGROUPID" },
|
||||
{ "MusicBrainz/Work Id", "MUSICBRAINZ_WORKID" },
|
||||
{ "MusicIP/PUID", "MUSICIP_PUID" },
|
||||
{ "Acoustid/Id", "ACOUSTID_ID" },
|
||||
{ "Acoustid/Fingerprint", "ACOUSTID_FINGERPRINT" },
|
||||
};
|
||||
namespace
|
||||
{
|
||||
constexpr std::array keyTranslation {
|
||||
std::pair("WM/AlbumTitle", "ALBUM"),
|
||||
std::pair("WM/AlbumArtist", "ALBUMARTIST"),
|
||||
std::pair("WM/AuthorURL", "ARTISTWEBPAGE"),
|
||||
std::pair("WM/Composer", "COMPOSER"),
|
||||
std::pair("WM/Writer", "LYRICIST"),
|
||||
std::pair("WM/Conductor", "CONDUCTOR"),
|
||||
std::pair("WM/ModifiedBy", "REMIXER"),
|
||||
std::pair("WM/Year", "DATE"),
|
||||
std::pair("WM/OriginalAlbumTitle", "ORIGINALALBUM"),
|
||||
std::pair("WM/OriginalArtist", "ORIGINALARTIST"),
|
||||
std::pair("WM/OriginalFilename", "ORIGINALFILENAME"),
|
||||
std::pair("WM/OriginalLyricist", "ORIGINALLYRICIST"),
|
||||
std::pair("WM/OriginalReleaseYear", "ORIGINALDATE"),
|
||||
std::pair("WM/Producer", "PRODUCER"),
|
||||
std::pair("WM/ContentGroupDescription", "WORK"),
|
||||
std::pair("WM/SubTitle", "SUBTITLE"),
|
||||
std::pair("WM/SetSubTitle", "DISCSUBTITLE"),
|
||||
std::pair("WM/TrackNumber", "TRACKNUMBER"),
|
||||
std::pair("WM/PartOfSet", "DISCNUMBER"),
|
||||
std::pair("WM/Genre", "GENRE"),
|
||||
std::pair("WM/BeatsPerMinute", "BPM"),
|
||||
std::pair("WM/Mood", "MOOD"),
|
||||
std::pair("WM/InitialKey", "INITIALKEY"),
|
||||
std::pair("WM/ISRC", "ISRC"),
|
||||
std::pair("WM/Lyrics", "LYRICS"),
|
||||
std::pair("WM/Media", "MEDIA"),
|
||||
std::pair("WM/Publisher", "LABEL"),
|
||||
std::pair("WM/CatalogNo", "CATALOGNUMBER"),
|
||||
std::pair("WM/Barcode", "BARCODE"),
|
||||
std::pair("WM/EncodedBy", "ENCODEDBY"),
|
||||
std::pair("WM/EncodingSettings", "ENCODING"),
|
||||
std::pair("WM/EncodingTime", "ENCODINGTIME"),
|
||||
std::pair("WM/AudioFileURL", "FILEWEBPAGE"),
|
||||
std::pair("WM/AlbumSortOrder", "ALBUMSORT"),
|
||||
std::pair("WM/AlbumArtistSortOrder", "ALBUMARTISTSORT"),
|
||||
std::pair("WM/ArtistSortOrder", "ARTISTSORT"),
|
||||
std::pair("WM/TitleSortOrder", "TITLESORT"),
|
||||
std::pair("WM/Script", "SCRIPT"),
|
||||
std::pair("WM/Language", "LANGUAGE"),
|
||||
std::pair("WM/ARTISTS", "ARTISTS"),
|
||||
std::pair("ASIN", "ASIN"),
|
||||
std::pair("MusicBrainz/Track Id", "MUSICBRAINZ_TRACKID"),
|
||||
std::pair("MusicBrainz/Artist Id", "MUSICBRAINZ_ARTISTID"),
|
||||
std::pair("MusicBrainz/Album Id", "MUSICBRAINZ_ALBUMID"),
|
||||
std::pair("MusicBrainz/Album Artist Id", "MUSICBRAINZ_ALBUMARTISTID"),
|
||||
std::pair("MusicBrainz/Album Release Country", "RELEASECOUNTRY"),
|
||||
std::pair("MusicBrainz/Album Status", "RELEASESTATUS"),
|
||||
std::pair("MusicBrainz/Album Type", "RELEASETYPE"),
|
||||
std::pair("MusicBrainz/Release Group Id", "MUSICBRAINZ_RELEASEGROUPID"),
|
||||
std::pair("MusicBrainz/Release Track Id", "MUSICBRAINZ_RELEASETRACKID"),
|
||||
std::pair("MusicBrainz/Work Id", "MUSICBRAINZ_WORKID"),
|
||||
std::pair("MusicIP/PUID", "MUSICIP_PUID"),
|
||||
std::pair("Acoustid/Id", "ACOUSTID_ID"),
|
||||
std::pair("Acoustid/Fingerprint", "ACOUSTID_FINGERPRINT"),
|
||||
};
|
||||
|
||||
String translateKey(const String &key)
|
||||
{
|
||||
for(const auto &[k, t] : keyTranslation) {
|
||||
if(key == k)
|
||||
return t;
|
||||
}
|
||||
|
||||
return String();
|
||||
}
|
||||
} // namespace
|
||||
|
||||
PropertyMap ASF::Tag::properties() const
|
||||
{
|
||||
static Map<String, String> keyMap;
|
||||
if(keyMap.isEmpty()) {
|
||||
int numKeys = sizeof(keyTranslation) / sizeof(keyTranslation[0]);
|
||||
for(int i = 0; i < numKeys; i++) {
|
||||
keyMap[keyTranslation[i][0]] = keyTranslation[i][1];
|
||||
}
|
||||
}
|
||||
|
||||
PropertyMap props;
|
||||
|
||||
if(!d->title.isEmpty()) {
|
||||
@@ -277,25 +312,23 @@ PropertyMap ASF::Tag::properties() const
|
||||
props["COMMENT"] = d->comment;
|
||||
}
|
||||
|
||||
ASF::AttributeListMap::ConstIterator it = d->attributeListMap.begin();
|
||||
for(; it != d->attributeListMap.end(); ++it) {
|
||||
if(keyMap.contains(it->first)) {
|
||||
String key = keyMap[it->first];
|
||||
AttributeList::ConstIterator it2 = it->second.begin();
|
||||
for(; it2 != it->second.end(); ++it2) {
|
||||
for(const auto &[k, attributes] : std::as_const(d->attributeListMap)) {
|
||||
const String key = translateKey(k);
|
||||
if(!key.isEmpty()) {
|
||||
for(const auto &attr : attributes) {
|
||||
if(key == "TRACKNUMBER") {
|
||||
if(it2->type() == ASF::Attribute::DWordType)
|
||||
props.insert(key, String::number(it2->toUInt()));
|
||||
if(attr.type() == ASF::Attribute::DWordType)
|
||||
props.insert(key, String::number(attr.toUInt()));
|
||||
else
|
||||
props.insert(key, it2->toString());
|
||||
props.insert(key, attr.toString());
|
||||
}
|
||||
else {
|
||||
props.insert(key, it2->toString());
|
||||
props.insert(key, attr.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
props.unsupportedData().append(it->first);
|
||||
props.addUnsupportedData(k);
|
||||
}
|
||||
}
|
||||
return props;
|
||||
@@ -303,70 +336,116 @@ PropertyMap ASF::Tag::properties() const
|
||||
|
||||
void ASF::Tag::removeUnsupportedProperties(const StringList &props)
|
||||
{
|
||||
StringList::ConstIterator it = props.begin();
|
||||
for(; it != props.end(); ++it)
|
||||
d->attributeListMap.erase(*it);
|
||||
for(const auto &prop : props)
|
||||
d->attributeListMap.erase(prop);
|
||||
}
|
||||
|
||||
PropertyMap ASF::Tag::setProperties(const PropertyMap &props)
|
||||
{
|
||||
static Map<String, String> reverseKeyMap;
|
||||
if(reverseKeyMap.isEmpty()) {
|
||||
int numKeys = sizeof(keyTranslation) / sizeof(keyTranslation[0]);
|
||||
for(int i = 0; i < numKeys; i++) {
|
||||
reverseKeyMap[keyTranslation[i][1]] = keyTranslation[i][0];
|
||||
for(const auto &[k, t] : keyTranslation) {
|
||||
reverseKeyMap[t] = k;
|
||||
}
|
||||
}
|
||||
|
||||
PropertyMap origProps = properties();
|
||||
PropertyMap::ConstIterator it = origProps.begin();
|
||||
for(; it != origProps.end(); ++it) {
|
||||
if(!props.contains(it->first) || props[it->first].isEmpty()) {
|
||||
if(it->first == "TITLE") {
|
||||
d->title = String::null;
|
||||
const PropertyMap origProps = properties();
|
||||
for(const auto &[prop, _] : origProps) {
|
||||
if(!props.contains(prop) || props[prop].isEmpty()) {
|
||||
if(prop == "TITLE") {
|
||||
d->title.clear();
|
||||
}
|
||||
else if(it->first == "ARTIST") {
|
||||
d->artist = String::null;
|
||||
else if(prop == "ARTIST") {
|
||||
d->artist.clear();
|
||||
}
|
||||
else if(it->first == "COMMENT") {
|
||||
d->comment = String::null;
|
||||
else if(prop == "COMMENT") {
|
||||
d->comment.clear();
|
||||
}
|
||||
else if(it->first == "COPYRIGHT") {
|
||||
d->copyright = String::null;
|
||||
else if(prop == "COPYRIGHT") {
|
||||
d->copyright.clear();
|
||||
}
|
||||
else {
|
||||
d->attributeListMap.erase(reverseKeyMap[it->first]);
|
||||
d->attributeListMap.erase(reverseKeyMap[prop]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PropertyMap ignoredProps;
|
||||
it = props.begin();
|
||||
for(; it != props.end(); ++it) {
|
||||
if(reverseKeyMap.contains(it->first)) {
|
||||
String name = reverseKeyMap[it->first];
|
||||
for(const auto &[prop, attributes] : props) {
|
||||
if(reverseKeyMap.contains(prop)) {
|
||||
String name = reverseKeyMap[prop];
|
||||
removeItem(name);
|
||||
StringList::ConstIterator it2 = it->second.begin();
|
||||
for(; it2 != it->second.end(); ++it2) {
|
||||
addAttribute(name, *it2);
|
||||
for(const auto &attr : attributes) {
|
||||
addAttribute(name, attr);
|
||||
}
|
||||
}
|
||||
else if(it->first == "TITLE") {
|
||||
d->title = it->second.toString();
|
||||
else if(prop == "TITLE") {
|
||||
d->title = attributes.toString();
|
||||
}
|
||||
else if(it->first == "ARTIST") {
|
||||
d->artist = it->second.toString();
|
||||
else if(prop == "ARTIST") {
|
||||
d->artist = attributes.toString();
|
||||
}
|
||||
else if(it->first == "COMMENT") {
|
||||
d->comment = it->second.toString();
|
||||
else if(prop == "COMMENT") {
|
||||
d->comment = attributes.toString();
|
||||
}
|
||||
else if(it->first == "COPYRIGHT") {
|
||||
d->copyright = it->second.toString();
|
||||
else if(prop == "COPYRIGHT") {
|
||||
d->copyright = attributes.toString();
|
||||
}
|
||||
else {
|
||||
ignoredProps.insert(it->first, it->second);
|
||||
ignoredProps.insert(prop, attributes);
|
||||
}
|
||||
}
|
||||
|
||||
return ignoredProps;
|
||||
}
|
||||
|
||||
StringList ASF::Tag::complexPropertyKeys() const
|
||||
{
|
||||
StringList keys;
|
||||
if(d->attributeListMap.contains("WM/Picture")) {
|
||||
keys.append("PICTURE");
|
||||
}
|
||||
return keys;
|
||||
}
|
||||
|
||||
List<VariantMap> ASF::Tag::complexProperties(const String &key) const
|
||||
{
|
||||
List<VariantMap> props;
|
||||
const String uppercaseKey = key.upper();
|
||||
if(uppercaseKey == "PICTURE") {
|
||||
const AttributeList pictures = d->attributeListMap.value("WM/Picture");
|
||||
for(const Attribute &attr : pictures) {
|
||||
ASF::Picture picture = attr.toPicture();
|
||||
VariantMap property;
|
||||
property.insert("data", picture.picture());
|
||||
property.insert("mimeType", picture.mimeType());
|
||||
property.insert("description", picture.description());
|
||||
property.insert("pictureType",
|
||||
ASF::Picture::typeToString(picture.type()));
|
||||
props.append(property);
|
||||
}
|
||||
}
|
||||
return props;
|
||||
}
|
||||
|
||||
bool ASF::Tag::setComplexProperties(const String &key, const List<VariantMap> &value)
|
||||
{
|
||||
const String uppercaseKey = key.upper();
|
||||
if(uppercaseKey == "PICTURE") {
|
||||
removeItem("WM/Picture");
|
||||
|
||||
for(const auto &property : value) {
|
||||
ASF::Picture picture;
|
||||
picture.setPicture(property.value("data").value<ByteVector>());
|
||||
picture.setMimeType(property.value("mimeType").value<String>());
|
||||
picture.setDescription(property.value("description").value<String>());
|
||||
picture.setType(ASF::Picture::typeFromString(
|
||||
property.value("pictureType").value<String>()));
|
||||
addAttribute("WM/Picture", Attribute(picture));
|
||||
}
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -26,18 +26,18 @@
|
||||
#ifndef TAGLIB_ASFTAG_H
|
||||
#define TAGLIB_ASFTAG_H
|
||||
|
||||
#include "tag.h"
|
||||
#include "tlist.h"
|
||||
#include "tmap.h"
|
||||
#include "taglib_export.h"
|
||||
#include "tag.h"
|
||||
#include "asfattribute.h"
|
||||
|
||||
namespace TagLib {
|
||||
|
||||
namespace ASF {
|
||||
|
||||
typedef List<Attribute> AttributeList;
|
||||
typedef Map<String, AttributeList> AttributeListMap;
|
||||
using AttributeList = List<Attribute>;
|
||||
using AttributeListMap = Map<String, AttributeList>;
|
||||
|
||||
class TAGLIB_EXPORT Tag : public TagLib::Tag {
|
||||
|
||||
@@ -47,34 +47,37 @@ namespace TagLib {
|
||||
|
||||
Tag();
|
||||
|
||||
virtual ~Tag();
|
||||
~Tag() override;
|
||||
|
||||
Tag(const Tag &) = delete;
|
||||
Tag &operator=(const Tag &) = delete;
|
||||
|
||||
/*!
|
||||
* Returns the track name.
|
||||
*/
|
||||
virtual String title() const;
|
||||
String title() const override;
|
||||
|
||||
/*!
|
||||
* Returns the artist name.
|
||||
*/
|
||||
virtual String artist() const;
|
||||
String artist() const override;
|
||||
|
||||
/*!
|
||||
* Returns the album name; if no album name is present in the tag
|
||||
* String::null will be returned.
|
||||
*/
|
||||
virtual String album() const;
|
||||
String album() const override;
|
||||
|
||||
/*!
|
||||
* Returns the track comment.
|
||||
*/
|
||||
virtual String comment() const;
|
||||
String comment() const override;
|
||||
|
||||
/*!
|
||||
* Returns the genre name; if no genre is present in the tag String::null
|
||||
* will be returned.
|
||||
*/
|
||||
virtual String genre() const;
|
||||
String genre() const override;
|
||||
|
||||
/*!
|
||||
* Returns the rating.
|
||||
@@ -90,69 +93,71 @@ namespace TagLib {
|
||||
/*!
|
||||
* Returns the year; if there is no year set, this will return 0.
|
||||
*/
|
||||
virtual uint year() const;
|
||||
unsigned int year() const override;
|
||||
|
||||
/*!
|
||||
* Returns the track number; if there is no track number set, this will
|
||||
* return 0.
|
||||
*/
|
||||
virtual uint track() const;
|
||||
unsigned int track() const override;
|
||||
|
||||
/*!
|
||||
* Sets the title to \a s.
|
||||
* Sets the title to \a value.
|
||||
*/
|
||||
virtual void setTitle(const String &s);
|
||||
void setTitle(const String &value) override;
|
||||
|
||||
/*!
|
||||
* Sets the artist to \a s.
|
||||
* Sets the artist to \a value.
|
||||
*/
|
||||
virtual void setArtist(const String &s);
|
||||
void setArtist(const String &value) override;
|
||||
|
||||
/*!
|
||||
* Sets the album to \a s. If \a s is String::null then this value will be
|
||||
* Sets the album to \a value. If \a value is String::null then this value will be
|
||||
* cleared.
|
||||
*/
|
||||
virtual void setAlbum(const String &s);
|
||||
void setAlbum(const String &value) override;
|
||||
|
||||
/*!
|
||||
* Sets the comment to \a s.
|
||||
* Sets the comment to \a value.
|
||||
*/
|
||||
virtual void setComment(const String &s);
|
||||
void setComment(const String &value) override;
|
||||
|
||||
/*!
|
||||
* Sets the rating to \a s.
|
||||
* Sets the rating to \a value.
|
||||
*/
|
||||
virtual void setRating(const String &s);
|
||||
virtual void setRating(const String &value);
|
||||
|
||||
/*!
|
||||
* Sets the copyright to \a s.
|
||||
* Sets the copyright to \a value.
|
||||
*/
|
||||
virtual void setCopyright(const String &s);
|
||||
virtual void setCopyright(const String &value);
|
||||
|
||||
/*!
|
||||
* Sets the genre to \a s.
|
||||
* Sets the genre to \a value.
|
||||
*/
|
||||
virtual void setGenre(const String &s);
|
||||
void setGenre(const String &value) override;
|
||||
|
||||
/*!
|
||||
* Sets the year to \a i. If \a s is 0 then this value will be cleared.
|
||||
* Sets the year to \a value. If \a value is 0 then this value will be cleared.
|
||||
*/
|
||||
virtual void setYear(uint i);
|
||||
void setYear(unsigned int value) override;
|
||||
|
||||
/*!
|
||||
* Sets the track to \a i. If \a s is 0 then this value will be cleared.
|
||||
* Sets the track to \a value. If \a value is 0 then this value will be cleared.
|
||||
*/
|
||||
virtual void setTrack(uint i);
|
||||
void setTrack(unsigned int value) override;
|
||||
|
||||
/*!
|
||||
* Returns true if the tag does not contain any data. This should be
|
||||
* reimplemented in subclasses that provide more than the basic tagging
|
||||
* abilities in this class.
|
||||
*/
|
||||
virtual bool isEmpty() const;
|
||||
bool isEmpty() const override;
|
||||
|
||||
/*!
|
||||
* \deprecated
|
||||
* \warning You should not modify this data structure directly, instead
|
||||
* use attributeListMap() const, contains(), removeItem(),
|
||||
* attribute(), setAttribute(), addAttribute().
|
||||
*/
|
||||
AttributeListMap &attributeListMap();
|
||||
|
||||
@@ -163,14 +168,14 @@ namespace TagLib {
|
||||
const AttributeListMap &attributeListMap() const;
|
||||
|
||||
/*!
|
||||
* \return True if a value for \a attribute is currently set.
|
||||
* \return True if a value for \a key is currently set.
|
||||
*/
|
||||
bool contains(const String &name) const;
|
||||
bool contains(const String &key) const;
|
||||
|
||||
/*!
|
||||
* Removes the \a key attribute from the tag
|
||||
*/
|
||||
void removeItem(const String &name);
|
||||
void removeItem(const String &key);
|
||||
|
||||
/*!
|
||||
* \return The list of values for the key \a name, or an empty list if no
|
||||
@@ -179,8 +184,8 @@ namespace TagLib {
|
||||
AttributeList attribute(const String &name) const;
|
||||
|
||||
/*!
|
||||
* Sets the \a key attribute to the value of \a attribute. If an attribute
|
||||
* with the \a key is already present, it will be replaced.
|
||||
* Sets the \a name attribute to the value of \a attribute. If an attribute
|
||||
* with the \a name is already present, it will be replaced.
|
||||
*/
|
||||
void setAttribute(const String &name, const Attribute &attribute);
|
||||
|
||||
@@ -190,20 +195,25 @@ namespace TagLib {
|
||||
void setAttribute(const String &name, const AttributeList &values);
|
||||
|
||||
/*!
|
||||
* Sets the \a key attribute to the value of \a attribute. If an attribute
|
||||
* with the \a key is already present, it will be added to the list.
|
||||
* Sets the \a name attribute to the value of \a attribute. If an attribute
|
||||
* with the \a name is already present, it will be added to the list.
|
||||
*/
|
||||
void addAttribute(const String &name, const Attribute &attribute);
|
||||
|
||||
PropertyMap properties() const;
|
||||
void removeUnsupportedProperties(const StringList& properties);
|
||||
PropertyMap setProperties(const PropertyMap &properties);
|
||||
PropertyMap properties() const override;
|
||||
void removeUnsupportedProperties(const StringList &props) override;
|
||||
PropertyMap setProperties(const PropertyMap &props) override;
|
||||
|
||||
StringList complexPropertyKeys() const override;
|
||||
List<VariantMap> complexProperties(const String &key) const override;
|
||||
bool setComplexProperties(const String &key, const List<VariantMap> &value) override;
|
||||
|
||||
private:
|
||||
|
||||
class TagPrivate;
|
||||
TagPrivate *d;
|
||||
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
|
||||
std::unique_ptr<TagPrivate> d;
|
||||
};
|
||||
}
|
||||
}
|
||||
} // namespace ASF
|
||||
} // namespace TagLib
|
||||
#endif
|
||||
|
||||
@@ -34,67 +34,70 @@ namespace TagLib
|
||||
{
|
||||
namespace ASF
|
||||
{
|
||||
|
||||
inline ushort readWORD(File *file, bool *ok = 0)
|
||||
namespace
|
||||
{
|
||||
const ByteVector v = file->readBlock(2);
|
||||
if(v.size() != 2) {
|
||||
if(ok) *ok = false;
|
||||
return 0;
|
||||
}
|
||||
if(ok) *ok = true;
|
||||
return v.toUShort(false);
|
||||
}
|
||||
|
||||
inline uint readDWORD(File *file, bool *ok = 0)
|
||||
{
|
||||
const ByteVector v = file->readBlock(4);
|
||||
if(v.size() != 4) {
|
||||
if(ok) *ok = false;
|
||||
return 0;
|
||||
}
|
||||
if(ok) *ok = true;
|
||||
return v.toUInt(false);
|
||||
}
|
||||
|
||||
inline long long readQWORD(File *file, bool *ok = 0)
|
||||
{
|
||||
const ByteVector v = file->readBlock(8);
|
||||
if(v.size() != 8) {
|
||||
if(ok) *ok = false;
|
||||
return 0;
|
||||
}
|
||||
if(ok) *ok = true;
|
||||
return v.toLongLong(false);
|
||||
}
|
||||
|
||||
inline String readString(File *file, int length)
|
||||
{
|
||||
ByteVector data = file->readBlock(length);
|
||||
unsigned int size = data.size();
|
||||
while (size >= 2) {
|
||||
if(data[size - 1] != '\0' || data[size - 2] != '\0') {
|
||||
break;
|
||||
inline unsigned short readWORD(File *file, bool *ok = nullptr)
|
||||
{
|
||||
const ByteVector v = file->readBlock(2);
|
||||
if(v.size() != 2) {
|
||||
if(ok) *ok = false;
|
||||
return 0;
|
||||
}
|
||||
size -= 2;
|
||||
if(ok) *ok = true;
|
||||
return v.toUShort(false);
|
||||
}
|
||||
if(size != data.size()) {
|
||||
data.resize(size);
|
||||
}
|
||||
return String(data, String::UTF16LE);
|
||||
}
|
||||
|
||||
inline ByteVector renderString(const String &str, bool includeLength = false)
|
||||
{
|
||||
ByteVector data = str.data(String::UTF16LE) + ByteVector::fromShort(0, false);
|
||||
if(includeLength) {
|
||||
data = ByteVector::fromShort(data.size(), false) + data;
|
||||
inline unsigned int readDWORD(File *file, bool *ok = nullptr)
|
||||
{
|
||||
const ByteVector v = file->readBlock(4);
|
||||
if(v.size() != 4) {
|
||||
if(ok) *ok = false;
|
||||
return 0;
|
||||
}
|
||||
if(ok) *ok = true;
|
||||
return v.toUInt(false);
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
inline long long readQWORD(File *file, bool *ok = nullptr)
|
||||
{
|
||||
const ByteVector v = file->readBlock(8);
|
||||
if(v.size() != 8) {
|
||||
if(ok) *ok = false;
|
||||
return 0;
|
||||
}
|
||||
if(ok) *ok = true;
|
||||
return v.toLongLong(false);
|
||||
}
|
||||
|
||||
inline String readString(File *file, int length)
|
||||
{
|
||||
ByteVector data = file->readBlock(length);
|
||||
unsigned int size = data.size();
|
||||
while (size >= 2) {
|
||||
if(data[size - 1] != '\0' || data[size - 2] != '\0') {
|
||||
break;
|
||||
}
|
||||
size -= 2;
|
||||
}
|
||||
if(size != data.size()) {
|
||||
data.resize(size);
|
||||
}
|
||||
return String(data, String::UTF16LE);
|
||||
}
|
||||
|
||||
inline ByteVector renderString(const String &str, bool includeLength = false)
|
||||
{
|
||||
ByteVector data = str.data(String::UTF16LE) + ByteVector::fromShort(0, false);
|
||||
if(includeLength) {
|
||||
data = ByteVector::fromShort(data.size(), false) + data;
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace ASF
|
||||
} // namespace TagLib
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
@@ -23,142 +23,49 @@
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#include <tbytevector.h>
|
||||
|
||||
#include "aiffproperties.h"
|
||||
#include "apeproperties.h"
|
||||
#include "asfproperties.h"
|
||||
#include "flacproperties.h"
|
||||
#include "mp4properties.h"
|
||||
#include "mpcproperties.h"
|
||||
#include "mpegproperties.h"
|
||||
#include "opusproperties.h"
|
||||
#include "speexproperties.h"
|
||||
#include "trueaudioproperties.h"
|
||||
#include "vorbisproperties.h"
|
||||
#include "wavproperties.h"
|
||||
#include "wavpackproperties.h"
|
||||
|
||||
#include "audioproperties.h"
|
||||
|
||||
using namespace TagLib;
|
||||
|
||||
class AudioProperties::AudioPropertiesPrivate
|
||||
{
|
||||
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// public methods
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
AudioProperties::~AudioProperties()
|
||||
{
|
||||
AudioProperties::~AudioProperties() = default;
|
||||
|
||||
int AudioProperties::length() const
|
||||
{
|
||||
return lengthInSeconds();
|
||||
}
|
||||
|
||||
int TagLib::AudioProperties::lengthInSeconds() const
|
||||
int AudioProperties::lengthInSeconds() const
|
||||
{
|
||||
// This is an ugly workaround but we can't add a virtual function.
|
||||
// Should be virtual in taglib2.
|
||||
|
||||
if(dynamic_cast<const APE::Properties*>(this))
|
||||
return dynamic_cast<const APE::Properties*>(this)->lengthInSeconds();
|
||||
|
||||
else if(dynamic_cast<const ASF::Properties*>(this))
|
||||
return dynamic_cast<const ASF::Properties*>(this)->lengthInSeconds();
|
||||
|
||||
else if(dynamic_cast<const FLAC::Properties*>(this))
|
||||
return dynamic_cast<const FLAC::Properties*>(this)->lengthInSeconds();
|
||||
|
||||
else if(dynamic_cast<const MP4::Properties*>(this))
|
||||
return dynamic_cast<const MP4::Properties*>(this)->lengthInSeconds();
|
||||
|
||||
else if(dynamic_cast<const MPC::Properties*>(this))
|
||||
return dynamic_cast<const MPC::Properties*>(this)->lengthInSeconds();
|
||||
|
||||
else if(dynamic_cast<const MPEG::Properties*>(this))
|
||||
return dynamic_cast<const MPEG::Properties*>(this)->lengthInSeconds();
|
||||
|
||||
else if(dynamic_cast<const Ogg::Opus::Properties*>(this))
|
||||
return dynamic_cast<const Ogg::Opus::Properties*>(this)->lengthInSeconds();
|
||||
|
||||
else if(dynamic_cast<const Ogg::Speex::Properties*>(this))
|
||||
return dynamic_cast<const Ogg::Speex::Properties*>(this)->lengthInSeconds();
|
||||
|
||||
else if(dynamic_cast<const TrueAudio::Properties*>(this))
|
||||
return dynamic_cast<const TrueAudio::Properties*>(this)->lengthInSeconds();
|
||||
|
||||
else if (dynamic_cast<const RIFF::AIFF::Properties*>(this))
|
||||
return dynamic_cast<const RIFF::AIFF::Properties*>(this)->lengthInSeconds();
|
||||
|
||||
else if(dynamic_cast<const RIFF::WAV::Properties*>(this))
|
||||
return dynamic_cast<const RIFF::WAV::Properties*>(this)->lengthInSeconds();
|
||||
|
||||
else if(dynamic_cast<const Vorbis::Properties*>(this))
|
||||
return dynamic_cast<const Vorbis::Properties*>(this)->lengthInSeconds();
|
||||
|
||||
else if(dynamic_cast<const WavPack::Properties*>(this))
|
||||
return dynamic_cast<const WavPack::Properties*>(this)->lengthInSeconds();
|
||||
|
||||
else
|
||||
return 0;
|
||||
return lengthInMilliseconds() / 1000;
|
||||
}
|
||||
|
||||
int TagLib::AudioProperties::lengthInMilliseconds() const
|
||||
int AudioProperties::lengthInMilliseconds() const
|
||||
{
|
||||
// This is an ugly workaround but we can't add a virtual function.
|
||||
// Should be virtual in taglib2.
|
||||
return 0;
|
||||
}
|
||||
|
||||
if(dynamic_cast<const APE::Properties*>(this))
|
||||
return dynamic_cast<const APE::Properties*>(this)->lengthInMilliseconds();
|
||||
int AudioProperties::bitrate() const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
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;
|
||||
int AudioProperties::sampleRate() const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// protected methods
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
AudioProperties::AudioProperties(ReadStyle) :
|
||||
d(0)
|
||||
AudioProperties::AudioProperties(ReadStyle)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
@@ -26,6 +26,9 @@
|
||||
#ifndef TAGLIB_AUDIOPROPERTIES_H
|
||||
#define TAGLIB_AUDIOPROPERTIES_H
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "taglib.h"
|
||||
#include "taglib_export.h"
|
||||
|
||||
namespace TagLib {
|
||||
@@ -64,10 +67,19 @@ namespace TagLib {
|
||||
*/
|
||||
virtual ~AudioProperties();
|
||||
|
||||
AudioProperties(const AudioProperties &) = delete;
|
||||
AudioProperties &operator=(const AudioProperties &) = delete;
|
||||
|
||||
/*!
|
||||
* Returns the length of the file in seconds.
|
||||
*/
|
||||
virtual int length() const = 0;
|
||||
* Returns the length of the file in seconds. The length is rounded down to
|
||||
* the nearest whole second.
|
||||
*
|
||||
* \note This method is just an alias of lengthInSeconds().
|
||||
*
|
||||
* \deprecated Use lengthInSeconds().
|
||||
*/
|
||||
TAGLIB_DEPRECATED
|
||||
virtual int length() const;
|
||||
|
||||
/*!
|
||||
* Returns the length of the file in seconds. The length is rounded down to
|
||||
@@ -75,28 +87,26 @@ namespace TagLib {
|
||||
*
|
||||
* \see lengthInMilliseconds()
|
||||
*/
|
||||
// BIC: make virtual
|
||||
int lengthInSeconds() const;
|
||||
virtual int lengthInSeconds() const;
|
||||
|
||||
/*!
|
||||
* Returns the length of the file in milliseconds.
|
||||
*
|
||||
* \see lengthInSeconds()
|
||||
*/
|
||||
// BIC: make virtual
|
||||
int lengthInMilliseconds() const;
|
||||
virtual int lengthInMilliseconds() const;
|
||||
|
||||
/*!
|
||||
* Returns the most appropriate bit rate for the file in kb/s. For constant
|
||||
* bitrate formats this is simply the bitrate of the file. For variable
|
||||
* bitrate formats this is either the average or nominal bitrate.
|
||||
*/
|
||||
virtual int bitrate() const = 0;
|
||||
virtual int bitrate() const;
|
||||
|
||||
/*!
|
||||
* Returns the sample rate in Hz.
|
||||
*/
|
||||
virtual int sampleRate() const = 0;
|
||||
virtual int sampleRate() const;
|
||||
|
||||
/*!
|
||||
* Returns the number of audio channels.
|
||||
@@ -115,13 +125,11 @@ namespace TagLib {
|
||||
AudioProperties(ReadStyle style);
|
||||
|
||||
private:
|
||||
AudioProperties(const AudioProperties &);
|
||||
AudioProperties &operator=(const AudioProperties &);
|
||||
|
||||
class AudioPropertiesPrivate;
|
||||
AudioPropertiesPrivate *d;
|
||||
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
|
||||
std::unique_ptr<AudioPropertiesPrivate> d;
|
||||
};
|
||||
|
||||
}
|
||||
} // namespace TagLib
|
||||
|
||||
#endif
|
||||
|
||||
156
taglib/dsdiff/dsdiffdiintag.cpp
Normal file
156
taglib/dsdiff/dsdiffdiintag.cpp
Normal file
@@ -0,0 +1,156 @@
|
||||
/***************************************************************************
|
||||
copyright : (C) 2016 by Damien Plisson, Audirvana
|
||||
email : damien78@audirvana.com
|
||||
***************************************************************************/
|
||||
|
||||
/***************************************************************************
|
||||
* This library is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU Lesser General Public License version *
|
||||
* 2.1 as published by the Free Software Foundation. *
|
||||
* *
|
||||
* This library is distributed in the hope that it will be useful, but *
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
||||
* Lesser General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Lesser General Public *
|
||||
* License along with this library; if not, write to the Free Software *
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
|
||||
* 02110-1301 USA *
|
||||
* *
|
||||
* Alternatively, this file is available under the Mozilla Public *
|
||||
* License Version 1.1. You may obtain a copy of the License at *
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#include "dsdiffdiintag.h"
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include "tstringlist.h"
|
||||
#include "tpropertymap.h"
|
||||
#include "tdebug.h"
|
||||
|
||||
using namespace TagLib;
|
||||
using namespace DSDIFF::DIIN;
|
||||
|
||||
class DSDIFF::DIIN::Tag::TagPrivate
|
||||
{
|
||||
public:
|
||||
String title;
|
||||
String artist;
|
||||
};
|
||||
|
||||
DSDIFF::DIIN::Tag::Tag() :
|
||||
d(std::make_unique<TagPrivate>())
|
||||
{
|
||||
}
|
||||
|
||||
DSDIFF::DIIN::Tag::~Tag() = default;
|
||||
|
||||
String DSDIFF::DIIN::Tag::title() const
|
||||
{
|
||||
return d->title;
|
||||
}
|
||||
|
||||
String DSDIFF::DIIN::Tag::artist() const
|
||||
{
|
||||
return d->artist;
|
||||
}
|
||||
|
||||
String DSDIFF::DIIN::Tag::album() const
|
||||
{
|
||||
return String();
|
||||
}
|
||||
|
||||
String DSDIFF::DIIN::Tag::comment() const
|
||||
{
|
||||
return String();
|
||||
}
|
||||
|
||||
String DSDIFF::DIIN::Tag::genre() const
|
||||
{
|
||||
return String();
|
||||
}
|
||||
|
||||
unsigned int DSDIFF::DIIN::Tag::year() const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned int DSDIFF::DIIN::Tag::track() const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void DSDIFF::DIIN::Tag::setTitle(const String &title)
|
||||
{
|
||||
d->title = title;
|
||||
}
|
||||
|
||||
void DSDIFF::DIIN::Tag::setArtist(const String &artist)
|
||||
{
|
||||
d->artist = artist;
|
||||
}
|
||||
|
||||
void DSDIFF::DIIN::Tag::setAlbum(const String &)
|
||||
{
|
||||
debug("DSDIFF::DIIN::Tag::setAlbum() -- Ignoring unsupported tag.");
|
||||
}
|
||||
|
||||
void DSDIFF::DIIN::Tag::setComment(const String &)
|
||||
{
|
||||
debug("DSDIFF::DIIN::Tag::setComment() -- Ignoring unsupported tag.");
|
||||
}
|
||||
|
||||
void DSDIFF::DIIN::Tag::setGenre(const String &)
|
||||
{
|
||||
debug("DSDIFF::DIIN::Tag::setGenre() -- Ignoring unsupported tag.");
|
||||
}
|
||||
|
||||
void DSDIFF::DIIN::Tag::setYear(unsigned int)
|
||||
{
|
||||
debug("DSDIFF::DIIN::Tag::setYear() -- Ignoring unsupported tag.");
|
||||
}
|
||||
|
||||
void DSDIFF::DIIN::Tag::setTrack(unsigned int)
|
||||
{
|
||||
debug("DSDIFF::DIIN::Tag::setTrack() -- Ignoring unsupported tag.");
|
||||
}
|
||||
|
||||
PropertyMap DSDIFF::DIIN::Tag::properties() const
|
||||
{
|
||||
PropertyMap properties;
|
||||
properties["TITLE"] = d->title;
|
||||
properties["ARTIST"] = d->artist;
|
||||
return properties;
|
||||
}
|
||||
|
||||
PropertyMap DSDIFF::DIIN::Tag::setProperties(const PropertyMap &origProps)
|
||||
{
|
||||
PropertyMap props(origProps);
|
||||
props.removeEmpty();
|
||||
StringList oneValueSet;
|
||||
|
||||
if(props.contains("TITLE")) {
|
||||
d->title = props["TITLE"].front();
|
||||
oneValueSet.append("TITLE");
|
||||
} else
|
||||
d->title.clear();
|
||||
|
||||
if(props.contains("ARTIST")) {
|
||||
d->artist = props["ARTIST"].front();
|
||||
oneValueSet.append("ARTIST");
|
||||
} else
|
||||
d->artist.clear();
|
||||
|
||||
// for each tag that has been set above, remove the first entry in the corresponding
|
||||
// value list. The others will be returned as unsupported by this format.
|
||||
for(const auto &entry : std::as_const(oneValueSet)) {
|
||||
if(props[entry].size() == 1)
|
||||
props.erase(entry);
|
||||
else
|
||||
props[entry].erase(props[entry].begin());
|
||||
}
|
||||
return props;
|
||||
}
|
||||
147
taglib/dsdiff/dsdiffdiintag.h
Normal file
147
taglib/dsdiff/dsdiffdiintag.h
Normal file
@@ -0,0 +1,147 @@
|
||||
/***************************************************************************
|
||||
copyright : (C) 2016 by Damien Plisson, Audirvana
|
||||
email : damien78@audirvana.com
|
||||
***************************************************************************/
|
||||
|
||||
/***************************************************************************
|
||||
* This library is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU Lesser General Public License version *
|
||||
* 2.1 as published by the Free Software Foundation. *
|
||||
* *
|
||||
* This library is distributed in the hope that it will be useful, but *
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
||||
* Lesser General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Lesser General Public *
|
||||
* License along with this library; if not, write to the Free Software *
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
|
||||
* 02110-1301 USA *
|
||||
* *
|
||||
* Alternatively, this file is available under the Mozilla Public *
|
||||
* License Version 1.1. You may obtain a copy of the License at *
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef TAGLIB_DSDIFFDIINTAG_H
|
||||
#define TAGLIB_DSDIFFDIINTAG_H
|
||||
|
||||
#include "tag.h"
|
||||
|
||||
namespace TagLib {
|
||||
|
||||
namespace DSDIFF {
|
||||
|
||||
namespace DIIN {
|
||||
|
||||
/*!
|
||||
* Tags from the Edited Master Chunk Info
|
||||
*
|
||||
* Only Title and Artist tags are supported
|
||||
*/
|
||||
class TAGLIB_EXPORT Tag : public TagLib::Tag
|
||||
{
|
||||
public:
|
||||
Tag();
|
||||
~Tag() override;
|
||||
|
||||
/*!
|
||||
* Returns the track name; if no track name is present in the tag
|
||||
* String() will be returned.
|
||||
*/
|
||||
String title() const override;
|
||||
|
||||
/*!
|
||||
* Returns the artist name; if no artist name is present in the tag
|
||||
* String() will be returned.
|
||||
*/
|
||||
String artist() const override;
|
||||
|
||||
/*!
|
||||
* Not supported. Therefore always returns String().
|
||||
*/
|
||||
String album() const override;
|
||||
|
||||
/*!
|
||||
* Not supported. Therefore always returns String().
|
||||
*/
|
||||
String comment() const override;
|
||||
|
||||
/*!
|
||||
* Not supported. Therefore always returns String().
|
||||
*/
|
||||
String genre() const override;
|
||||
|
||||
/*!
|
||||
* Not supported. Therefore always returns 0.
|
||||
*/
|
||||
unsigned int year() const override;
|
||||
|
||||
/*!
|
||||
* Not supported. Therefore always returns 0.
|
||||
*/
|
||||
unsigned int track() const override;
|
||||
|
||||
/*!
|
||||
* Sets the title to \a title. If \a title is String() then this
|
||||
* value will be cleared.
|
||||
*/
|
||||
void setTitle(const String &title) override;
|
||||
|
||||
/*!
|
||||
* Sets the artist to \a artist. If \a artist is String() then this
|
||||
* value will be cleared.
|
||||
*/
|
||||
void setArtist(const String &artist) override;
|
||||
|
||||
/*!
|
||||
* Not supported and therefore ignored.
|
||||
*/
|
||||
void setAlbum(const String &album) override;
|
||||
|
||||
/*!
|
||||
* Not supported and therefore ignored.
|
||||
*/
|
||||
void setComment(const String &comment) override;
|
||||
|
||||
/*!
|
||||
* Not supported and therefore ignored.
|
||||
*/
|
||||
void setGenre(const String &genre) override;
|
||||
|
||||
/*!
|
||||
* Not supported and therefore ignored.
|
||||
*/
|
||||
void setYear(unsigned int year) override;
|
||||
|
||||
/*!
|
||||
* Not supported and therefore ignored.
|
||||
*/
|
||||
void setTrack(unsigned int track) override;
|
||||
|
||||
/*!
|
||||
* Implements the unified property interface -- export function.
|
||||
* Since the DIIN tag is very limited, the exported map is as well.
|
||||
*/
|
||||
PropertyMap properties() const override;
|
||||
|
||||
/*!
|
||||
* Implements the unified property interface -- import function.
|
||||
* Because of the limitations of the DIIN file tag, any tags besides
|
||||
* TITLE and ARTIST, will be
|
||||
* returned. Additionally, if the map contains tags with multiple values,
|
||||
* all but the first will be contained in the returned map of unsupported
|
||||
* properties.
|
||||
*/
|
||||
PropertyMap setProperties(const PropertyMap &) override;
|
||||
|
||||
private:
|
||||
class TagPrivate;
|
||||
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
|
||||
std::unique_ptr<TagPrivate> d;
|
||||
};
|
||||
} // namespace DIIN
|
||||
} // namespace DSDIFF
|
||||
} // namespace TagLib
|
||||
|
||||
#endif
|
||||
934
taglib/dsdiff/dsdifffile.cpp
Normal file
934
taglib/dsdiff/dsdifffile.cpp
Normal file
@@ -0,0 +1,934 @@
|
||||
/***************************************************************************
|
||||
copyright : (C) 2016 by Damien Plisson, Audirvana
|
||||
email : damien78@audirvana.com
|
||||
***************************************************************************/
|
||||
|
||||
/***************************************************************************
|
||||
* This library is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU Lesser General Public License version *
|
||||
* 2.1 as published by the Free Software Foundation. *
|
||||
* *
|
||||
* This library is distributed in the hope that it will be useful, but *
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
||||
* Lesser General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Lesser General Public *
|
||||
* License along with this library; if not, write to the Free Software *
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
|
||||
* 02110-1301 USA *
|
||||
* *
|
||||
* Alternatively, this file is available under the Mozilla Public *
|
||||
* License Version 1.1. You may obtain a copy of the License at *
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#include "dsdifffile.h"
|
||||
|
||||
#include <array>
|
||||
#include <memory>
|
||||
|
||||
#include "tbytevector.h"
|
||||
#include "tstringlist.h"
|
||||
#include "tpropertymap.h"
|
||||
#include "tdebug.h"
|
||||
#include "id3v2tag.h"
|
||||
#include "tagutils.h"
|
||||
#include "tagunion.h"
|
||||
|
||||
using namespace TagLib;
|
||||
|
||||
namespace
|
||||
{
|
||||
struct Chunk64
|
||||
{
|
||||
ByteVector name;
|
||||
unsigned long long offset;
|
||||
unsigned long long size;
|
||||
char padding;
|
||||
};
|
||||
|
||||
typedef std::vector<Chunk64> ChunkList;
|
||||
|
||||
int chunkIndex(const ChunkList &chunks, const ByteVector &id)
|
||||
{
|
||||
for(size_t i = 0; i < chunks.size(); i++) {
|
||||
if(chunks[i].name == id)
|
||||
return static_cast<int>(i);
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool isValidChunkID(const ByteVector &name)
|
||||
{
|
||||
if(name.size() != 4)
|
||||
return false;
|
||||
|
||||
for(int i = 0; i < 4; i++) {
|
||||
if(name[i] < 32 || name[i] > 126)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
enum {
|
||||
ID3v2Index = 0,
|
||||
DIINIndex = 1
|
||||
};
|
||||
enum {
|
||||
PROPChunk = 0,
|
||||
DIINChunk = 1
|
||||
};
|
||||
} // namespace
|
||||
|
||||
class DSDIFF::File::FilePrivate
|
||||
{
|
||||
public:
|
||||
FilePrivate(ID3v2::FrameFactory *frameFactory)
|
||||
: ID3v2FrameFactory(frameFactory ? frameFactory
|
||||
: ID3v2::FrameFactory::instance())
|
||||
{
|
||||
}
|
||||
|
||||
~FilePrivate() = default;
|
||||
|
||||
const ID3v2::FrameFactory *ID3v2FrameFactory;
|
||||
Endianness endianness { BigEndian };
|
||||
ByteVector type;
|
||||
unsigned long long size { 0 };
|
||||
ByteVector format;
|
||||
ChunkList chunks;
|
||||
std::array<ChunkList, 2> childChunks;
|
||||
std::array<int, 2> childChunkIndex { -1, -1 };
|
||||
/*
|
||||
* Two possibilities can be found: ID3V2 chunk inside PROP chunk or at root level
|
||||
*/
|
||||
bool isID3InPropChunk { false };
|
||||
/*
|
||||
* ID3 chunks are present. This is then the index of the one in PROP chunk that
|
||||
* will be removed upon next save to remove duplicates.
|
||||
*/
|
||||
int duplicateID3V2chunkIndex { -1 };
|
||||
|
||||
std::unique_ptr<Properties> properties;
|
||||
|
||||
TagUnion tag;
|
||||
|
||||
ByteVector id3v2TagChunkID { "ID3 " };
|
||||
|
||||
bool hasID3v2 { false };
|
||||
bool hasDiin { false };
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// static members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
bool DSDIFF::File::isSupported(IOStream *stream)
|
||||
{
|
||||
// A DSDIFF file has to start with "FRM8????????DSD ".
|
||||
const ByteVector id = Utils::readHeader(stream, 16, false);
|
||||
return (id.startsWith("FRM8") && id.containsAt("DSD ", 12));
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// public members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
DSDIFF::File::File(FileName file, bool readProperties,
|
||||
Properties::ReadStyle propertiesStyle,
|
||||
ID3v2::FrameFactory *frameFactory) :
|
||||
TagLib::File(file),
|
||||
d(std::make_unique<FilePrivate>(frameFactory))
|
||||
{
|
||||
if(isOpen())
|
||||
read(readProperties, propertiesStyle);
|
||||
}
|
||||
|
||||
DSDIFF::File::File(IOStream *stream, bool readProperties,
|
||||
Properties::ReadStyle propertiesStyle,
|
||||
ID3v2::FrameFactory *frameFactory) :
|
||||
TagLib::File(stream),
|
||||
d(std::make_unique<FilePrivate>(frameFactory))
|
||||
{
|
||||
if(isOpen())
|
||||
read(readProperties, propertiesStyle);
|
||||
}
|
||||
|
||||
DSDIFF::File::~File() = default;
|
||||
|
||||
TagLib::Tag *DSDIFF::File::tag() const
|
||||
{
|
||||
return &d->tag;
|
||||
}
|
||||
|
||||
ID3v2::Tag *DSDIFF::File::ID3v2Tag(bool create) const
|
||||
{
|
||||
return d->tag.access<ID3v2::Tag>(ID3v2Index, create, d->ID3v2FrameFactory);
|
||||
}
|
||||
|
||||
bool DSDIFF::File::hasID3v2Tag() const
|
||||
{
|
||||
return d->hasID3v2;
|
||||
}
|
||||
|
||||
DSDIFF::DIIN::Tag *DSDIFF::File::DIINTag(bool create) const
|
||||
{
|
||||
return d->tag.access<DSDIFF::DIIN::Tag>(DIINIndex, create);
|
||||
}
|
||||
|
||||
bool DSDIFF::File::hasDIINTag() const
|
||||
{
|
||||
return d->hasDiin;
|
||||
}
|
||||
|
||||
PropertyMap DSDIFF::File::properties() const
|
||||
{
|
||||
return d->tag.properties();
|
||||
}
|
||||
|
||||
void DSDIFF::File::removeUnsupportedProperties(const StringList &unsupported)
|
||||
{
|
||||
d->tag.removeUnsupportedProperties(unsupported);
|
||||
}
|
||||
|
||||
PropertyMap DSDIFF::File::setProperties(const PropertyMap &properties)
|
||||
{
|
||||
return ID3v2Tag(true)->setProperties(properties);
|
||||
}
|
||||
|
||||
DSDIFF::Properties *DSDIFF::File::audioProperties() const
|
||||
{
|
||||
return d->properties.get();
|
||||
}
|
||||
|
||||
bool DSDIFF::File::save()
|
||||
{
|
||||
return save(AllTags);
|
||||
}
|
||||
|
||||
bool DSDIFF::File::save(int tags, StripTags strip, ID3v2::Version version)
|
||||
{
|
||||
if(readOnly()) {
|
||||
debug("DSDIFF::File::save() -- File is read only.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!isValid()) {
|
||||
debug("DSDIFF::File::save() -- Trying to save invalid file.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if(strip == StripOthers)
|
||||
File::strip(~tags);
|
||||
|
||||
// First: save ID3V2 chunk
|
||||
|
||||
ID3v2::Tag *id3v2Tag = ID3v2Tag();
|
||||
|
||||
if((tags & ID3v2) && id3v2Tag) {
|
||||
if(d->isID3InPropChunk) {
|
||||
if(!id3v2Tag->isEmpty()) {
|
||||
setChildChunkData(d->id3v2TagChunkID, id3v2Tag->render(version), PROPChunk);
|
||||
d->hasID3v2 = true;
|
||||
}
|
||||
else {
|
||||
// Empty tag: remove it
|
||||
setChildChunkData(d->id3v2TagChunkID, ByteVector(), PROPChunk);
|
||||
d->hasID3v2 = false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if(!id3v2Tag->isEmpty()) {
|
||||
setRootChunkData(d->id3v2TagChunkID, id3v2Tag->render(version));
|
||||
d->hasID3v2 = true;
|
||||
}
|
||||
else {
|
||||
// Empty tag: remove it
|
||||
setRootChunkData(d->id3v2TagChunkID, ByteVector());
|
||||
d->hasID3v2 = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Second: save the DIIN chunk
|
||||
|
||||
DSDIFF::DIIN::Tag *diinTag = DIINTag();
|
||||
|
||||
if((tags & DIIN) && diinTag) {
|
||||
if(!diinTag->title().isEmpty()) {
|
||||
ByteVector diinTitle;
|
||||
diinTitle.append(ByteVector::fromUInt(diinTag->title().size(), d->endianness == BigEndian));
|
||||
diinTitle.append(ByteVector::fromCString(diinTag->title().toCString()));
|
||||
setChildChunkData("DITI", diinTitle, DIINChunk);
|
||||
}
|
||||
else
|
||||
setChildChunkData("DITI", ByteVector(), DIINChunk);
|
||||
|
||||
if(!diinTag->artist().isEmpty()) {
|
||||
ByteVector diinArtist;
|
||||
diinArtist.append(ByteVector::fromUInt(diinTag->artist().size(), d->endianness == BigEndian));
|
||||
diinArtist.append(ByteVector::fromCString(diinTag->artist().toCString()));
|
||||
setChildChunkData("DIAR", diinArtist, DIINChunk);
|
||||
}
|
||||
else
|
||||
setChildChunkData("DIAR", ByteVector(), DIINChunk);
|
||||
}
|
||||
|
||||
// Third: remove the duplicate ID3V2 chunk (inside PROP chunk) if any
|
||||
if(d->duplicateID3V2chunkIndex >= 0) {
|
||||
setChildChunkData(d->duplicateID3V2chunkIndex, ByteVector(), PROPChunk);
|
||||
d->duplicateID3V2chunkIndex = -1;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void DSDIFF::File::strip(int tags)
|
||||
{
|
||||
if(tags & ID3v2) {
|
||||
removeRootChunk("ID3 ");
|
||||
removeRootChunk("id3 ");
|
||||
removeChildChunk("ID3 ", PROPChunk);
|
||||
removeChildChunk("id3 ", PROPChunk);
|
||||
d->hasID3v2 = false;
|
||||
d->tag.set(ID3v2Index, new ID3v2::Tag(nullptr, 0, d->ID3v2FrameFactory));
|
||||
d->duplicateID3V2chunkIndex = -1;
|
||||
d->isID3InPropChunk = false;
|
||||
d->id3v2TagChunkID.setData("ID3 ");
|
||||
}
|
||||
if(tags & DIIN) {
|
||||
removeChildChunk("DITI", DIINChunk);
|
||||
removeChildChunk("DIAR", DIINChunk);
|
||||
if(d->childChunks[DIINIndex].empty()) {
|
||||
removeRootChunk("DIIN");
|
||||
}
|
||||
|
||||
d->hasDiin = false;
|
||||
d->tag.set(DIINIndex, new DIIN::Tag);
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// private members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void DSDIFF::File::removeRootChunk(unsigned int i)
|
||||
{
|
||||
unsigned long long chunkSize = d->chunks[i].size + d->chunks[i].padding + 12;
|
||||
|
||||
d->size -= chunkSize;
|
||||
insert(ByteVector::fromLongLong(d->size, d->endianness == BigEndian), 4, 8);
|
||||
|
||||
removeBlock(d->chunks[i].offset - 12, chunkSize);
|
||||
|
||||
// Update the internal offsets
|
||||
|
||||
d->chunks.erase(d->chunks.begin() + i);
|
||||
for(int &cci : d->childChunkIndex) {
|
||||
if(cci > static_cast<int>(i)) {
|
||||
--cci;
|
||||
}
|
||||
}
|
||||
updateRootChunksStructure(i);
|
||||
}
|
||||
|
||||
void DSDIFF::File::removeRootChunk(const ByteVector &id)
|
||||
{
|
||||
int i = chunkIndex(d->chunks, id);
|
||||
|
||||
if(i >= 0)
|
||||
removeRootChunk(i);
|
||||
}
|
||||
|
||||
void DSDIFF::File::setRootChunkData(unsigned int i, const ByteVector &data)
|
||||
{
|
||||
if(data.isEmpty()) {
|
||||
removeRootChunk(i);
|
||||
return;
|
||||
}
|
||||
|
||||
// Non null data: update chunk
|
||||
// First we update the global size
|
||||
|
||||
d->size += ((data.size() + 1) & ~1) - (d->chunks[i].size + d->chunks[i].padding);
|
||||
insert(ByteVector::fromLongLong(d->size, d->endianness == BigEndian), 4, 8);
|
||||
|
||||
// Now update the specific chunk
|
||||
|
||||
writeChunk(d->chunks[i].name,
|
||||
data,
|
||||
d->chunks[i].offset - 12,
|
||||
static_cast<unsigned long>(d->chunks[i].size + d->chunks[i].padding + 12));
|
||||
|
||||
d->chunks[i].size = data.size();
|
||||
d->chunks[i].padding = (data.size() & 0x01) ? 1 : 0;
|
||||
|
||||
// Finally update the internal offsets
|
||||
|
||||
updateRootChunksStructure(i + 1);
|
||||
}
|
||||
|
||||
void DSDIFF::File::setRootChunkData(const ByteVector &name, const ByteVector &data)
|
||||
{
|
||||
if(d->chunks.empty()) {
|
||||
debug("DSDIFF::File::setRootChunkData('" + name + "') - No valid chunks found.");
|
||||
return;
|
||||
}
|
||||
|
||||
int i = chunkIndex(d->chunks, name);
|
||||
|
||||
if(i >= 0) {
|
||||
setRootChunkData(i, data);
|
||||
return;
|
||||
}
|
||||
|
||||
// Couldn't find an existing chunk, so let's create a new one.
|
||||
i = static_cast<int>(d->chunks.size()) - 1;
|
||||
unsigned long long offset = d->chunks[i].offset + d->chunks[i].size + d->chunks[i].padding;
|
||||
|
||||
// First we update the global size
|
||||
d->size += (offset & 1) + ((data.size() + 1) & ~1) + 12;
|
||||
insert(ByteVector::fromLongLong(d->size, d->endianness == BigEndian), 4, 8);
|
||||
|
||||
// Now add the chunk to the file
|
||||
const unsigned long long fileLength = length();
|
||||
writeChunk(name,
|
||||
data,
|
||||
offset,
|
||||
static_cast<unsigned long>(fileLength > offset ? fileLength - offset : 0),
|
||||
(offset & 1) ? 1 : 0);
|
||||
|
||||
Chunk64 chunk;
|
||||
chunk.name = name;
|
||||
chunk.size = data.size();
|
||||
chunk.offset = offset + 12;
|
||||
chunk.padding = (data.size() & 0x01) ? 1 : 0;
|
||||
|
||||
d->chunks.push_back(chunk);
|
||||
}
|
||||
|
||||
void DSDIFF::File::removeChildChunk(unsigned int i, unsigned int childChunkNum)
|
||||
{
|
||||
ChunkList &childChunks = d->childChunks[childChunkNum];
|
||||
|
||||
// Update global size
|
||||
|
||||
unsigned long long removedChunkTotalSize = childChunks[i].size + childChunks[i].padding + 12;
|
||||
d->size -= removedChunkTotalSize;
|
||||
insert(ByteVector::fromLongLong(d->size, d->endianness == BigEndian), 4, 8);
|
||||
|
||||
// Update child chunk size
|
||||
|
||||
d->chunks[d->childChunkIndex[childChunkNum]].size -= removedChunkTotalSize;
|
||||
insert(ByteVector::fromLongLong(d->chunks[d->childChunkIndex[childChunkNum]].size,
|
||||
d->endianness == BigEndian),
|
||||
d->chunks[d->childChunkIndex[childChunkNum]].offset - 8, 8);
|
||||
// Remove the chunk
|
||||
|
||||
removeBlock(childChunks[i].offset - 12, removedChunkTotalSize);
|
||||
|
||||
// Update the internal offsets
|
||||
// For child chunks
|
||||
|
||||
if((i + 1) < childChunks.size()) {
|
||||
childChunks[i + 1].offset = childChunks[i].offset;
|
||||
for(unsigned int c = i + 2; c < childChunks.size(); ++c)
|
||||
childChunks[c].offset = childChunks[c - 1].offset + 12
|
||||
+ childChunks[c - 1].size + childChunks[c - 1].padding;
|
||||
}
|
||||
|
||||
// And for root chunks
|
||||
|
||||
childChunks.erase(childChunks.begin() + i);
|
||||
updateRootChunksStructure(d->childChunkIndex[childChunkNum] + 1);
|
||||
}
|
||||
|
||||
void DSDIFF::File::removeChildChunk(const ByteVector &id, unsigned int childChunkNum)
|
||||
{
|
||||
int i = chunkIndex(d->childChunks[childChunkNum], id);
|
||||
|
||||
if(i >= 0)
|
||||
removeChildChunk(i, childChunkNum);
|
||||
}
|
||||
|
||||
void DSDIFF::File::setChildChunkData(unsigned int i,
|
||||
const ByteVector &data,
|
||||
unsigned int childChunkNum)
|
||||
{
|
||||
ChunkList &childChunks = d->childChunks[childChunkNum];
|
||||
|
||||
if(data.isEmpty()) {
|
||||
removeChildChunk(i, childChunkNum);
|
||||
return;
|
||||
}
|
||||
|
||||
// Non null data: update chunk
|
||||
// First we update the global size
|
||||
|
||||
d->size += ((data.size() + 1) & ~1) - (childChunks[i].size + childChunks[i].padding);
|
||||
|
||||
insert(ByteVector::fromLongLong(d->size, d->endianness == BigEndian), 4, 8);
|
||||
|
||||
// And the PROP chunk size
|
||||
|
||||
d->chunks[d->childChunkIndex[childChunkNum]].size +=
|
||||
((data.size() + 1) & ~1) - (childChunks[i].size + childChunks[i].padding);
|
||||
insert(ByteVector::fromLongLong(d->chunks[d->childChunkIndex[childChunkNum]].size,
|
||||
d->endianness == BigEndian),
|
||||
d->chunks[d->childChunkIndex[childChunkNum]].offset - 8, 8);
|
||||
|
||||
// Now update the specific chunk
|
||||
|
||||
writeChunk(childChunks[i].name,
|
||||
data,
|
||||
childChunks[i].offset - 12,
|
||||
static_cast<unsigned long>(childChunks[i].size + childChunks[i].padding + 12));
|
||||
|
||||
childChunks[i].size = data.size();
|
||||
childChunks[i].padding = (data.size() & 0x01) ? 1 : 0;
|
||||
|
||||
// Now update the internal offsets
|
||||
// For child Chunks
|
||||
for(i++; i < childChunks.size(); i++)
|
||||
childChunks[i].offset = childChunks[i - 1].offset + 12
|
||||
+ childChunks[i - 1].size + childChunks[i - 1].padding;
|
||||
|
||||
// And for root chunks
|
||||
updateRootChunksStructure(d->childChunkIndex[childChunkNum] + 1);
|
||||
}
|
||||
|
||||
void DSDIFF::File::setChildChunkData(const ByteVector &name,
|
||||
const ByteVector &data,
|
||||
unsigned int childChunkNum)
|
||||
{
|
||||
ChunkList &childChunks = d->childChunks[childChunkNum];
|
||||
|
||||
if(int i = chunkIndex(childChunks, name); i >= 0) {
|
||||
setChildChunkData(i, data, childChunkNum);
|
||||
return;
|
||||
}
|
||||
|
||||
// Do not attempt to remove a non existing chunk
|
||||
|
||||
if(data.isEmpty())
|
||||
return;
|
||||
|
||||
// Couldn't find an existing chunk, so let's create a new one.
|
||||
|
||||
unsigned long long offset = 0;
|
||||
if(!childChunks.empty()) {
|
||||
size_t i = childChunks.size() - 1;
|
||||
offset = childChunks[i].offset + childChunks[i].size + childChunks[i].padding;
|
||||
}
|
||||
else if(childChunkNum == DIINChunk) {
|
||||
int i = d->childChunkIndex[DIINChunk];
|
||||
if(i < 0) {
|
||||
setRootChunkData("DIIN", ByteVector());
|
||||
const int lastChunkIndex = static_cast<int>(d->chunks.size()) - 1;
|
||||
if(lastChunkIndex >= 0 && d->chunks[lastChunkIndex].name == "DIIN") {
|
||||
i = lastChunkIndex;
|
||||
d->childChunkIndex[DIINChunk] = lastChunkIndex;
|
||||
d->hasDiin = true;
|
||||
}
|
||||
}
|
||||
if(i >= 0) {
|
||||
offset = d->chunks[i].offset; // 12 is already added in setRootChunkData()
|
||||
}
|
||||
}
|
||||
if(offset == 0) {
|
||||
debug("DSDIFF::File::setChildChunkData - No valid chunks found.");
|
||||
return;
|
||||
}
|
||||
|
||||
// First we update the global size
|
||||
|
||||
d->size += (offset & 1) + ((data.size() + 1) & ~1) + 12;
|
||||
insert(ByteVector::fromLongLong(d->size, d->endianness == BigEndian), 4, 8);
|
||||
|
||||
// And the child chunk size
|
||||
|
||||
d->chunks[d->childChunkIndex[childChunkNum]].size += (offset & 1)
|
||||
+ ((data.size() + 1) & ~1) + 12;
|
||||
insert(ByteVector::fromLongLong(d->chunks[d->childChunkIndex[childChunkNum]].size,
|
||||
d->endianness == BigEndian),
|
||||
d->chunks[d->childChunkIndex[childChunkNum]].offset - 8, 8);
|
||||
|
||||
// Now add the chunk to the file
|
||||
|
||||
unsigned long long nextRootChunkIdx = length();
|
||||
if((d->childChunkIndex[childChunkNum] + 1) < static_cast<int>(d->chunks.size()))
|
||||
nextRootChunkIdx = d->chunks[d->childChunkIndex[childChunkNum] + 1].offset - 12;
|
||||
|
||||
writeChunk(name, data, offset,
|
||||
static_cast<unsigned long>(
|
||||
nextRootChunkIdx > offset ? nextRootChunkIdx - offset : 0),
|
||||
(offset & 1) ? 1 : 0);
|
||||
|
||||
// For root chunks
|
||||
|
||||
updateRootChunksStructure(d->childChunkIndex[childChunkNum] + 1);
|
||||
|
||||
Chunk64 chunk;
|
||||
chunk.name = name;
|
||||
chunk.size = data.size();
|
||||
chunk.offset = offset + 12;
|
||||
chunk.padding = (data.size() & 0x01) ? 1 : 0;
|
||||
|
||||
childChunks.push_back(chunk);
|
||||
}
|
||||
|
||||
void DSDIFF::File::updateRootChunksStructure(unsigned int startingChunk)
|
||||
{
|
||||
for(unsigned int i = startingChunk; i < d->chunks.size(); i++)
|
||||
d->chunks[i].offset = d->chunks[i - 1].offset + 12
|
||||
+ d->chunks[i - 1].size + d->chunks[i - 1].padding;
|
||||
|
||||
// Update child chunks structure as well
|
||||
|
||||
if(d->childChunkIndex[PROPChunk] >= static_cast<int>(startingChunk)) {
|
||||
ChunkList &childChunksToUpdate = d->childChunks[PROPChunk];
|
||||
if(!childChunksToUpdate.empty()) {
|
||||
childChunksToUpdate[0].offset = d->chunks[d->childChunkIndex[PROPChunk]].offset + 12;
|
||||
for(unsigned int i = 1; i < childChunksToUpdate.size(); i++)
|
||||
childChunksToUpdate[i].offset = childChunksToUpdate[i - 1].offset + 12
|
||||
+ childChunksToUpdate[i - 1].size + childChunksToUpdate[i - 1].padding;
|
||||
}
|
||||
|
||||
}
|
||||
if(d->childChunkIndex[DIINChunk] >= static_cast<int>(startingChunk)) {
|
||||
ChunkList &childChunksToUpdate = d->childChunks[DIINChunk];
|
||||
if(!childChunksToUpdate.empty()) {
|
||||
childChunksToUpdate[0].offset = d->chunks[d->childChunkIndex[DIINChunk]].offset + 12;
|
||||
for(unsigned int i = 1; i < childChunksToUpdate.size(); i++)
|
||||
childChunksToUpdate[i].offset = childChunksToUpdate[i - 1].offset + 12
|
||||
+ childChunksToUpdate[i - 1].size + childChunksToUpdate[i - 1].padding;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DSDIFF::File::read(bool readProperties, Properties::ReadStyle propertiesStyle)
|
||||
{
|
||||
bool bigEndian = d->endianness == BigEndian;
|
||||
|
||||
d->type = readBlock(4);
|
||||
d->size = readBlock(8).toLongLong(bigEndian);
|
||||
d->format = readBlock(4);
|
||||
|
||||
// + 12: chunk header at least, fix for additional junk bytes
|
||||
|
||||
while(tell() + 12 <= length()) {
|
||||
ByteVector chunkName = readBlock(4);
|
||||
unsigned long long chunkSize = readBlock(8).toLongLong(bigEndian);
|
||||
|
||||
if(!isValidChunkID(chunkName)) {
|
||||
debug("DSDIFF::File::read() -- Chunk '" + chunkName + "' has invalid ID");
|
||||
setValid(false);
|
||||
break;
|
||||
}
|
||||
|
||||
if(static_cast<unsigned long long>(tell()) + chunkSize >
|
||||
static_cast<unsigned long long>(length())) {
|
||||
debug("DSDIFF::File::read() -- Chunk '" + chunkName
|
||||
+ "' has invalid size (larger than the file size)");
|
||||
setValid(false);
|
||||
break;
|
||||
}
|
||||
|
||||
Chunk64 chunk;
|
||||
chunk.name = chunkName;
|
||||
chunk.size = chunkSize;
|
||||
chunk.offset = tell();
|
||||
|
||||
seek(chunk.size, Current);
|
||||
|
||||
// Check padding
|
||||
|
||||
chunk.padding = 0;
|
||||
offset_t 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);
|
||||
}
|
||||
|
||||
// For DSD uncompressed
|
||||
unsigned long long lengthDSDSamplesTimeChannels = 0;
|
||||
// For computing bitrate
|
||||
unsigned long long audioDataSizeinBytes = 0;
|
||||
// For DST compressed frames
|
||||
unsigned long dstNumFrames = 0;
|
||||
// For DST compressed frames
|
||||
unsigned short dstFrameRate = 0;
|
||||
|
||||
for(unsigned int i = 0; i < d->chunks.size(); i++) {
|
||||
if(d->chunks[i].name == "DSD ") {
|
||||
lengthDSDSamplesTimeChannels = d->chunks[i].size * 8;
|
||||
audioDataSizeinBytes = d->chunks[i].size;
|
||||
}
|
||||
else if(d->chunks[i].name == "DST ") {
|
||||
// Now decode the chunks inside the DST chunk to read the DST Frame Information one
|
||||
long long dstChunkEnd = d->chunks[i].offset + d->chunks[i].size;
|
||||
seek(d->chunks[i].offset);
|
||||
|
||||
audioDataSizeinBytes = d->chunks[i].size;
|
||||
|
||||
while(tell() + 12 <= dstChunkEnd) {
|
||||
ByteVector dstChunkName = readBlock(4);
|
||||
long long dstChunkSize = readBlock(8).toLongLong(bigEndian);
|
||||
|
||||
if(!isValidChunkID(dstChunkName)) {
|
||||
debug("DSDIFF::File::read() -- DST Chunk '" + dstChunkName + "' has invalid ID");
|
||||
setValid(false);
|
||||
break;
|
||||
}
|
||||
|
||||
if(static_cast<long long>(tell()) + dstChunkSize > dstChunkEnd) {
|
||||
debug("DSDIFF::File::read() -- DST Chunk '" + dstChunkName
|
||||
+ "' has invalid size (larger than the DST chunk)");
|
||||
setValid(false);
|
||||
break;
|
||||
}
|
||||
|
||||
if(dstChunkName == "FRTE") {
|
||||
// Found the DST frame information chunk
|
||||
dstNumFrames = readBlock(4).toUInt(bigEndian);
|
||||
dstFrameRate = readBlock(2).toUShort(bigEndian);
|
||||
// Found the wanted one, no need to look at the others
|
||||
break;
|
||||
}
|
||||
|
||||
seek(dstChunkSize, Current);
|
||||
|
||||
// Check padding
|
||||
offset_t 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;
|
||||
// +4 to remove the 'SND ' marker at beginning of 'PROP' chunk
|
||||
seek(d->chunks[i].offset + 4);
|
||||
while(tell() + 12 <= propChunkEnd) {
|
||||
ByteVector propChunkName = readBlock(4);
|
||||
long long propChunkSize = readBlock(8).toLongLong(bigEndian);
|
||||
|
||||
if(!isValidChunkID(propChunkName)) {
|
||||
debug("DSDIFF::File::read() -- PROP Chunk '" + propChunkName + "' has invalid ID");
|
||||
setValid(false);
|
||||
break;
|
||||
}
|
||||
|
||||
if(static_cast<long long>(tell()) + propChunkSize > propChunkEnd) {
|
||||
debug("DSDIFF::File::read() -- PROP Chunk '" + propChunkName
|
||||
+ "' has invalid size (larger than the PROP chunk)");
|
||||
setValid(false);
|
||||
break;
|
||||
}
|
||||
|
||||
Chunk64 chunk;
|
||||
chunk.name = propChunkName;
|
||||
chunk.size = propChunkSize;
|
||||
chunk.offset = tell();
|
||||
|
||||
seek(chunk.size, Current);
|
||||
|
||||
// Check padding
|
||||
chunk.padding = 0;
|
||||
offset_t 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 = readBlock(8).toLongLong(bigEndian);
|
||||
|
||||
if(!isValidChunkID(diinChunkName)) {
|
||||
debug("DSDIFF::File::read() -- DIIN Chunk '" + diinChunkName + "' has invalid ID");
|
||||
setValid(false);
|
||||
break;
|
||||
}
|
||||
|
||||
if(static_cast<long long>(tell()) + diinChunkSize > diinChunkEnd) {
|
||||
debug("DSDIFF::File::read() -- DIIN Chunk '" + diinChunkName
|
||||
+ "' has invalid size (larger than the DIIN chunk)");
|
||||
setValid(false);
|
||||
break;
|
||||
}
|
||||
|
||||
Chunk64 chunk;
|
||||
chunk.name = diinChunkName;
|
||||
chunk.size = diinChunkSize;
|
||||
chunk.offset = tell();
|
||||
|
||||
seek(chunk.size, Current);
|
||||
|
||||
// Check padding
|
||||
|
||||
chunk.padding = 0;
|
||||
offset_t 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->ID3v2FrameFactory));
|
||||
d->isID3InPropChunk = false;
|
||||
d->hasID3v2 = true;
|
||||
}
|
||||
}
|
||||
|
||||
if(!isValid())
|
||||
return;
|
||||
|
||||
if(d->childChunkIndex[PROPChunk] < 0) {
|
||||
debug("DSDIFF::File::read() -- no PROP chunk found");
|
||||
setValid(false);
|
||||
return;
|
||||
}
|
||||
|
||||
// Read properties
|
||||
|
||||
unsigned int sampleRate = 0;
|
||||
unsigned short channels = 0;
|
||||
|
||||
for(unsigned int i = 0; i < d->childChunks[PROPChunk].size(); i++) {
|
||||
if(d->childChunks[PROPChunk][i].name == "ID3 " ||
|
||||
d->childChunks[PROPChunk][i].name == "id3 ") {
|
||||
if(d->hasID3v2) {
|
||||
d->duplicateID3V2chunkIndex = i;
|
||||
// ID3V2 tag has already been found at root level
|
||||
continue;
|
||||
}
|
||||
d->id3v2TagChunkID = d->childChunks[PROPChunk][i].name;
|
||||
d->tag.set(ID3v2Index, new ID3v2::Tag(this, d->childChunks[PROPChunk][i].offset,
|
||||
d->ID3v2FrameFactory));
|
||||
d->isID3InPropChunk = true;
|
||||
d->hasID3v2 = true;
|
||||
}
|
||||
else if(d->childChunks[PROPChunk][i].name == "FS ") {
|
||||
// Sample rate
|
||||
seek(d->childChunks[PROPChunk][i].offset);
|
||||
sampleRate = readBlock(4).toUInt(0, 4, bigEndian);
|
||||
}
|
||||
else if(d->childChunks[PROPChunk][i].name == "CHNL") {
|
||||
// Channels
|
||||
seek(d->childChunks[PROPChunk][i].offset);
|
||||
channels = readBlock(2).toShort(0, bigEndian);
|
||||
}
|
||||
}
|
||||
|
||||
// Read title & artist from DIIN chunk
|
||||
|
||||
d->tag.access<DSDIFF::DIIN::Tag>(DIINIndex, true);
|
||||
|
||||
if(d->hasDiin) {
|
||||
for(unsigned int i = 0; i < d->childChunks[DIINChunk].size(); i++) {
|
||||
if(d->childChunks[DIINChunk][i].name == "DITI") {
|
||||
seek(d->childChunks[DIINChunk][i].offset);
|
||||
unsigned int titleStrLength = readBlock(4).toUInt(0, 4, bigEndian);
|
||||
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 = readBlock(4).toUInt(0, 4, bigEndian);
|
||||
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 = static_cast<unsigned long long>(dstNumFrames) *
|
||||
static_cast<unsigned long long>(sampleRate) /
|
||||
static_cast<unsigned long long>(dstFrameRate);
|
||||
else
|
||||
lengthDSDSamplesTimeChannels = 0;
|
||||
}
|
||||
else {
|
||||
// In DSD uncompressed files, the read number of samples is the total for each channel
|
||||
if(channels > 0)
|
||||
lengthDSDSamplesTimeChannels /= channels;
|
||||
}
|
||||
int bitrate = 0;
|
||||
if(lengthDSDSamplesTimeChannels > 0)
|
||||
bitrate = static_cast<int>(
|
||||
(audioDataSizeinBytes * 8 * sampleRate) / lengthDSDSamplesTimeChannels / 1000);
|
||||
|
||||
d->properties = std::make_unique<Properties>(sampleRate, channels,
|
||||
lengthDSDSamplesTimeChannels, bitrate, propertiesStyle);
|
||||
}
|
||||
|
||||
if(!ID3v2Tag()) {
|
||||
d->tag.access<ID3v2::Tag>(ID3v2Index, true, d->ID3v2FrameFactory);
|
||||
// By default, ID3 chunk is at root level
|
||||
d->isID3InPropChunk = false;
|
||||
d->hasID3v2 = false;
|
||||
}
|
||||
}
|
||||
|
||||
void DSDIFF::File::writeChunk(const ByteVector &name, const ByteVector &data,
|
||||
unsigned long long offset, unsigned long replace,
|
||||
unsigned int leadingPadding)
|
||||
{
|
||||
ByteVector combined;
|
||||
if(leadingPadding)
|
||||
combined.append(ByteVector(leadingPadding, '\x00'));
|
||||
|
||||
combined.append(name);
|
||||
combined.append(ByteVector::fromLongLong(data.size(), d->endianness == BigEndian));
|
||||
combined.append(data);
|
||||
if((data.size() & 0x01) != 0)
|
||||
combined.append('\x00');
|
||||
|
||||
insert(combined, offset, replace);
|
||||
}
|
||||
292
taglib/dsdiff/dsdifffile.h
Normal file
292
taglib/dsdiff/dsdifffile.h
Normal file
@@ -0,0 +1,292 @@
|
||||
/***************************************************************************
|
||||
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 explicitly 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:
|
||||
|
||||
/*!
|
||||
* This set of flags is used for various operations and is suitable for
|
||||
* being OR-ed together.
|
||||
*/
|
||||
enum TagTypes {
|
||||
//! Empty set. Matches no tag types.
|
||||
NoTags = 0x0000,
|
||||
//! Matches DIIN tags.
|
||||
DIIN = 0x0001,
|
||||
//! Matches ID3v2 tags.
|
||||
ID3v2 = 0x0002,
|
||||
//! Matches all tag types.
|
||||
AllTags = 0xffff
|
||||
};
|
||||
|
||||
/*!
|
||||
* Constructs a DSDIFF file from \a file. If \a readProperties is true
|
||||
* the file's audio properties will also be read.
|
||||
*
|
||||
* \note In the current implementation, \a propertiesStyle is ignored.
|
||||
*
|
||||
* If this file contains an ID3v2 tag, the frames will be created using
|
||||
* \a frameFactory (default if null).
|
||||
*/
|
||||
File(FileName file, bool readProperties = true,
|
||||
Properties::ReadStyle propertiesStyle = Properties::Average,
|
||||
ID3v2::FrameFactory *frameFactory = nullptr);
|
||||
|
||||
/*!
|
||||
* Constructs an DSDIFF file from \a stream. If \a readProperties is true
|
||||
* the file's audio properties will also be read.
|
||||
*
|
||||
* If this file contains an ID3v2 tag, the frames will be created using
|
||||
* \a frameFactory (default if null).
|
||||
*
|
||||
* \note TagLib will *not* take ownership of the stream, the caller is
|
||||
* responsible for deleting it after the File object.
|
||||
*
|
||||
* \note In the current implementation, \a propertiesStyle is ignored.
|
||||
*/
|
||||
File(IOStream *stream, bool readProperties = true,
|
||||
Properties::ReadStyle propertiesStyle = Properties::Average,
|
||||
ID3v2::FrameFactory *frameFactory = nullptr);
|
||||
|
||||
/*!
|
||||
* Destroys this instance of the File.
|
||||
*/
|
||||
~File() override;
|
||||
|
||||
/*!
|
||||
* Returns a pointer to a tag that is the union of the ID3v2 and DIIN
|
||||
* tags. The ID3v2 tag is given priority in reading the information -- if
|
||||
* requested information exists in both the ID3v2 tag and the ID3v1 tag,
|
||||
* the information from the ID3v2 tag will be returned.
|
||||
*
|
||||
* If you would like more granular control over the content of the tags,
|
||||
* with the concession of generality, use the tag-type specific calls.
|
||||
*
|
||||
* \note As this tag is not implemented as an ID3v2 tag or a DIIN tag,
|
||||
* but a union of the two this pointer may not be cast to the specific
|
||||
* tag types.
|
||||
*
|
||||
* \see ID3v2Tag()
|
||||
* \see DIINTag()
|
||||
*/
|
||||
Tag *tag() const override;
|
||||
|
||||
/*!
|
||||
* Returns the ID3V2 Tag for this file.
|
||||
*
|
||||
* \note This always returns a valid pointer regardless of whether or not
|
||||
* the file on disk has an ID3v2 tag. Use hasID3v2Tag() to check if the
|
||||
* file on disk actually has an ID3v2 tag.
|
||||
*
|
||||
* \see hasID3v2Tag()
|
||||
*/
|
||||
ID3v2::Tag *ID3v2Tag(bool create = false) const;
|
||||
|
||||
/*!
|
||||
* Returns the DSDIFF DIIN Tag for this file
|
||||
*
|
||||
*/
|
||||
DSDIFF::DIIN::Tag *DIINTag(bool create = false) const;
|
||||
|
||||
/*!
|
||||
* Implements the unified property interface -- export function.
|
||||
* This method forwards to ID3v2::Tag::properties().
|
||||
*/
|
||||
PropertyMap properties() const override;
|
||||
|
||||
void removeUnsupportedProperties(const StringList &properties) override;
|
||||
|
||||
/*!
|
||||
* Implements the unified property interface -- import function.
|
||||
* This method forwards to ID3v2::Tag::setProperties().
|
||||
*/
|
||||
PropertyMap setProperties(const PropertyMap &) override;
|
||||
|
||||
/*!
|
||||
* Returns the AIFF::Properties for this file. If no audio properties
|
||||
* were read then this will return a null pointer.
|
||||
*/
|
||||
Properties *audioProperties() const override;
|
||||
|
||||
/*!
|
||||
* Save the file. If at least one tag -- ID3v1 or DIIN -- exists this
|
||||
* will duplicate its content into the other tag. This returns 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)
|
||||
*/
|
||||
bool save() override;
|
||||
|
||||
/*!
|
||||
* Save the file. If \a strip is specified, it is possible to choose if
|
||||
* tags not specified in \a tags should be stripped from the file or
|
||||
* retained. With \a version, it is possible to specify whether ID3v2.4
|
||||
* or ID3v2.3 should be used.
|
||||
*/
|
||||
bool save(int tags, StripTags strip = StripOthers, ID3v2::Version version = ID3v2::v4);
|
||||
|
||||
/*!
|
||||
* This will strip the tags that match the OR-ed together TagTypes from the
|
||||
* file. By default it strips all tags. It returns true if the tags are
|
||||
* successfully stripped.
|
||||
*
|
||||
* \note This will update the file immediately.
|
||||
*/
|
||||
void strip(int tags = AllTags);
|
||||
|
||||
/*!
|
||||
* Returns whether or not the file on disk actually has an ID3v2 tag.
|
||||
*
|
||||
* \see ID3v2Tag()
|
||||
*/
|
||||
bool hasID3v2Tag() const;
|
||||
|
||||
/*!
|
||||
* Returns whether or not the file on disk actually has the DSDIFF
|
||||
* title and artist tags.
|
||||
*
|
||||
* \see DIINTag()
|
||||
*/
|
||||
bool hasDIINTag() const;
|
||||
|
||||
/*!
|
||||
* Returns whether or not the given \a stream can be opened as a DSDIFF
|
||||
* file.
|
||||
*
|
||||
* \note This method is designed to do a quick check. The result may
|
||||
* not necessarily be correct.
|
||||
*/
|
||||
static bool isSupported(IOStream *stream);
|
||||
|
||||
protected:
|
||||
enum Endianness { BigEndian, LittleEndian };
|
||||
|
||||
File(FileName file, Endianness endianness);
|
||||
File(IOStream *stream, Endianness endianness);
|
||||
|
||||
private:
|
||||
void removeRootChunk(const ByteVector &id);
|
||||
void removeRootChunk(unsigned int i);
|
||||
void removeChildChunk(const ByteVector &id, unsigned int childChunkNum);
|
||||
void removeChildChunk(unsigned int i, unsigned int childChunkNum);
|
||||
|
||||
/*!
|
||||
* Sets the data for the 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, Properties::ReadStyle propertiesStyle);
|
||||
void writeChunk(const ByteVector &name, const ByteVector &data,
|
||||
unsigned long long offset, unsigned long replace = 0,
|
||||
unsigned int leadingPadding = 0);
|
||||
|
||||
class FilePrivate;
|
||||
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
|
||||
std::unique_ptr<FilePrivate> d;
|
||||
};
|
||||
} // namespace DSDIFF
|
||||
} // namespace TagLib
|
||||
|
||||
#endif
|
||||
101
taglib/dsdiff/dsdiffproperties.cpp
Normal file
101
taglib/dsdiff/dsdiffproperties.cpp
Normal file
@@ -0,0 +1,101 @@
|
||||
/***************************************************************************
|
||||
copyright : (C) 2016 by Damien Plisson, Audirvana
|
||||
email : damien78@audirvana.com
|
||||
***************************************************************************/
|
||||
|
||||
/***************************************************************************
|
||||
* This library is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU Lesser General Public License version *
|
||||
* 2.1 as published by the Free Software Foundation. *
|
||||
* *
|
||||
* This library is distributed in the hope that it will be useful, but *
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
||||
* Lesser General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Lesser General Public *
|
||||
* License along with this library; if not, write to the Free Software *
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
|
||||
* 02110-1301 USA *
|
||||
* *
|
||||
* Alternatively, this file is available under the Mozilla Public *
|
||||
* License Version 1.1. You may obtain a copy of the License at *
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#include "dsdiffproperties.h"
|
||||
|
||||
#include "tstring.h"
|
||||
#include "tdebug.h"
|
||||
|
||||
using namespace TagLib;
|
||||
|
||||
class DSDIFF::Properties::PropertiesPrivate
|
||||
{
|
||||
public:
|
||||
int length { 0 };
|
||||
int bitrate { 0 };
|
||||
int sampleRate { 0 };
|
||||
int channels { 0 };
|
||||
int sampleWidth { 0 };
|
||||
unsigned long long sampleCount { 0 };
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// public members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
DSDIFF::Properties::Properties(const unsigned int sampleRate,
|
||||
const unsigned short channels,
|
||||
const unsigned long long samplesCount,
|
||||
const int bitrate,
|
||||
ReadStyle style) :
|
||||
AudioProperties(style),
|
||||
d(std::make_unique<PropertiesPrivate>())
|
||||
{
|
||||
d->channels = channels;
|
||||
d->sampleCount = samplesCount;
|
||||
d->sampleWidth = 1;
|
||||
d->sampleRate = sampleRate;
|
||||
d->bitrate = bitrate;
|
||||
d->length = d->sampleRate > 0
|
||||
? static_cast<int>((d->sampleCount * 1000.0) / d->sampleRate + 0.5)
|
||||
: 0;
|
||||
}
|
||||
|
||||
DSDIFF::Properties::~Properties() = default;
|
||||
|
||||
int DSDIFF::Properties::lengthInSeconds() const
|
||||
{
|
||||
return d->length / 1000;
|
||||
}
|
||||
|
||||
int DSDIFF::Properties::lengthInMilliseconds() const
|
||||
{
|
||||
return d->length;
|
||||
}
|
||||
|
||||
int DSDIFF::Properties::bitrate() const
|
||||
{
|
||||
return d->bitrate;
|
||||
}
|
||||
|
||||
int DSDIFF::Properties::sampleRate() const
|
||||
{
|
||||
return d->sampleRate;
|
||||
}
|
||||
|
||||
int DSDIFF::Properties::channels() const
|
||||
{
|
||||
return d->channels;
|
||||
}
|
||||
|
||||
int DSDIFF::Properties::bitsPerSample() const
|
||||
{
|
||||
return d->sampleWidth;
|
||||
}
|
||||
|
||||
long long DSDIFF::Properties::sampleCount() const
|
||||
{
|
||||
return d->sampleCount;
|
||||
}
|
||||
79
taglib/dsdiff/dsdiffproperties.h
Normal file
79
taglib/dsdiff/dsdiffproperties.h
Normal file
@@ -0,0 +1,79 @@
|
||||
/***************************************************************************
|
||||
copyright : (C) 2016 by Damien Plisson, Audirvana
|
||||
email : damien78@audirvana.com
|
||||
***************************************************************************/
|
||||
|
||||
/***************************************************************************
|
||||
* This library is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU Lesser General Public License version *
|
||||
* 2.1 as published by the Free Software Foundation. *
|
||||
* *
|
||||
* This library is distributed in the hope that it will be useful, but *
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
||||
* Lesser General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Lesser General Public *
|
||||
* License along with this library; if not, write to the Free Software *
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
|
||||
* 02110-1301 USA *
|
||||
* *
|
||||
* Alternatively, this file is available under the Mozilla Public *
|
||||
* License Version 1.1. You may obtain a copy of the License at *
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef TAGLIB_DSDIFFPROPERTIES_H
|
||||
#define TAGLIB_DSDIFFPROPERTIES_H
|
||||
|
||||
#include "audioproperties.h"
|
||||
|
||||
namespace TagLib {
|
||||
|
||||
namespace DSDIFF {
|
||||
|
||||
class File;
|
||||
|
||||
//! An implementation of audio property reading for DSDIFF
|
||||
|
||||
/*!
|
||||
* This reads the data from an DSDIFF stream found in the AudioProperties
|
||||
* API.
|
||||
*/
|
||||
|
||||
class TAGLIB_EXPORT Properties : public AudioProperties
|
||||
{
|
||||
public:
|
||||
/*!
|
||||
* Create an instance of DSDIFF::Properties with the data read from the
|
||||
* ByteVector \a data.
|
||||
*/
|
||||
Properties(const unsigned int sampleRate, const unsigned short channels,
|
||||
const unsigned long long samplesCount, const int bitrate,
|
||||
ReadStyle style);
|
||||
|
||||
/*!
|
||||
* Destroys this DSDIFF::Properties instance.
|
||||
*/
|
||||
~Properties() override;
|
||||
|
||||
// Reimplementations.
|
||||
|
||||
int lengthInSeconds() const override;
|
||||
int lengthInMilliseconds() const override;
|
||||
int bitrate() const override;
|
||||
int sampleRate() const override;
|
||||
int channels() const override;
|
||||
|
||||
int bitsPerSample() const;
|
||||
long long sampleCount() const;
|
||||
|
||||
private:
|
||||
class PropertiesPrivate;
|
||||
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
|
||||
std::unique_ptr<PropertiesPrivate> d;
|
||||
};
|
||||
} // namespace DSDIFF
|
||||
} // namespace TagLib
|
||||
|
||||
#endif
|
||||
232
taglib/dsf/dsffile.cpp
Normal file
232
taglib/dsf/dsffile.cpp
Normal file
@@ -0,0 +1,232 @@
|
||||
/***************************************************************************
|
||||
copyright : (C) 2013-2023 Stephen F. Booth
|
||||
email : me@sbooth.org
|
||||
***************************************************************************/
|
||||
|
||||
/***************************************************************************
|
||||
* This library is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU Lesser General Public License version *
|
||||
* 2.1 as published by the Free Software Foundation. *
|
||||
* *
|
||||
* This library is distributed in the hope that it will be useful, but *
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
||||
* Lesser General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Lesser General Public *
|
||||
* License along with this library; if not, write to the Free Software *
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
|
||||
* 02110-1301 USA *
|
||||
* *
|
||||
* Alternatively, this file is available under the Mozilla Public *
|
||||
* License Version 1.1. You may obtain a copy of the License at *
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#include "dsffile.h"
|
||||
|
||||
#include "tdebug.h"
|
||||
#include "tpropertymap.h"
|
||||
#include "tagutils.h"
|
||||
|
||||
using namespace TagLib;
|
||||
|
||||
// The DSF specification is located at http://dsd-guide.com/sites/default/files/white-papers/DSFFileFormatSpec_E.pdf
|
||||
|
||||
class DSF::File::FilePrivate
|
||||
{
|
||||
public:
|
||||
FilePrivate(ID3v2::FrameFactory *frameFactory)
|
||||
: ID3v2FrameFactory(frameFactory ? frameFactory
|
||||
: ID3v2::FrameFactory::instance())
|
||||
{
|
||||
}
|
||||
|
||||
~FilePrivate() = default;
|
||||
|
||||
FilePrivate(const FilePrivate &) = delete;
|
||||
FilePrivate &operator=(const FilePrivate &) = delete;
|
||||
|
||||
const ID3v2::FrameFactory *ID3v2FrameFactory;
|
||||
long long fileSize = 0;
|
||||
long long metadataOffset = 0;
|
||||
std::unique_ptr<Properties> properties;
|
||||
std::unique_ptr<ID3v2::Tag> tag;
|
||||
};
|
||||
|
||||
bool DSF::File::isSupported(IOStream *stream)
|
||||
{
|
||||
// A DSF file has to start with "DSD "
|
||||
const ByteVector id = Utils::readHeader(stream, 4, false);
|
||||
return id.startsWith("DSD ");
|
||||
}
|
||||
|
||||
DSF::File::File(FileName file, bool readProperties,
|
||||
AudioProperties::ReadStyle propertiesStyle,
|
||||
ID3v2::FrameFactory *frameFactory) :
|
||||
TagLib::File(file),
|
||||
d(std::make_unique<FilePrivate>(frameFactory))
|
||||
{
|
||||
if(isOpen())
|
||||
read(propertiesStyle);
|
||||
}
|
||||
|
||||
DSF::File::File(IOStream *stream, bool readProperties,
|
||||
AudioProperties::ReadStyle propertiesStyle,
|
||||
ID3v2::FrameFactory *frameFactory) :
|
||||
TagLib::File(stream),
|
||||
d(std::make_unique<FilePrivate>(frameFactory))
|
||||
{
|
||||
if(isOpen())
|
||||
read(propertiesStyle);
|
||||
}
|
||||
|
||||
DSF::File::~File() = default;
|
||||
|
||||
ID3v2::Tag *DSF::File::tag() const
|
||||
{
|
||||
return d->tag.get();
|
||||
}
|
||||
|
||||
PropertyMap DSF::File::properties() const
|
||||
{
|
||||
return d->tag->properties();
|
||||
}
|
||||
|
||||
PropertyMap DSF::File::setProperties(const PropertyMap &properties)
|
||||
{
|
||||
return d->tag->setProperties(properties);
|
||||
}
|
||||
|
||||
DSF::Properties *DSF::File::audioProperties() const
|
||||
{
|
||||
return d->properties.get();
|
||||
}
|
||||
|
||||
bool DSF::File::save()
|
||||
{
|
||||
return save(ID3v2::v4);
|
||||
}
|
||||
|
||||
bool DSF::File::save(ID3v2::Version version)
|
||||
{
|
||||
if(readOnly()) {
|
||||
debug("DSF::File::save() - Cannot save to a read only file.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Three things must be updated: the file size, the tag data, and the metadata offset
|
||||
|
||||
if(d->tag->isEmpty()) {
|
||||
long long newFileSize = d->metadataOffset ? d->metadataOffset : d->fileSize;
|
||||
|
||||
// Update the file size
|
||||
if(d->fileSize != newFileSize) {
|
||||
insert(ByteVector::fromLongLong(newFileSize, false), 12, 8);
|
||||
d->fileSize = newFileSize;
|
||||
}
|
||||
|
||||
// Update the metadata offset to 0 since there is no longer a tag
|
||||
if(d->metadataOffset) {
|
||||
insert(ByteVector::fromLongLong(0ULL, false), 20, 8);
|
||||
d->metadataOffset = 0;
|
||||
}
|
||||
|
||||
// Delete the old tag
|
||||
truncate(newFileSize);
|
||||
}
|
||||
else {
|
||||
ByteVector tagData = d->tag->render(version);
|
||||
|
||||
long long newMetadataOffset = d->metadataOffset ? d->metadataOffset : d->fileSize;
|
||||
long long newFileSize = newMetadataOffset + tagData.size();
|
||||
long long oldTagSize = d->fileSize - newMetadataOffset;
|
||||
|
||||
// Update the file size
|
||||
if(d->fileSize != newFileSize) {
|
||||
insert(ByteVector::fromLongLong(newFileSize, false), 12, 8);
|
||||
d->fileSize = newFileSize;
|
||||
}
|
||||
|
||||
// Update the metadata offset
|
||||
if(d->metadataOffset != newMetadataOffset) {
|
||||
insert(ByteVector::fromLongLong(newMetadataOffset, false), 20, 8);
|
||||
d->metadataOffset = newMetadataOffset;
|
||||
}
|
||||
|
||||
// Delete the old tag and write the new one
|
||||
insert(tagData, newMetadataOffset, static_cast<size_t>(oldTagSize));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void DSF::File::read(AudioProperties::ReadStyle propertiesStyle)
|
||||
{
|
||||
if(!isOpen())
|
||||
return;
|
||||
|
||||
// A DSF file consists of four chunks: DSD chunk, format chunk, data chunk, and metadata chunk
|
||||
// The file format is not chunked in the sense of a RIFF File, though
|
||||
|
||||
// DSD chunk
|
||||
ByteVector chunkName = readBlock(4);
|
||||
if(chunkName != "DSD ") {
|
||||
debug("DSF::File::read() -- Not a DSF file.");
|
||||
setValid(false);
|
||||
return;
|
||||
}
|
||||
|
||||
long long dsdHeaderSize = readBlock(8).toLongLong(false);
|
||||
|
||||
// Integrity check
|
||||
if(dsdHeaderSize != 28) {
|
||||
debug("DSF::File::read() -- File is corrupted, wrong DSD header size");
|
||||
setValid(false);
|
||||
return;
|
||||
}
|
||||
|
||||
d->fileSize = readBlock(8).toLongLong(false);
|
||||
|
||||
// File is malformed or corrupted, allow trailing garbage
|
||||
if(d->fileSize > length()) {
|
||||
debug("DSF::File::read() -- File is corrupted wrong length");
|
||||
setValid(false);
|
||||
return;
|
||||
}
|
||||
|
||||
d->metadataOffset = readBlock(8).toLongLong(false);
|
||||
|
||||
// File is malformed or corrupted
|
||||
if(d->metadataOffset > d->fileSize) {
|
||||
debug("DSF::File::read() -- Invalid metadata offset.");
|
||||
setValid(false);
|
||||
return;
|
||||
}
|
||||
|
||||
// Format chunk
|
||||
chunkName = readBlock(4);
|
||||
if(chunkName != "fmt ") {
|
||||
debug("DSF::File::read() -- Missing 'fmt ' chunk.");
|
||||
setValid(false);
|
||||
return;
|
||||
}
|
||||
|
||||
long long fmtHeaderSize = readBlock(8).toLongLong(false);
|
||||
if(fmtHeaderSize != 52) {
|
||||
debug("DSF::File::read() -- File is corrupted, wrong FMT header size");
|
||||
setValid(false);
|
||||
return;
|
||||
}
|
||||
|
||||
d->properties = std::make_unique<Properties>(readBlock(fmtHeaderSize), propertiesStyle);
|
||||
|
||||
// Skip the data chunk
|
||||
|
||||
// A metadata offset of 0 indicates the absence of an ID3v2 tag
|
||||
if(d->metadataOffset == 0)
|
||||
d->tag = std::make_unique<ID3v2::Tag>(nullptr, 0, d->ID3v2FrameFactory);
|
||||
else
|
||||
d->tag = std::make_unique<ID3v2::Tag>(this, d->metadataOffset,
|
||||
d->ID3v2FrameFactory);
|
||||
}
|
||||
136
taglib/dsf/dsffile.h
Normal file
136
taglib/dsf/dsffile.h
Normal file
@@ -0,0 +1,136 @@
|
||||
/***************************************************************************
|
||||
copyright : (C) 2013-2023 Stephen F. Booth
|
||||
email : me@sbooth.org
|
||||
***************************************************************************/
|
||||
|
||||
/***************************************************************************
|
||||
* This library is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU Lesser General Public License version *
|
||||
* 2.1 as published by the Free Software Foundation. *
|
||||
* *
|
||||
* This library is distributed in the hope that it will be useful, but *
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
||||
* Lesser General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Lesser General Public *
|
||||
* License along with this library; if not, write to the Free Software *
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
|
||||
* 02110-1301 USA *
|
||||
* *
|
||||
* Alternatively, this file is available under the Mozilla Public *
|
||||
* License Version 1.1. You may obtain a copy of the License at *
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef TAGLIB_DSFFILE_H
|
||||
#define TAGLIB_DSFFILE_H
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "taglib_export.h"
|
||||
#include "tfile.h"
|
||||
|
||||
#include "dsfproperties.h"
|
||||
|
||||
#include "id3v2tag.h"
|
||||
|
||||
namespace TagLib {
|
||||
namespace DSF {
|
||||
class TAGLIB_EXPORT File : public TagLib::File {
|
||||
public:
|
||||
/*!
|
||||
* Constructs a DSD stream file from \a file.
|
||||
*
|
||||
* \note In the current implementation, both \a readProperties and
|
||||
* \a propertiesStyle are ignored. The audio properties are always
|
||||
* read.
|
||||
*
|
||||
* If this file contains an ID3v2 tag, the frames will be created using
|
||||
* \a frameFactory (default if null).
|
||||
*/
|
||||
File(FileName file, bool readProperties = true,
|
||||
AudioProperties::ReadStyle propertiesStyle =
|
||||
AudioProperties::Average,
|
||||
ID3v2::FrameFactory *frameFactory = nullptr);
|
||||
|
||||
/*!
|
||||
* Constructs a DSD stream file from \a stream.
|
||||
*
|
||||
* \note In the current implementation, both \a readProperties and
|
||||
* \a propertiesStyle are ignored. The audio properties are always
|
||||
* read.
|
||||
*
|
||||
* If this file contains an ID3v2 tag, the frames will be created using
|
||||
* \a frameFactory (default if null).
|
||||
*
|
||||
* \note TagLib will *not* take ownership of the stream, the caller is
|
||||
* responsible for deleting it after the File object.
|
||||
*/
|
||||
File(IOStream *stream, bool readProperties = true,
|
||||
AudioProperties::ReadStyle propertiesStyle =
|
||||
AudioProperties::Average,
|
||||
ID3v2::FrameFactory *frameFactory = nullptr);
|
||||
|
||||
/*!
|
||||
* Destroys this instance of the File.
|
||||
*/
|
||||
~File() override;
|
||||
|
||||
/*!
|
||||
* Returns the ID3v2 Tag for this file.
|
||||
*/
|
||||
ID3v2::Tag *tag() const override;
|
||||
|
||||
/*!
|
||||
* Implements the unified property interface -- export function.
|
||||
* Forwards to ID3v2::Tag::properties().
|
||||
*/
|
||||
PropertyMap properties() const override;
|
||||
|
||||
/*!
|
||||
* Implements the unified property interface -- import function.
|
||||
* Forwards to ID3v2::Tag::setProperties().
|
||||
*/
|
||||
PropertyMap setProperties(const PropertyMap &) override;
|
||||
|
||||
/*!
|
||||
* Returns the DSF::Properties for this file. If no audio properties
|
||||
* were read then this will return a null pointer.
|
||||
*/
|
||||
Properties *audioProperties() const override;
|
||||
|
||||
/*!
|
||||
* Save the file.
|
||||
*
|
||||
* This returns true if the save was successful.
|
||||
*/
|
||||
bool save() override;
|
||||
|
||||
/*!
|
||||
* Save the file.
|
||||
*
|
||||
* \a version specifies the ID3v2 version to be used for writing tags.
|
||||
*/
|
||||
bool save(ID3v2::Version version);
|
||||
|
||||
/*!
|
||||
* Returns whether or not the given \a stream can be opened as a DSF
|
||||
* file.
|
||||
*
|
||||
* \note This method is designed to do a quick check. The result may
|
||||
* not necessarily be correct.
|
||||
*/
|
||||
static bool isSupported(IOStream *stream);
|
||||
|
||||
private:
|
||||
void read(AudioProperties::ReadStyle propertiesStyle);
|
||||
|
||||
class FilePrivate;
|
||||
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
|
||||
std::unique_ptr<FilePrivate> d;
|
||||
};
|
||||
} // namespace DSF
|
||||
} // namespace TagLib
|
||||
|
||||
#endif
|
||||
134
taglib/dsf/dsfproperties.cpp
Normal file
134
taglib/dsf/dsfproperties.cpp
Normal file
@@ -0,0 +1,134 @@
|
||||
/***************************************************************************
|
||||
copyright : (C) 2013-2023 Stephen F. Booth
|
||||
email : me@sbooth.org
|
||||
***************************************************************************/
|
||||
|
||||
/***************************************************************************
|
||||
* This library is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU Lesser General Public License version *
|
||||
* 2.1 as published by the Free Software Foundation. *
|
||||
* *
|
||||
* This library is distributed in the hope that it will be useful, but *
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
||||
* Lesser General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Lesser General Public *
|
||||
* License along with this library; if not, write to the Free Software *
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
|
||||
* 02110-1301 USA *
|
||||
* *
|
||||
* Alternatively, this file is available under the Mozilla Public *
|
||||
* License Version 1.1. You may obtain a copy of the License at *
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
|
||||
#include "dsfproperties.h"
|
||||
|
||||
using namespace TagLib;
|
||||
|
||||
class DSF::Properties::PropertiesPrivate
|
||||
{
|
||||
public:
|
||||
PropertiesPrivate() = default;
|
||||
~PropertiesPrivate() = default;
|
||||
|
||||
PropertiesPrivate(const PropertiesPrivate &) = delete;
|
||||
PropertiesPrivate &operator=(const PropertiesPrivate &) = delete;
|
||||
|
||||
// Nomenclature is from DSF file format specification
|
||||
unsigned int formatVersion = 0;
|
||||
unsigned int formatID = 0;
|
||||
unsigned int channelType = 0;
|
||||
unsigned int channelNum = 0;
|
||||
unsigned int samplingFrequency = 0;
|
||||
unsigned int bitsPerSample = 0;
|
||||
long long sampleCount = 0;
|
||||
unsigned int blockSizePerChannel = 0;
|
||||
|
||||
// Computed
|
||||
unsigned int bitrate = 0;
|
||||
unsigned int length = 0;
|
||||
};
|
||||
|
||||
DSF::Properties::Properties(const ByteVector &data, ReadStyle style) :
|
||||
AudioProperties(style),
|
||||
d(std::make_unique<PropertiesPrivate>())
|
||||
{
|
||||
read(data);
|
||||
}
|
||||
|
||||
DSF::Properties::~Properties() = default;
|
||||
|
||||
int DSF::Properties::lengthInMilliseconds() const
|
||||
{
|
||||
return d->length;
|
||||
}
|
||||
|
||||
int DSF::Properties::bitrate() const
|
||||
{
|
||||
return d->bitrate;
|
||||
}
|
||||
|
||||
int DSF::Properties::sampleRate() const
|
||||
{
|
||||
return d->samplingFrequency;
|
||||
}
|
||||
|
||||
int DSF::Properties::channels() const
|
||||
{
|
||||
return d->channelNum;
|
||||
}
|
||||
|
||||
int DSF::Properties::formatVersion() const
|
||||
{
|
||||
return d->formatVersion;
|
||||
}
|
||||
|
||||
int DSF::Properties::formatID() const
|
||||
{
|
||||
return d->formatID;
|
||||
}
|
||||
|
||||
int DSF::Properties::channelType() const
|
||||
{
|
||||
return d->channelType;
|
||||
}
|
||||
|
||||
int DSF::Properties::bitsPerSample() const
|
||||
{
|
||||
return d->bitsPerSample;
|
||||
}
|
||||
|
||||
long long DSF::Properties::sampleCount() const
|
||||
{
|
||||
return d->sampleCount;
|
||||
}
|
||||
|
||||
int DSF::Properties::blockSizePerChannel() const
|
||||
{
|
||||
return d->blockSizePerChannel;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// private members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void DSF::Properties::read(const ByteVector &data)
|
||||
{
|
||||
d->formatVersion = data.toUInt(0U,false);
|
||||
d->formatID = data.toUInt(4U,false);
|
||||
d->channelType = data.toUInt(8U,false);
|
||||
d->channelNum = data.toUInt(12U,false);
|
||||
d->samplingFrequency = data.toUInt(16U,false);
|
||||
d->bitsPerSample = data.toUInt(20U,false);
|
||||
d->sampleCount = data.toLongLong(24U,false);
|
||||
d->blockSizePerChannel = data.toUInt(32U,false);
|
||||
|
||||
d->bitrate = static_cast<unsigned int>(
|
||||
(d->samplingFrequency * d->bitsPerSample * d->channelNum) / 1000.0 + 0.5);
|
||||
d->length = d->samplingFrequency > 0
|
||||
? static_cast<unsigned int>(d->sampleCount * 1000.0 / d->samplingFrequency + 0.5)
|
||||
: 0;
|
||||
}
|
||||
72
taglib/dsf/dsfproperties.h
Normal file
72
taglib/dsf/dsfproperties.h
Normal file
@@ -0,0 +1,72 @@
|
||||
/***************************************************************************
|
||||
copyright : (C) 2013-2023 Stephen F. Booth
|
||||
email : me@sbooth.org
|
||||
***************************************************************************/
|
||||
|
||||
/***************************************************************************
|
||||
* This library is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU Lesser General Public License version *
|
||||
* 2.1 as published by the Free Software Foundation. *
|
||||
* *
|
||||
* This library is distributed in the hope that it will be useful, but *
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
||||
* Lesser General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Lesser General Public *
|
||||
* License along with this library; if not, write to the Free Software *
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
|
||||
* 02110-1301 USA *
|
||||
* *
|
||||
* Alternatively, this file is available under the Mozilla Public *
|
||||
* License Version 1.1. You may obtain a copy of the License at *
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef TAGLIB_DSFPROPERTIES_H
|
||||
#define TAGLIB_DSFPROPERTIES_H
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "taglib_export.h"
|
||||
#include "tbytevector.h"
|
||||
#include "audioproperties.h"
|
||||
|
||||
namespace TagLib {
|
||||
namespace DSF {
|
||||
class TAGLIB_EXPORT Properties : public AudioProperties {
|
||||
public:
|
||||
Properties(const ByteVector &data, ReadStyle style);
|
||||
~Properties() override;
|
||||
|
||||
Properties(const Properties &) = delete;
|
||||
Properties &operator=(const Properties &) = delete;
|
||||
|
||||
int lengthInMilliseconds() const override;
|
||||
int bitrate() const override;
|
||||
int sampleRate() const override;
|
||||
int channels() const override;
|
||||
|
||||
int formatVersion() const;
|
||||
int formatID() const;
|
||||
|
||||
/*!
|
||||
* Channel type values: 1 = mono, 2 = stereo, 3 = 3 channels,
|
||||
* 4 = quad, 5 = 4 channels, 6 = 5 channels, 7 = 5.1 channels
|
||||
*/
|
||||
int channelType() const;
|
||||
int bitsPerSample() const;
|
||||
long long sampleCount() const;
|
||||
int blockSizePerChannel() const;
|
||||
|
||||
private:
|
||||
void read(const ByteVector &data);
|
||||
|
||||
class PropertiesPrivate;
|
||||
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
|
||||
std::unique_ptr<PropertiesPrivate> d;
|
||||
};
|
||||
} // namespace DSF
|
||||
} // namespace TagLib
|
||||
|
||||
#endif
|
||||
@@ -27,92 +27,348 @@
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#include <tfile.h>
|
||||
#include <tstring.h>
|
||||
#include <tdebug.h>
|
||||
#include "trefcounter.h"
|
||||
|
||||
#include "fileref.h"
|
||||
#include "asffile.h"
|
||||
#include "mpegfile.h"
|
||||
#include "vorbisfile.h"
|
||||
#include "flacfile.h"
|
||||
#include "oggflacfile.h"
|
||||
#include "mpcfile.h"
|
||||
#include "mp4file.h"
|
||||
#include "wavpackfile.h"
|
||||
#include "speexfile.h"
|
||||
#include "opusfile.h"
|
||||
#include "trueaudiofile.h"
|
||||
|
||||
#include <cstring>
|
||||
#include <utility>
|
||||
|
||||
#include "tfilestream.h"
|
||||
#include "tpropertymap.h"
|
||||
#include "tstringlist.h"
|
||||
#include "tvariant.h"
|
||||
#include "tdebug.h"
|
||||
#include "aifffile.h"
|
||||
#include "wavfile.h"
|
||||
#include "apefile.h"
|
||||
#include "modfile.h"
|
||||
#include "s3mfile.h"
|
||||
#include "asffile.h"
|
||||
#include "flacfile.h"
|
||||
#include "itfile.h"
|
||||
#include "modfile.h"
|
||||
#include "mp4file.h"
|
||||
#include "mpcfile.h"
|
||||
#include "mpegfile.h"
|
||||
#include "oggflacfile.h"
|
||||
#include "opusfile.h"
|
||||
#include "s3mfile.h"
|
||||
#include "speexfile.h"
|
||||
#include "trueaudiofile.h"
|
||||
#include "vorbisfile.h"
|
||||
#include "wavfile.h"
|
||||
#include "wavpackfile.h"
|
||||
#include "xmfile.h"
|
||||
#include "dsffile.h"
|
||||
#include "dsdifffile.h"
|
||||
|
||||
using namespace TagLib;
|
||||
|
||||
class FileRef::FileRefPrivate : public RefCounter
|
||||
class FileRef::FileTypeResolver::FileTypeResolverPrivate
|
||||
{
|
||||
public:
|
||||
FileRefPrivate(File *f) : RefCounter(), file(f) {}
|
||||
~FileRefPrivate() {
|
||||
delete file;
|
||||
}
|
||||
|
||||
File *file;
|
||||
static List<const FileTypeResolver *> fileTypeResolvers;
|
||||
};
|
||||
|
||||
List<const FileRef::FileTypeResolver *> FileRef::FileRefPrivate::fileTypeResolvers;
|
||||
class FileRef::StreamTypeResolver::StreamTypeResolverPrivate
|
||||
{
|
||||
};
|
||||
|
||||
namespace
|
||||
{
|
||||
List<const FileRef::FileTypeResolver *> fileTypeResolvers;
|
||||
|
||||
// Detect the file type by user-defined resolvers.
|
||||
|
||||
File *detectByResolvers(FileName fileName, bool readAudioProperties,
|
||||
AudioProperties::ReadStyle audioPropertiesStyle)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
if(::wcslen(fileName) == 0)
|
||||
return nullptr;
|
||||
#else
|
||||
if(::strlen(fileName) == 0)
|
||||
return nullptr;
|
||||
#endif
|
||||
for(const auto &resolver : std::as_const(fileTypeResolvers)) {
|
||||
File *file = resolver->createFile(fileName, readAudioProperties, audioPropertiesStyle);
|
||||
if(file)
|
||||
return file;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
File *detectByResolvers(IOStream* stream, bool readAudioProperties,
|
||||
AudioProperties::ReadStyle audioPropertiesStyle)
|
||||
{
|
||||
for(const auto &resolver : std::as_const(fileTypeResolvers)) {
|
||||
if(auto streamResolver = dynamic_cast<const FileRef::StreamTypeResolver *>(resolver)) {
|
||||
if(File *file = streamResolver->createFileFromStream(
|
||||
stream, readAudioProperties, audioPropertiesStyle))
|
||||
return file;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Detect the file type based on the file extension.
|
||||
|
||||
File* detectByExtension(IOStream *stream, bool readAudioProperties,
|
||||
AudioProperties::ReadStyle audioPropertiesStyle)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
const String s = stream->name().toString();
|
||||
#else
|
||||
const String s(stream->name());
|
||||
#endif
|
||||
|
||||
String ext;
|
||||
const int pos = s.rfind(".");
|
||||
if(pos != -1)
|
||||
ext = s.substr(pos + 1).upper();
|
||||
|
||||
// If this list is updated, the method defaultFileExtensions() should also be
|
||||
// updated. However at some point that list should be created at the same time
|
||||
// that a default file type resolver is created.
|
||||
|
||||
if(ext.isEmpty())
|
||||
return nullptr;
|
||||
|
||||
// .oga can be any audio in the Ogg container. So leave it to content-based detection.
|
||||
|
||||
File *file = nullptr;
|
||||
|
||||
if(ext == "MP3" || ext == "MP2" || ext == "AAC")
|
||||
file = new MPEG::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
else if(ext == "OGG")
|
||||
file = new Ogg::Vorbis::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
else if(ext == "OGA") {
|
||||
/* .oga can be any audio in the Ogg container. First try FLAC, then Vorbis. */
|
||||
file = new Ogg::FLAC::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
if(!file->isValid()) {
|
||||
delete file;
|
||||
file = new Ogg::Vorbis::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
}
|
||||
}
|
||||
else if(ext == "FLAC")
|
||||
file = new FLAC::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
else if(ext == "MPC")
|
||||
file = new MPC::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
else if(ext == "WV")
|
||||
file = new WavPack::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
else if(ext == "SPX")
|
||||
file = new Ogg::Speex::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
else if(ext == "OPUS")
|
||||
file = new Ogg::Opus::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
else if(ext == "TTA")
|
||||
file = new TrueAudio::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
else if(ext == "M4A" || ext == "M4R" || ext == "M4B" || ext == "M4P" || ext == "MP4" || ext == "3G2" || ext == "M4V")
|
||||
file = new MP4::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
else if(ext == "WMA" || ext == "ASF")
|
||||
file = new ASF::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
else if(ext == "AIF" || ext == "AIFF" || ext == "AFC" || ext == "AIFC")
|
||||
file = new RIFF::AIFF::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
else if(ext == "WAV")
|
||||
file = new RIFF::WAV::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
else if(ext == "APE")
|
||||
file = new APE::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
// module, nst and wow are possible but uncommon extensions
|
||||
else if(ext == "MOD" || ext == "MODULE" || ext == "NST" || ext == "WOW")
|
||||
file = new Mod::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
else if(ext == "S3M")
|
||||
file = new S3M::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
else if(ext == "IT")
|
||||
file = new IT::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
else if(ext == "XM")
|
||||
file = new XM::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
else if(ext == "DSF")
|
||||
file = new DSF::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
else if(ext == "DFF" || ext == "DSDIFF")
|
||||
file = new DSDIFF::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
|
||||
// if file is not valid, leave it to content-based detection.
|
||||
|
||||
if(file) {
|
||||
if(file->isValid())
|
||||
return file;
|
||||
delete file;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Detect the file type based on the actual content of the stream.
|
||||
|
||||
File *detectByContent(IOStream *stream, bool readAudioProperties,
|
||||
AudioProperties::ReadStyle audioPropertiesStyle)
|
||||
{
|
||||
File *file = nullptr;
|
||||
|
||||
if(MPEG::File::isSupported(stream))
|
||||
file = new MPEG::File(stream, 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(DSF::File::isSupported(stream))
|
||||
file = new DSF::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
else if(DSDIFF::File::isSupported(stream))
|
||||
file = new DSDIFF::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
|
||||
// isSupported() only does a quick check, so double check the file here.
|
||||
|
||||
if(file) {
|
||||
if(file->isValid())
|
||||
return file;
|
||||
delete file;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
class FileRef::FileRefPrivate
|
||||
{
|
||||
public:
|
||||
FileRefPrivate() = default;
|
||||
~FileRefPrivate()
|
||||
{
|
||||
delete file;
|
||||
delete stream;
|
||||
}
|
||||
|
||||
FileRefPrivate(const FileRefPrivate &) = delete;
|
||||
FileRefPrivate &operator=(const FileRefPrivate &) = delete;
|
||||
|
||||
bool isNull() const
|
||||
{
|
||||
return !file || !file->isValid();
|
||||
}
|
||||
|
||||
bool isNullWithDebugMessage(const String &methodName) const
|
||||
{
|
||||
if(isNull()) {
|
||||
debug("FileRef::" + methodName + "() - Called without a valid file.");
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
File *file { nullptr };
|
||||
IOStream *stream { nullptr };
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// public members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
FileRef::FileRef()
|
||||
FileRef::FileRef() :
|
||||
d(std::make_shared<FileRefPrivate>())
|
||||
{
|
||||
d = new FileRefPrivate(0);
|
||||
}
|
||||
|
||||
FileRef::FileRef(FileName fileName, bool readAudioProperties,
|
||||
AudioProperties::ReadStyle audioPropertiesStyle)
|
||||
AudioProperties::ReadStyle audioPropertiesStyle) :
|
||||
d(std::make_shared<FileRefPrivate>())
|
||||
{
|
||||
d = new FileRefPrivate(create(fileName, readAudioProperties, audioPropertiesStyle));
|
||||
parse(fileName, readAudioProperties, audioPropertiesStyle);
|
||||
}
|
||||
|
||||
FileRef::FileRef(File *file)
|
||||
FileRef::FileRef(IOStream *stream, bool readAudioProperties, AudioProperties::ReadStyle audioPropertiesStyle) :
|
||||
d(std::make_shared<FileRefPrivate>())
|
||||
{
|
||||
d = new FileRefPrivate(file);
|
||||
parse(stream, readAudioProperties, audioPropertiesStyle);
|
||||
}
|
||||
|
||||
FileRef::FileRef(const FileRef &ref) : d(ref.d)
|
||||
FileRef::FileRef(File *file) :
|
||||
d(std::make_shared<FileRefPrivate>())
|
||||
{
|
||||
d->ref();
|
||||
d->file = file;
|
||||
}
|
||||
|
||||
FileRef::~FileRef()
|
||||
{
|
||||
if(d->deref())
|
||||
delete d;
|
||||
}
|
||||
FileRef::FileRef(const FileRef &) = default;
|
||||
|
||||
FileRef::~FileRef() = default;
|
||||
|
||||
Tag *FileRef::tag() const
|
||||
{
|
||||
if(isNull()) {
|
||||
debug("FileRef::tag() - Called without a valid file.");
|
||||
return 0;
|
||||
if(d->isNullWithDebugMessage(__func__)) {
|
||||
return nullptr;
|
||||
}
|
||||
return d->file->tag();
|
||||
}
|
||||
|
||||
PropertyMap FileRef::properties() const
|
||||
{
|
||||
if(d->isNullWithDebugMessage(__func__)) {
|
||||
return PropertyMap();
|
||||
}
|
||||
return d->file->properties();
|
||||
}
|
||||
|
||||
void FileRef::removeUnsupportedProperties(const StringList& properties)
|
||||
{
|
||||
if(d->isNullWithDebugMessage(__func__)) {
|
||||
return;
|
||||
}
|
||||
return d->file->removeUnsupportedProperties(properties);
|
||||
}
|
||||
|
||||
PropertyMap FileRef::setProperties(const PropertyMap &properties)
|
||||
{
|
||||
if(d->isNullWithDebugMessage(__func__)) {
|
||||
return PropertyMap();
|
||||
}
|
||||
return d->file->setProperties(properties);
|
||||
}
|
||||
|
||||
StringList FileRef::complexPropertyKeys() const
|
||||
{
|
||||
if(d->isNullWithDebugMessage(__func__)) {
|
||||
return StringList();
|
||||
}
|
||||
return d->file->complexPropertyKeys();
|
||||
}
|
||||
|
||||
List<VariantMap> FileRef::complexProperties(const String &key) const
|
||||
{
|
||||
if(d->isNullWithDebugMessage(__func__)) {
|
||||
return List<VariantMap>();
|
||||
}
|
||||
return d->file->complexProperties(key);
|
||||
}
|
||||
|
||||
bool FileRef::setComplexProperties(const String &key, const List<VariantMap> &value)
|
||||
{
|
||||
if(d->isNullWithDebugMessage(__func__)) {
|
||||
return false;
|
||||
}
|
||||
return d->file->setComplexProperties(key, value);
|
||||
}
|
||||
|
||||
AudioProperties *FileRef::audioProperties() const
|
||||
{
|
||||
if(isNull()) {
|
||||
debug("FileRef::audioProperties() - Called without a valid file.");
|
||||
return 0;
|
||||
if(d->isNullWithDebugMessage(__func__)) {
|
||||
return nullptr;
|
||||
}
|
||||
return d->file->audioProperties();
|
||||
}
|
||||
@@ -124,8 +380,7 @@ File *FileRef::file() const
|
||||
|
||||
bool FileRef::save()
|
||||
{
|
||||
if(isNull()) {
|
||||
debug("FileRef::save() - Called without a valid file.");
|
||||
if(d->isNullWithDebugMessage(__func__)) {
|
||||
return false;
|
||||
}
|
||||
return d->file->save();
|
||||
@@ -133,10 +388,15 @@ bool FileRef::save()
|
||||
|
||||
const FileRef::FileTypeResolver *FileRef::addFileTypeResolver(const FileRef::FileTypeResolver *resolver) // static
|
||||
{
|
||||
FileRefPrivate::fileTypeResolvers.prepend(resolver);
|
||||
fileTypeResolvers.prepend(resolver);
|
||||
return resolver;
|
||||
}
|
||||
|
||||
void FileRef::clearFileTypeResolvers() // static
|
||||
{
|
||||
fileTypeResolvers.clear();
|
||||
}
|
||||
|
||||
StringList FileRef::defaultFileExtensions()
|
||||
{
|
||||
StringList l;
|
||||
@@ -144,21 +404,27 @@ StringList FileRef::defaultFileExtensions()
|
||||
l.append("ogg");
|
||||
l.append("flac");
|
||||
l.append("oga");
|
||||
l.append("opus");
|
||||
l.append("mp3");
|
||||
l.append("mp2");
|
||||
l.append("mpc");
|
||||
l.append("wv");
|
||||
l.append("spx");
|
||||
l.append("tta");
|
||||
l.append("aac");
|
||||
l.append("m4a");
|
||||
l.append("m4r");
|
||||
l.append("m4b");
|
||||
l.append("m4p");
|
||||
l.append("3g2");
|
||||
l.append("mp4");
|
||||
l.append("m4v");
|
||||
l.append("wma");
|
||||
l.append("asf");
|
||||
l.append("aif");
|
||||
l.append("aiff");
|
||||
l.append("afc");
|
||||
l.append("aifc");
|
||||
l.append("wav");
|
||||
l.append("ape");
|
||||
l.append("mod");
|
||||
@@ -168,119 +434,97 @@ StringList FileRef::defaultFileExtensions()
|
||||
l.append("s3m");
|
||||
l.append("it");
|
||||
l.append("xm");
|
||||
l.append("dsf");
|
||||
l.append("dff");
|
||||
l.append("dsdiff"); // alias for "dff"
|
||||
|
||||
return l;
|
||||
}
|
||||
|
||||
bool FileRef::isNull() const
|
||||
{
|
||||
return !d->file || !d->file->isValid();
|
||||
return d->isNull();
|
||||
}
|
||||
|
||||
FileRef &FileRef::operator=(const FileRef &ref)
|
||||
FileRef &FileRef::operator=(const FileRef &) = default;
|
||||
|
||||
void FileRef::swap(FileRef &ref) noexcept
|
||||
{
|
||||
if(&ref == this)
|
||||
return *this;
|
||||
using std::swap;
|
||||
|
||||
if(d->deref())
|
||||
delete d;
|
||||
|
||||
d = ref.d;
|
||||
d->ref();
|
||||
|
||||
return *this;
|
||||
swap(d, ref.d);
|
||||
}
|
||||
|
||||
bool FileRef::operator==(const FileRef &ref) const
|
||||
{
|
||||
return ref.d->file == d->file;
|
||||
return (ref.d->file == d->file);
|
||||
}
|
||||
|
||||
bool FileRef::operator!=(const FileRef &ref) const
|
||||
{
|
||||
return ref.d->file != d->file;
|
||||
return (ref.d->file != d->file);
|
||||
}
|
||||
|
||||
File *FileRef::create(FileName fileName, bool readAudioProperties,
|
||||
AudioProperties::ReadStyle audioPropertiesStyle) // static
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// private members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void FileRef::parse(FileName fileName, bool readAudioProperties,
|
||||
AudioProperties::ReadStyle audioPropertiesStyle)
|
||||
{
|
||||
// Try user-defined resolvers.
|
||||
|
||||
List<const FileTypeResolver *>::ConstIterator it = FileRefPrivate::fileTypeResolvers.begin();
|
||||
d->file = detectByResolvers(fileName, readAudioProperties, audioPropertiesStyle);
|
||||
if(d->file)
|
||||
return;
|
||||
|
||||
for(; it != FileRefPrivate::fileTypeResolvers.end(); ++it) {
|
||||
File *file = (*it)->createFile(fileName, readAudioProperties, audioPropertiesStyle);
|
||||
if(file)
|
||||
return file;
|
||||
}
|
||||
// Try to resolve file types based on the file extension.
|
||||
|
||||
// Ok, this is really dumb for now, but it works for testing.
|
||||
d->stream = new FileStream(fileName);
|
||||
d->file = detectByExtension(d->stream, readAudioProperties, audioPropertiesStyle);
|
||||
if(d->file)
|
||||
return;
|
||||
|
||||
String ext;
|
||||
{
|
||||
#ifdef _WIN32
|
||||
// At last, try to resolve file types based on the actual content.
|
||||
|
||||
String s = fileName.toString();
|
||||
d->file = detectByContent(d->stream, readAudioProperties, audioPropertiesStyle);
|
||||
if(d->file)
|
||||
return;
|
||||
|
||||
#else
|
||||
// Stream have to be closed here if failed to resolve file types.
|
||||
|
||||
String s = fileName;
|
||||
|
||||
#endif
|
||||
|
||||
const int pos = s.rfind(".");
|
||||
if(pos != -1)
|
||||
ext = s.substr(pos + 1).upper();
|
||||
}
|
||||
|
||||
// If this list is updated, the method defaultFileExtensions() should also be
|
||||
// updated. However at some point that list should be created at the same time
|
||||
// that a default file type resolver is created.
|
||||
|
||||
if(!ext.isEmpty()) {
|
||||
if(ext == "MP3")
|
||||
return new MPEG::File(fileName, readAudioProperties, audioPropertiesStyle);
|
||||
if(ext == "OGG")
|
||||
return new Ogg::Vorbis::File(fileName, readAudioProperties, audioPropertiesStyle);
|
||||
if(ext == "OGA") {
|
||||
/* .oga can be any audio in the Ogg container. First try FLAC, then Vorbis. */
|
||||
File *file = new Ogg::FLAC::File(fileName, readAudioProperties, audioPropertiesStyle);
|
||||
if (file->isValid())
|
||||
return file;
|
||||
delete file;
|
||||
return new Ogg::Vorbis::File(fileName, readAudioProperties, audioPropertiesStyle);
|
||||
}
|
||||
if(ext == "FLAC")
|
||||
return new FLAC::File(fileName, readAudioProperties, audioPropertiesStyle);
|
||||
if(ext == "MPC")
|
||||
return new MPC::File(fileName, readAudioProperties, audioPropertiesStyle);
|
||||
if(ext == "WV")
|
||||
return new WavPack::File(fileName, readAudioProperties, audioPropertiesStyle);
|
||||
if(ext == "SPX")
|
||||
return new Ogg::Speex::File(fileName, readAudioProperties, audioPropertiesStyle);
|
||||
if(ext == "OPUS")
|
||||
return new Ogg::Opus::File(fileName, readAudioProperties, audioPropertiesStyle);
|
||||
if(ext == "TTA")
|
||||
return new TrueAudio::File(fileName, readAudioProperties, audioPropertiesStyle);
|
||||
if(ext == "M4A" || ext == "M4R" || ext == "M4B" || ext == "M4P" || ext == "MP4" || ext == "3G2")
|
||||
return new MP4::File(fileName, readAudioProperties, audioPropertiesStyle);
|
||||
if(ext == "WMA" || ext == "ASF")
|
||||
return new ASF::File(fileName, readAudioProperties, audioPropertiesStyle);
|
||||
if(ext == "AIF" || ext == "AIFF" || ext == "AFC" || ext == "AIFC")
|
||||
return new RIFF::AIFF::File(fileName, readAudioProperties, audioPropertiesStyle);
|
||||
if(ext == "WAV")
|
||||
return new RIFF::WAV::File(fileName, readAudioProperties, audioPropertiesStyle);
|
||||
if(ext == "APE")
|
||||
return new APE::File(fileName, readAudioProperties, audioPropertiesStyle);
|
||||
// module, nst and wow are possible but uncommon extensions
|
||||
if(ext == "MOD" || ext == "MODULE" || ext == "NST" || ext == "WOW")
|
||||
return new Mod::File(fileName, readAudioProperties, audioPropertiesStyle);
|
||||
if(ext == "S3M")
|
||||
return new S3M::File(fileName, readAudioProperties, audioPropertiesStyle);
|
||||
if(ext == "IT")
|
||||
return new IT::File(fileName, readAudioProperties, audioPropertiesStyle);
|
||||
if(ext == "XM")
|
||||
return new XM::File(fileName, readAudioProperties, audioPropertiesStyle);
|
||||
}
|
||||
|
||||
return 0;
|
||||
delete d->stream;
|
||||
d->stream = nullptr;
|
||||
}
|
||||
|
||||
void FileRef::parse(IOStream *stream, bool readAudioProperties,
|
||||
AudioProperties::ReadStyle audioPropertiesStyle)
|
||||
{
|
||||
// Try user-defined stream resolvers.
|
||||
|
||||
d->file = detectByResolvers(stream, readAudioProperties, audioPropertiesStyle);
|
||||
if(d->file)
|
||||
return;
|
||||
|
||||
// Try user-defined resolvers.
|
||||
|
||||
d->file = detectByResolvers(stream->name(), readAudioProperties, audioPropertiesStyle);
|
||||
if(d->file)
|
||||
return;
|
||||
|
||||
// Try to resolve file types based on the file extension.
|
||||
|
||||
d->file = detectByExtension(stream, readAudioProperties, audioPropertiesStyle);
|
||||
if(d->file)
|
||||
return;
|
||||
|
||||
// At last, try to resolve file types based on the actual content of the file.
|
||||
|
||||
d->file = detectByContent(stream, readAudioProperties, audioPropertiesStyle);
|
||||
}
|
||||
|
||||
FileRef::FileTypeResolver::FileTypeResolver() = default;
|
||||
FileRef::FileTypeResolver::~FileTypeResolver() = default;
|
||||
|
||||
FileRef::StreamTypeResolver::StreamTypeResolver() = default;
|
||||
FileRef::StreamTypeResolver::~StreamTypeResolver() = default;
|
||||
|
||||
173
taglib/fileref.h
173
taglib/fileref.h
@@ -63,7 +63,7 @@ namespace TagLib {
|
||||
//! A class for pluggable file type resolution.
|
||||
|
||||
/*!
|
||||
* This class is used to add extend TagLib's very basic file name based file
|
||||
* This class is used to extend TagLib's very basic file name based file
|
||||
* type resolution.
|
||||
*
|
||||
* This can be accomplished with:
|
||||
@@ -91,12 +91,20 @@ namespace TagLib {
|
||||
|
||||
class TAGLIB_EXPORT FileTypeResolver
|
||||
{
|
||||
TAGLIB_IGNORE_MISSING_DESTRUCTOR
|
||||
public:
|
||||
FileTypeResolver();
|
||||
/*!
|
||||
* Destroys this FileTypeResolver instance.
|
||||
*/
|
||||
virtual ~FileTypeResolver() = 0;
|
||||
|
||||
FileTypeResolver(const FileTypeResolver &) = delete;
|
||||
FileTypeResolver &operator=(const FileTypeResolver &) = delete;
|
||||
|
||||
/*!
|
||||
* This method must be overridden to provide an additional file type
|
||||
* resolver. If the resolver is able to determine the file type it should
|
||||
* return a valid File object; if not it should return 0.
|
||||
* return a valid File object; if not it should return nullptr.
|
||||
*
|
||||
* \note The created file is then owned by the FileRef and should not be
|
||||
* deleted. Deletion will happen automatically when the FileRef passes
|
||||
@@ -106,6 +114,32 @@ namespace TagLib {
|
||||
bool readAudioProperties = true,
|
||||
AudioProperties::ReadStyle
|
||||
audioPropertiesStyle = AudioProperties::Average) const = 0;
|
||||
private:
|
||||
class FileTypeResolverPrivate;
|
||||
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
|
||||
std::unique_ptr<FileTypeResolverPrivate> d;
|
||||
};
|
||||
|
||||
class TAGLIB_EXPORT StreamTypeResolver : public FileTypeResolver
|
||||
{
|
||||
public:
|
||||
StreamTypeResolver();
|
||||
/*!
|
||||
* Destroys this StreamTypeResolver instance.
|
||||
*/
|
||||
~StreamTypeResolver() override = 0;
|
||||
|
||||
StreamTypeResolver(const StreamTypeResolver &) = delete;
|
||||
StreamTypeResolver &operator=(const StreamTypeResolver &) = delete;
|
||||
|
||||
virtual File *createFileFromStream(IOStream *stream,
|
||||
bool readAudioProperties = true,
|
||||
AudioProperties::ReadStyle
|
||||
audioPropertiesStyle = AudioProperties::Average) const = 0;
|
||||
private:
|
||||
class StreamTypeResolverPrivate;
|
||||
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
|
||||
std::unique_ptr<StreamTypeResolverPrivate> d;
|
||||
};
|
||||
|
||||
/*!
|
||||
@@ -127,6 +161,23 @@ namespace TagLib {
|
||||
AudioProperties::ReadStyle
|
||||
audioPropertiesStyle = AudioProperties::Average);
|
||||
|
||||
/*!
|
||||
* Construct a FileRef from an opened \a IOStream. If \a readAudioProperties
|
||||
* is true then the audio properties will be read using \a audioPropertiesStyle.
|
||||
* If \a readAudioProperties is false then \a audioPropertiesStyle will be
|
||||
* ignored.
|
||||
*
|
||||
* Also see the note in the class documentation about why you may not want to
|
||||
* use this method in your application.
|
||||
*
|
||||
* \note TagLib will *not* take ownership of the stream, the caller is
|
||||
* responsible for deleting it after the File object.
|
||||
*/
|
||||
explicit FileRef(IOStream* stream,
|
||||
bool readAudioProperties = true,
|
||||
AudioProperties::ReadStyle
|
||||
audioPropertiesStyle = AudioProperties::Average);
|
||||
|
||||
/*!
|
||||
* Construct a FileRef using \a file. The FileRef now takes ownership of the
|
||||
* pointer and will delete the File when it passes out of scope.
|
||||
@@ -141,7 +192,7 @@ namespace TagLib {
|
||||
/*!
|
||||
* Destroys this FileRef instance.
|
||||
*/
|
||||
virtual ~FileRef();
|
||||
~FileRef();
|
||||
|
||||
/*!
|
||||
* Returns a pointer to represented file's tag.
|
||||
@@ -149,13 +200,91 @@ namespace TagLib {
|
||||
* \warning This pointer will become invalid when this FileRef and all
|
||||
* copies pass out of scope.
|
||||
*
|
||||
* \warning Do not cast it to any subclasses of \class Tag.
|
||||
* Use tag returning methods of appropriate subclasses of \class File instead.
|
||||
* \warning Do not cast it to any subclasses of Tag.
|
||||
* Use tag returning methods of appropriate subclasses of File instead.
|
||||
*
|
||||
* \see File::tag()
|
||||
*/
|
||||
Tag *tag() const;
|
||||
|
||||
/*!
|
||||
* Exports the tags of the file as dictionary mapping (human readable) tag
|
||||
* names (uppercase Strings) to StringLists of tag values. Calls this
|
||||
* method on the wrapped File instance.
|
||||
* For each metadata object of the file that could not be parsed into the PropertyMap
|
||||
* format, the returned map's unsupportedData() list will contain one entry identifying
|
||||
* that object (e.g. the frame type for ID3v2 tags). Use removeUnsupportedProperties()
|
||||
* to remove (a subset of) them.
|
||||
* For files that contain more than one tag (e.g. an MP3 with both an ID3v1 and an ID3v2
|
||||
* tag) only the most "modern" one will be exported (ID3v2 in this case).
|
||||
*/
|
||||
PropertyMap properties() const;
|
||||
|
||||
/*!
|
||||
* Removes unsupported properties, or a subset of them, from the file's metadata.
|
||||
* The parameter \a properties must contain only entries from
|
||||
* properties().unsupportedData().
|
||||
*/
|
||||
void removeUnsupportedProperties(const StringList& properties);
|
||||
|
||||
/*!
|
||||
* Sets the tags of the wrapped File to those specified in \a properties.
|
||||
* If some value(s) could not be written to the specific metadata format,
|
||||
* the returned PropertyMap will contain those value(s). Otherwise it will be empty,
|
||||
* indicating that no problems occurred.
|
||||
* With file types that support several tag formats (for instance, MP3 files can have
|
||||
* ID3v1, ID3v2, and APEv2 tags), this function will create the most appropriate one
|
||||
* (ID3v2 for MP3 files). Older formats will be updated as well, if they exist, but won't
|
||||
* be taken into account for the return value of this function.
|
||||
* See the documentation of the subclass implementations for detailed descriptions.
|
||||
*/
|
||||
PropertyMap setProperties(const PropertyMap &properties);
|
||||
|
||||
/*!
|
||||
* Get the keys of complex properties, i.e. properties which cannot be
|
||||
* represented simply by a string.
|
||||
* Because such properties might be expensive to fetch, there are separate
|
||||
* operations to get the available keys - which is expected to be cheap -
|
||||
* and getting and setting the property values.
|
||||
* Calls the method on the wrapped File, which collects the keys from one
|
||||
* or more of its tags.
|
||||
*/
|
||||
StringList complexPropertyKeys() const;
|
||||
|
||||
/*!
|
||||
* Get the complex properties for a given \a key.
|
||||
* In order to be flexible for different metadata formats, the properties
|
||||
* are represented as variant maps. Despite this dynamic nature, some
|
||||
* degree of standardization should be achieved between formats:
|
||||
*
|
||||
* - PICTURE
|
||||
* - data: ByteVector with picture data
|
||||
* - description: String with description
|
||||
* - pictureType: String with type as specified for ID3v2,
|
||||
* e.g. "Front Cover", "Back Cover", "Band"
|
||||
* - mimeType: String with image format, e.g. "image/jpeg"
|
||||
* - optionally more information found in the tag, such as
|
||||
* "width", "height", "numColors", "colorDepth" int values
|
||||
* in FLAC pictures
|
||||
* - GENERALOBJECT
|
||||
* - data: ByteVector with object data
|
||||
* - description: String with description
|
||||
* - fileName: String with file name
|
||||
* - mimeType: String with MIME type
|
||||
* - this is currently only implemented for ID3v2 GEOB frames
|
||||
*
|
||||
* Calls the method on the wrapped File, which gets the properties from one
|
||||
* or more of its tags.
|
||||
*/
|
||||
List<VariantMap> complexProperties(const String &key) const;
|
||||
|
||||
/*!
|
||||
* Set all complex properties for a given \a key using variant maps as
|
||||
* \a value with the same format as returned by complexProperties().
|
||||
* An empty list as \a value removes all complex properties for \a key.
|
||||
*/
|
||||
bool setComplexProperties(const String &key, const List<VariantMap> &value);
|
||||
|
||||
/*!
|
||||
* Returns the audio properties for this FileRef. If no audio properties
|
||||
* were read then this will returns a null pointer.
|
||||
@@ -198,6 +327,11 @@ namespace TagLib {
|
||||
*/
|
||||
static const FileTypeResolver *addFileTypeResolver(const FileTypeResolver *resolver);
|
||||
|
||||
/*!
|
||||
* Remove all resolvers added by addFileTypeResolver().
|
||||
*/
|
||||
static void clearFileTypeResolvers();
|
||||
|
||||
/*!
|
||||
* As is mentioned elsewhere in this class's documentation, the default file
|
||||
* type resolution code provided by TagLib only works by comparing file
|
||||
@@ -226,6 +360,11 @@ namespace TagLib {
|
||||
*/
|
||||
FileRef &operator=(const FileRef &ref);
|
||||
|
||||
/*!
|
||||
* Exchanges the content of the FileRef by the content of \a ref.
|
||||
*/
|
||||
void swap(FileRef &ref) noexcept;
|
||||
|
||||
/*!
|
||||
* Returns true if this FileRef and \a ref point to the same File object.
|
||||
*/
|
||||
@@ -237,25 +376,13 @@ 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;
|
||||
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
|
||||
std::shared_ptr<FileRefPrivate> d;
|
||||
};
|
||||
|
||||
} // namespace TagLib
|
||||
|
||||
@@ -23,20 +23,19 @@
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#include <tbytevector.h>
|
||||
#include <tstring.h>
|
||||
#include <tlist.h>
|
||||
#include <tdebug.h>
|
||||
#include <tagunion.h>
|
||||
#include <tpropertymap.h>
|
||||
|
||||
#include <id3v2header.h>
|
||||
#include <id3v2tag.h>
|
||||
#include <id3v1tag.h>
|
||||
#include <xiphcomment.h>
|
||||
|
||||
#include "flacpicture.h"
|
||||
#include "flacfile.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <utility>
|
||||
|
||||
#include "tdebug.h"
|
||||
#include "tpropertymap.h"
|
||||
#include "tagunion.h"
|
||||
#include "tagutils.h"
|
||||
#include "id3v2tag.h"
|
||||
#include "id3v1tag.h"
|
||||
#include "xiphcomment.h"
|
||||
#include "flacpicture.h"
|
||||
#include "flacmetadatablock.h"
|
||||
#include "flacunknownmetadatablock.h"
|
||||
|
||||
@@ -45,65 +44,61 @@ using namespace TagLib;
|
||||
namespace
|
||||
{
|
||||
enum { FlacXiphIndex = 0, FlacID3v2Index = 1, FlacID3v1Index = 2 };
|
||||
enum { MinPaddingLength = 4096 };
|
||||
enum { LastBlockFlag = 0x80 };
|
||||
}
|
||||
|
||||
const long MinPaddingLength = 4096;
|
||||
const long MaxPaddingLegnth = 1024 * 1024;
|
||||
|
||||
const char LastBlockFlag = '\x80';
|
||||
} // namespace
|
||||
|
||||
class FLAC::File::FilePrivate
|
||||
{
|
||||
public:
|
||||
FilePrivate() :
|
||||
ID3v2FrameFactory(ID3v2::FrameFactory::instance()),
|
||||
ID3v2Location(-1),
|
||||
ID3v2OriginalSize(0),
|
||||
ID3v1Location(-1),
|
||||
properties(0),
|
||||
flacStart(0),
|
||||
streamStart(0),
|
||||
scanned(false),
|
||||
hasXiphComment(false),
|
||||
hasID3v2(false),
|
||||
hasID3v1(false)
|
||||
FilePrivate(const ID3v2::FrameFactory *frameFactory = ID3v2::FrameFactory::instance()) :
|
||||
ID3v2FrameFactory(frameFactory)
|
||||
{
|
||||
}
|
||||
|
||||
~FilePrivate()
|
||||
{
|
||||
uint size = blocks.size();
|
||||
for(uint i = 0; i < size; i++) {
|
||||
delete blocks[i];
|
||||
}
|
||||
delete properties;
|
||||
blocks.setAutoDelete(true);
|
||||
}
|
||||
|
||||
const ID3v2::FrameFactory *ID3v2FrameFactory;
|
||||
long ID3v2Location;
|
||||
uint ID3v2OriginalSize;
|
||||
offset_t ID3v2Location { -1 };
|
||||
long ID3v2OriginalSize { 0 };
|
||||
|
||||
long ID3v1Location;
|
||||
offset_t ID3v1Location { -1 };
|
||||
|
||||
TagUnion tag;
|
||||
|
||||
Properties *properties;
|
||||
std::unique_ptr<Properties> properties;
|
||||
ByteVector xiphCommentData;
|
||||
List<MetadataBlock *> blocks;
|
||||
List<FLAC::MetadataBlock *> blocks;
|
||||
|
||||
long flacStart;
|
||||
long streamStart;
|
||||
bool scanned;
|
||||
|
||||
bool hasXiphComment;
|
||||
bool hasID3v2;
|
||||
bool hasID3v1;
|
||||
offset_t flacStart { 0 };
|
||||
offset_t streamStart { 0 };
|
||||
bool scanned { false };
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// 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") >= 0);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// public members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
FLAC::File::File(FileName file, bool readProperties, Properties::ReadStyle) :
|
||||
FLAC::File::File(FileName file, bool readProperties,
|
||||
Properties::ReadStyle,
|
||||
ID3v2::FrameFactory *frameFactory) :
|
||||
TagLib::File(file),
|
||||
d(new FilePrivate())
|
||||
d(std::make_unique<FilePrivate>(
|
||||
frameFactory ? frameFactory : ID3v2::FrameFactory::instance()))
|
||||
{
|
||||
if(isOpen())
|
||||
read(readProperties);
|
||||
@@ -112,9 +107,19 @@ FLAC::File::File(FileName file, bool readProperties, Properties::ReadStyle) :
|
||||
FLAC::File::File(FileName file, ID3v2::FrameFactory *frameFactory,
|
||||
bool readProperties, Properties::ReadStyle) :
|
||||
TagLib::File(file),
|
||||
d(new FilePrivate())
|
||||
d(std::make_unique<FilePrivate>(frameFactory))
|
||||
{
|
||||
if(isOpen())
|
||||
read(readProperties);
|
||||
}
|
||||
|
||||
FLAC::File::File(IOStream *stream, bool readProperties,
|
||||
Properties::ReadStyle,
|
||||
ID3v2::FrameFactory *frameFactory) :
|
||||
TagLib::File(stream),
|
||||
d(std::make_unique<FilePrivate>(
|
||||
frameFactory ? frameFactory : ID3v2::FrameFactory::instance()))
|
||||
{
|
||||
d->ID3v2FrameFactory = frameFactory;
|
||||
if(isOpen())
|
||||
read(readProperties);
|
||||
}
|
||||
@@ -122,17 +127,13 @@ FLAC::File::File(FileName file, ID3v2::FrameFactory *frameFactory,
|
||||
FLAC::File::File(IOStream *stream, ID3v2::FrameFactory *frameFactory,
|
||||
bool readProperties, Properties::ReadStyle) :
|
||||
TagLib::File(stream),
|
||||
d(new FilePrivate())
|
||||
d(std::make_unique<FilePrivate>(frameFactory))
|
||||
{
|
||||
d->ID3v2FrameFactory = frameFactory;
|
||||
if(isOpen())
|
||||
read(readProperties);
|
||||
}
|
||||
|
||||
FLAC::File::~File()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
FLAC::File::~File() = default;
|
||||
|
||||
TagLib::Tag *FLAC::File::tag() const
|
||||
{
|
||||
@@ -141,35 +142,87 @@ TagLib::Tag *FLAC::File::tag() const
|
||||
|
||||
PropertyMap FLAC::File::properties() const
|
||||
{
|
||||
// once Tag::properties() is virtual, this case distinction could actually be done
|
||||
// within TagUnion.
|
||||
if(d->hasXiphComment)
|
||||
return d->tag.access<Ogg::XiphComment>(FlacXiphIndex, false)->properties();
|
||||
if(d->hasID3v2)
|
||||
return d->tag.access<ID3v2::Tag>(FlacID3v2Index, false)->properties();
|
||||
if(d->hasID3v1)
|
||||
return d->tag.access<ID3v1::Tag>(FlacID3v1Index, false)->properties();
|
||||
return PropertyMap();
|
||||
return d->tag.properties();
|
||||
}
|
||||
|
||||
void FLAC::File::removeUnsupportedProperties(const StringList &unsupported)
|
||||
{
|
||||
if(d->hasXiphComment)
|
||||
d->tag.access<Ogg::XiphComment>(FlacXiphIndex, false)->removeUnsupportedProperties(unsupported);
|
||||
if(d->hasID3v2)
|
||||
d->tag.access<ID3v2::Tag>(FlacID3v2Index, false)->removeUnsupportedProperties(unsupported);
|
||||
if(d->hasID3v1)
|
||||
d->tag.access<ID3v1::Tag>(FlacID3v1Index, false)->removeUnsupportedProperties(unsupported);
|
||||
d->tag.removeUnsupportedProperties(unsupported);
|
||||
}
|
||||
|
||||
PropertyMap FLAC::File::setProperties(const PropertyMap &properties)
|
||||
{
|
||||
return d->tag.access<Ogg::XiphComment>(FlacXiphIndex, true)->setProperties(properties);
|
||||
return xiphComment(true)->setProperties(properties);
|
||||
}
|
||||
|
||||
StringList FLAC::File::complexPropertyKeys() const
|
||||
{
|
||||
StringList keys = TagLib::File::complexPropertyKeys();
|
||||
if(!keys.contains("PICTURE")) {
|
||||
if(std::any_of(d->blocks.cbegin(), d->blocks.cend(),
|
||||
[](MetadataBlock *block) {
|
||||
return dynamic_cast<Picture *>(block) != nullptr;
|
||||
})) {
|
||||
keys.append("PICTURE");
|
||||
}
|
||||
}
|
||||
return keys;
|
||||
}
|
||||
|
||||
List<VariantMap> FLAC::File::complexProperties(const String &key) const
|
||||
{
|
||||
const String uppercaseKey = key.upper();
|
||||
if(uppercaseKey == "PICTURE") {
|
||||
List<VariantMap> props;
|
||||
for(const auto &block : std::as_const(d->blocks)) {
|
||||
if(auto picture = dynamic_cast<Picture *>(block)) {
|
||||
VariantMap property;
|
||||
property.insert("data", picture->data());
|
||||
property.insert("mimeType", picture->mimeType());
|
||||
property.insert("description", picture->description());
|
||||
property.insert("pictureType",
|
||||
FLAC::Picture::typeToString(picture->type()));
|
||||
property.insert("width", picture->width());
|
||||
property.insert("height", picture->height());
|
||||
property.insert("numColors", picture->numColors());
|
||||
property.insert("colorDepth", picture->colorDepth());
|
||||
props.append(property);
|
||||
}
|
||||
}
|
||||
return props;
|
||||
}
|
||||
return TagLib::File::complexProperties(key);
|
||||
}
|
||||
|
||||
bool FLAC::File::setComplexProperties(const String &key, const List<VariantMap> &value)
|
||||
{
|
||||
const String uppercaseKey = key.upper();
|
||||
if(uppercaseKey == "PICTURE") {
|
||||
removePictures();
|
||||
|
||||
for(const auto &property : value) {
|
||||
auto picture = new FLAC::Picture;
|
||||
picture->setData(property.value("data").value<ByteVector>());
|
||||
picture->setMimeType(property.value("mimeType").value<String>());
|
||||
picture->setDescription(property.value("description").value<String>());
|
||||
picture->setType(FLAC::Picture::typeFromString(
|
||||
property.value("pictureType").value<String>()));
|
||||
picture->setWidth(property.value("width").value<int>());
|
||||
picture->setHeight(property.value("height").value<int>());
|
||||
picture->setNumColors(property.value("numColors").value<int>());
|
||||
picture->setColorDepth(property.value("colorDepth").value<int>());
|
||||
addPicture(picture);
|
||||
}
|
||||
}
|
||||
else {
|
||||
return TagLib::File::setComplexProperties(key, value);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
FLAC::Properties *FLAC::File::audioProperties() const
|
||||
{
|
||||
return d->properties;
|
||||
return d->properties.get();
|
||||
}
|
||||
|
||||
bool FLAC::File::save()
|
||||
@@ -185,40 +238,36 @@ 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);
|
||||
|
||||
// Replace metadata blocks
|
||||
|
||||
bool foundVorbisCommentBlock = false;
|
||||
List<MetadataBlock *> newBlocks;
|
||||
for(uint i = 0; i < d->blocks.size(); i++) {
|
||||
MetadataBlock *block = d->blocks[i];
|
||||
if(block->code() == MetadataBlock::VorbisComment) {
|
||||
// Set the new Vorbis Comment block
|
||||
delete block;
|
||||
block = new UnknownMetadataBlock(MetadataBlock::VorbisComment, d->xiphCommentData);
|
||||
foundVorbisCommentBlock = true;
|
||||
}
|
||||
if(block->code() == MetadataBlock::Padding) {
|
||||
delete block;
|
||||
MetadataBlock *commentBlock =
|
||||
new UnknownMetadataBlock(MetadataBlock::VorbisComment, d->xiphCommentData);
|
||||
for(auto it = d->blocks.begin(); it != d->blocks.end();) {
|
||||
if((*it)->code() == MetadataBlock::VorbisComment) {
|
||||
// Remove the old Vorbis Comment block
|
||||
delete *it;
|
||||
it = d->blocks.erase(it);
|
||||
continue;
|
||||
}
|
||||
newBlocks.append(block);
|
||||
if(commentBlock && (*it)->code() == MetadataBlock::Picture) {
|
||||
// Set the new Vorbis Comment block before the first picture block
|
||||
d->blocks.insert(it, commentBlock);
|
||||
commentBlock = nullptr;
|
||||
}
|
||||
++it;
|
||||
}
|
||||
if(!foundVorbisCommentBlock) {
|
||||
newBlocks.append(new UnknownMetadataBlock(MetadataBlock::VorbisComment, d->xiphCommentData));
|
||||
foundVorbisCommentBlock = true;
|
||||
}
|
||||
d->blocks = newBlocks;
|
||||
if(commentBlock)
|
||||
d->blocks.append(commentBlock);
|
||||
|
||||
// Render data for the metadata blocks
|
||||
|
||||
ByteVector data;
|
||||
for(uint i = 0; i < newBlocks.size(); i++) {
|
||||
FLAC::MetadataBlock *block = newBlocks[i];
|
||||
for(const auto &block : std::as_const(d->blocks)) {
|
||||
ByteVector blockData = block->render();
|
||||
ByteVector blockHeader = ByteVector::fromUInt(blockData.size());
|
||||
blockHeader[0] = block->code();
|
||||
@@ -226,39 +275,82 @@ bool FLAC::File::save()
|
||||
data.append(blockData);
|
||||
}
|
||||
|
||||
// Adjust the padding block(s)
|
||||
// Compute the amount of padding, and append that to data.
|
||||
|
||||
offset_t originalLength = d->streamStart - d->flacStart;
|
||||
offset_t paddingLength = originalLength - data.size() - 4;
|
||||
|
||||
long originalLength = d->streamStart - d->flacStart;
|
||||
int paddingLength = originalLength - data.size() - 4;
|
||||
if(paddingLength <= 0) {
|
||||
paddingLength = MinPaddingLength;
|
||||
}
|
||||
ByteVector padding = ByteVector::fromUInt(paddingLength);
|
||||
padding.resize(paddingLength + 4);
|
||||
padding[0] = (char)(FLAC::MetadataBlock::Padding | LastBlockFlag);
|
||||
data.append(padding);
|
||||
else {
|
||||
// Padding won't increase beyond 1% of the file size or 1MB.
|
||||
|
||||
offset_t threshold = length() / 100;
|
||||
threshold = std::max<offset_t>(threshold, MinPaddingLength);
|
||||
threshold = std::min<offset_t>(threshold, MaxPaddingLegnth);
|
||||
|
||||
if(paddingLength > threshold)
|
||||
paddingLength = MinPaddingLength;
|
||||
}
|
||||
|
||||
ByteVector paddingHeader = ByteVector::fromUInt(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));
|
||||
|
||||
// Write the data to the file
|
||||
|
||||
insert(data, d->flacStart, originalLength);
|
||||
d->hasXiphComment = true;
|
||||
|
||||
d->streamStart += (static_cast<long>(data.size()) - originalLength);
|
||||
|
||||
if(d->ID3v1Location >= 0)
|
||||
d->ID3v1Location += (static_cast<long>(data.size()) - originalLength);
|
||||
|
||||
// Update ID3 tags
|
||||
|
||||
if(ID3v2Tag()) {
|
||||
if(d->hasID3v2) {
|
||||
if(d->ID3v2Location < d->flacStart)
|
||||
debug("FLAC::File::save() -- This can't be right -- an ID3v2 tag after the "
|
||||
"start of the FLAC bytestream? Not writing the ID3v2 tag.");
|
||||
else
|
||||
insert(ID3v2Tag()->render(), d->ID3v2Location, d->ID3v2OriginalSize);
|
||||
if(ID3v2Tag() && !ID3v2Tag()->isEmpty()) {
|
||||
|
||||
// ID3v2 tag is not empty. Update the old one or create a new one.
|
||||
|
||||
if(d->ID3v2Location < 0)
|
||||
d->ID3v2Location = 0;
|
||||
|
||||
data = ID3v2Tag()->render();
|
||||
insert(data, d->ID3v2Location, d->ID3v2OriginalSize);
|
||||
|
||||
d->flacStart += (static_cast<long>(data.size()) - d->ID3v2OriginalSize);
|
||||
d->streamStart += (static_cast<long>(data.size()) - d->ID3v2OriginalSize);
|
||||
|
||||
if(d->ID3v1Location >= 0)
|
||||
d->ID3v1Location += (static_cast<long>(data.size()) - d->ID3v2OriginalSize);
|
||||
|
||||
d->ID3v2OriginalSize = data.size();
|
||||
}
|
||||
else {
|
||||
|
||||
// ID3v2 tag is empty. Remove the old one.
|
||||
|
||||
if(d->ID3v2Location >= 0) {
|
||||
removeBlock(d->ID3v2Location, d->ID3v2OriginalSize);
|
||||
|
||||
d->flacStart -= d->ID3v2OriginalSize;
|
||||
d->streamStart -= d->ID3v2OriginalSize;
|
||||
|
||||
if(d->ID3v1Location >= 0)
|
||||
d->ID3v1Location -= d->ID3v2OriginalSize;
|
||||
|
||||
d->ID3v2Location = -1;
|
||||
d->ID3v2OriginalSize = 0;
|
||||
}
|
||||
else
|
||||
insert(ID3v2Tag()->render(), 0, 0);
|
||||
}
|
||||
|
||||
if(ID3v1Tag()) {
|
||||
if(d->hasID3v1) {
|
||||
if(ID3v1Tag() && !ID3v1Tag()->isEmpty()) {
|
||||
|
||||
// ID3v1 tag is not empty. Update the old one or create a new one.
|
||||
|
||||
if(d->ID3v1Location >= 0) {
|
||||
seek(d->ID3v1Location);
|
||||
}
|
||||
else {
|
||||
@@ -267,7 +359,15 @@ bool FLAC::File::save()
|
||||
}
|
||||
|
||||
writeBlock(ID3v1Tag()->render());
|
||||
d->hasID3v1 = true;
|
||||
}
|
||||
else {
|
||||
|
||||
// ID3v1 tag is empty. Remove the old one.
|
||||
|
||||
if(d->ID3v1Location >= 0) {
|
||||
truncate(d->ID3v1Location);
|
||||
d->ID3v1Location = -1;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -275,11 +375,8 @@ 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,
|
||||
d->ID3v2FrameFactory);
|
||||
}
|
||||
|
||||
ID3v1::Tag *FLAC::File::ID3v1Tag(bool create)
|
||||
@@ -292,29 +389,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(uint i = 0; i < d->blocks.size(); i++) {
|
||||
Picture *picture = dynamic_cast<Picture *>(d->blocks[i]);
|
||||
if(picture) {
|
||||
for(const auto &block : std::as_const(d->blocks)) {
|
||||
if(auto picture = dynamic_cast<Picture *>(block)) {
|
||||
pictures.append(picture);
|
||||
}
|
||||
}
|
||||
@@ -328,8 +407,7 @@ void FLAC::File::addPicture(Picture *picture)
|
||||
|
||||
void FLAC::File::removePicture(Picture *picture, bool del)
|
||||
{
|
||||
MetadataBlock *block = picture;
|
||||
List<MetadataBlock *>::Iterator it = d->blocks.find(block);
|
||||
auto it = d->blocks.find(picture);
|
||||
if(it != d->blocks.end())
|
||||
d->blocks.erase(it);
|
||||
|
||||
@@ -339,32 +417,44 @@ void FLAC::File::removePicture(Picture *picture, bool del)
|
||||
|
||||
void FLAC::File::removePictures()
|
||||
{
|
||||
List<MetadataBlock *> newBlocks;
|
||||
for(uint i = 0; i < d->blocks.size(); i++) {
|
||||
Picture *picture = dynamic_cast<Picture *>(d->blocks[i]);
|
||||
if(picture) {
|
||||
delete picture;
|
||||
for(auto it = d->blocks.begin(); it != d->blocks.end(); ) {
|
||||
if(dynamic_cast<Picture *>(*it)) {
|
||||
delete *it;
|
||||
it = d->blocks.erase(it);
|
||||
}
|
||||
else {
|
||||
newBlocks.append(d->blocks[i]);
|
||||
++it;
|
||||
}
|
||||
}
|
||||
d->blocks = newBlocks;
|
||||
}
|
||||
|
||||
void FLAC::File::strip(int tags)
|
||||
{
|
||||
if(tags & ID3v1)
|
||||
d->tag.set(FlacID3v1Index, nullptr);
|
||||
|
||||
if(tags & ID3v2)
|
||||
d->tag.set(FlacID3v2Index, nullptr);
|
||||
|
||||
if(tags & XiphComment) {
|
||||
xiphComment()->removeAllFields();
|
||||
xiphComment()->removeAllPictures();
|
||||
}
|
||||
}
|
||||
|
||||
bool FLAC::File::hasXiphComment() const
|
||||
{
|
||||
return d->hasXiphComment;
|
||||
return !d->xiphCommentData.isEmpty();
|
||||
}
|
||||
|
||||
bool FLAC::File::hasID3v1Tag() const
|
||||
{
|
||||
return d->hasID3v1;
|
||||
return (d->ID3v1Location >= 0);
|
||||
}
|
||||
|
||||
bool FLAC::File::hasID3v2Tag() const
|
||||
{
|
||||
return d->hasID3v2;
|
||||
return (d->ID3v2Location >= 0);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -375,28 +465,19 @@ void FLAC::File::read(bool readProperties)
|
||||
{
|
||||
// Look for an ID3v2 tag
|
||||
|
||||
d->ID3v2Location = findID3v2();
|
||||
d->ID3v2Location = Utils::findID3v2(this);
|
||||
|
||||
if(d->ID3v2Location >= 0) {
|
||||
|
||||
d->tag.set(FlacID3v2Index, new ID3v2::Tag(this, d->ID3v2Location, d->ID3v2FrameFactory));
|
||||
|
||||
d->ID3v2OriginalSize = ID3v2Tag()->header()->completeTagSize();
|
||||
|
||||
if(ID3v2Tag()->header()->tagSize() <= 0)
|
||||
d->tag.set(FlacID3v2Index, 0);
|
||||
else
|
||||
d->hasID3v2 = true;
|
||||
}
|
||||
|
||||
// Look for an ID3v1 tag
|
||||
|
||||
d->ID3v1Location = findID3v1();
|
||||
d->ID3v1Location = Utils::findID3v1(this);
|
||||
|
||||
if(d->ID3v1Location >= 0) {
|
||||
if(d->ID3v1Location >= 0)
|
||||
d->tag.set(FlacID3v1Index, new ID3v1::Tag(this, d->ID3v1Location));
|
||||
d->hasID3v1 = true;
|
||||
}
|
||||
|
||||
// Look for FLAC metadata, including vorbis comments
|
||||
|
||||
@@ -405,10 +486,10 @@ void FLAC::File::read(bool readProperties)
|
||||
if(!isValid())
|
||||
return;
|
||||
|
||||
if(d->hasXiphComment)
|
||||
if(!d->xiphCommentData.isEmpty())
|
||||
d->tag.set(FlacXiphIndex, new Ogg::XiphComment(d->xiphCommentData));
|
||||
else
|
||||
d->tag.set(FlacXiphIndex, new Ogg::XiphComment);
|
||||
d->tag.set(FlacXiphIndex, new Ogg::XiphComment());
|
||||
|
||||
if(readProperties) {
|
||||
|
||||
@@ -416,14 +497,14 @@ void FLAC::File::read(bool readProperties)
|
||||
|
||||
const ByteVector infoData = d->blocks.front()->render();
|
||||
|
||||
long streamLength;
|
||||
offset_t streamLength;
|
||||
|
||||
if(d->hasID3v1)
|
||||
if(d->ID3v1Location >= 0)
|
||||
streamLength = d->ID3v1Location - d->streamStart;
|
||||
else
|
||||
streamLength = File::length() - d->streamStart;
|
||||
streamLength = length() - d->streamStart;
|
||||
|
||||
d->properties = new Properties(infoData, streamLength);
|
||||
d->properties = std::make_unique<Properties>(infoData, streamLength);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -437,9 +518,9 @@ void FLAC::File::scan()
|
||||
if(!isValid())
|
||||
return;
|
||||
|
||||
long nextBlockOffset;
|
||||
offset_t nextBlockOffset;
|
||||
|
||||
if(d->hasID3v2)
|
||||
if(d->ID3v2Location >= 0)
|
||||
nextBlockOffset = find("fLaC", d->ID3v2Location + d->ID3v2OriginalSize);
|
||||
else
|
||||
nextBlockOffset = find("fLaC");
|
||||
@@ -453,70 +534,69 @@ void FLAC::File::scan()
|
||||
nextBlockOffset += 4;
|
||||
d->flacStart = nextBlockOffset;
|
||||
|
||||
seek(nextBlockOffset);
|
||||
while(true) {
|
||||
|
||||
ByteVector header = readBlock(4);
|
||||
seek(nextBlockOffset);
|
||||
const ByteVector header = readBlock(4);
|
||||
if(header.size() != 4) {
|
||||
debug("FLAC::File::scan() -- Failed to read a block header");
|
||||
setValid(false);
|
||||
return;
|
||||
}
|
||||
|
||||
// Header format (from spec):
|
||||
// <1> Last-metadata-block flag
|
||||
// <7> BLOCK_TYPE
|
||||
// 0 : STREAMINFO
|
||||
// 1 : PADDING
|
||||
// ..
|
||||
// 4 : VORBIS_COMMENT
|
||||
// ..
|
||||
// <24> Length of metadata to follow
|
||||
// Header format (from spec):
|
||||
// <1> Last-metadata-block flag
|
||||
// <7> BLOCK_TYPE
|
||||
// 0 : STREAMINFO
|
||||
// 1 : PADDING
|
||||
// ..
|
||||
// 4 : VORBIS_COMMENT
|
||||
// ..
|
||||
// 6 : PICTURE
|
||||
// ..
|
||||
// <24> Length of metadata to follow
|
||||
|
||||
char blockType = header[0] & 0x7f;
|
||||
bool isLastBlock = (header[0] & 0x80) != 0;
|
||||
uint length = header.toUInt(1U, 3U);
|
||||
const char blockType = header[0] & ~LastBlockFlag;
|
||||
const bool isLastBlock = (header[0] & LastBlockFlag) != 0;
|
||||
const unsigned int blockLength = header.toUInt(1U, 3U);
|
||||
|
||||
// First block should be the stream_info metadata
|
||||
// First block should be the stream_info metadata
|
||||
|
||||
if(blockType != MetadataBlock::StreamInfo) {
|
||||
debug("FLAC::File::scan() -- invalid FLAC stream");
|
||||
setValid(false);
|
||||
return;
|
||||
}
|
||||
if(d->blocks.isEmpty() && blockType != MetadataBlock::StreamInfo) {
|
||||
debug("FLAC::File::scan() -- First block should be the stream_info metadata");
|
||||
setValid(false);
|
||||
return;
|
||||
}
|
||||
|
||||
d->blocks.append(new UnknownMetadataBlock(blockType, readBlock(length)));
|
||||
nextBlockOffset += length + 4;
|
||||
|
||||
// Search through the remaining metadata
|
||||
while(!isLastBlock) {
|
||||
|
||||
header = readBlock(4);
|
||||
blockType = header[0] & 0x7f;
|
||||
isLastBlock = (header[0] & 0x80) != 0;
|
||||
length = header.toUInt(1U, 3U);
|
||||
|
||||
if(length == 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;
|
||||
}
|
||||
|
||||
const ByteVector data = readBlock(length);
|
||||
if(data.size() != length) {
|
||||
const ByteVector data = readBlock(blockLength);
|
||||
if(data.size() != blockLength) {
|
||||
debug("FLAC::File::scan() -- Failed to read a metadata block");
|
||||
setValid(false);
|
||||
return;
|
||||
}
|
||||
|
||||
MetadataBlock *block = 0;
|
||||
MetadataBlock *block = nullptr;
|
||||
|
||||
// Found the vorbis-comment
|
||||
if(blockType == MetadataBlock::VorbisComment) {
|
||||
if(!d->hasXiphComment) {
|
||||
if(d->xiphCommentData.isEmpty()) {
|
||||
d->xiphCommentData = data;
|
||||
d->hasXiphComment = true;
|
||||
block = new UnknownMetadataBlock(MetadataBlock::VorbisComment, data);
|
||||
}
|
||||
else {
|
||||
debug("FLAC::File::scan() -- multiple Vorbis Comment blocks found, using the first one");
|
||||
debug("FLAC::File::scan() -- multiple Vorbis Comment blocks found, discarding");
|
||||
}
|
||||
}
|
||||
else if(blockType == MetadataBlock::Picture) {
|
||||
FLAC::Picture *picture = new FLAC::Picture();
|
||||
auto picture = new FLAC::Picture();
|
||||
if(picture->parse(data)) {
|
||||
block = picture;
|
||||
}
|
||||
@@ -525,25 +605,20 @@ void FLAC::File::scan()
|
||||
delete picture;
|
||||
}
|
||||
}
|
||||
|
||||
if(!block) {
|
||||
block = new UnknownMetadataBlock(blockType, data);
|
||||
}
|
||||
if(block->code() != MetadataBlock::Padding) {
|
||||
d->blocks.append(block);
|
||||
else if(blockType == MetadataBlock::Padding) {
|
||||
// Skip all padding blocks.
|
||||
}
|
||||
else {
|
||||
delete block;
|
||||
block = new UnknownMetadataBlock(blockType, data);
|
||||
}
|
||||
|
||||
nextBlockOffset += length + 4;
|
||||
if(block)
|
||||
d->blocks.append(block);
|
||||
|
||||
if(nextBlockOffset >= File::length()) {
|
||||
debug("FLAC::File::scan() -- FLAC stream corrupted");
|
||||
setValid(false);
|
||||
return;
|
||||
}
|
||||
seek(nextBlockOffset);
|
||||
nextBlockOffset += blockLength + 4;
|
||||
|
||||
if(isLastBlock)
|
||||
break;
|
||||
}
|
||||
|
||||
// End of metadata, now comes the datastream
|
||||
@@ -552,30 +627,3 @@ void FLAC::File::scan()
|
||||
|
||||
d->scanned = true;
|
||||
}
|
||||
|
||||
long FLAC::File::findID3v1()
|
||||
{
|
||||
if(!isValid())
|
||||
return -1;
|
||||
|
||||
seek(-128, End);
|
||||
long p = tell();
|
||||
|
||||
if(readBlock(3) == ID3v1::Tag::fileIdentifier())
|
||||
return p;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
long FLAC::File::findID3v2()
|
||||
{
|
||||
if(!isValid())
|
||||
return -1;
|
||||
|
||||
seek(0);
|
||||
|
||||
if(readBlock(3) == ID3v2::Header::fileIdentifier())
|
||||
return 0;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -26,11 +26,10 @@
|
||||
#ifndef TAGLIB_FLACFILE_H
|
||||
#define TAGLIB_FLACFILE_H
|
||||
|
||||
#include "taglib_export.h"
|
||||
#include "tfile.h"
|
||||
#include "tlist.h"
|
||||
#include "taglib_export.h"
|
||||
#include "tag.h"
|
||||
|
||||
#include "flacpicture.h"
|
||||
#include "flacproperties.h"
|
||||
|
||||
@@ -66,28 +65,46 @@ namespace TagLib {
|
||||
class TAGLIB_EXPORT File : public TagLib::File
|
||||
{
|
||||
public:
|
||||
/*!
|
||||
* This set of flags is used for various operations and is suitable for
|
||||
* being OR-ed together.
|
||||
*/
|
||||
enum TagTypes {
|
||||
//! Empty set. Matches no tag types.
|
||||
NoTags = 0x0000,
|
||||
//! Matches Vorbis comments.
|
||||
XiphComment = 0x0001,
|
||||
//! Matches ID3v1 tags.
|
||||
ID3v1 = 0x0002,
|
||||
//! Matches ID3v2 tags.
|
||||
ID3v2 = 0x0004,
|
||||
//! Matches all tag types.
|
||||
AllTags = 0xffff
|
||||
};
|
||||
|
||||
/*!
|
||||
* Constructs a FLAC file from \a file. If \a readProperties is true the
|
||||
* file's audio properties will also be read.
|
||||
*
|
||||
* \note In the current implementation, \a propertiesStyle is ignored.
|
||||
*
|
||||
* \deprecated This constructor will be dropped in favor of the one below
|
||||
* in a future version.
|
||||
* If this file contains an ID3v2 tag, the frames will be created using
|
||||
* \a frameFactory (default if null).
|
||||
*/
|
||||
File(FileName file, bool readProperties = true,
|
||||
Properties::ReadStyle propertiesStyle = Properties::Average);
|
||||
Properties::ReadStyle propertiesStyle = Properties::Average,
|
||||
ID3v2::FrameFactory *frameFactory = nullptr);
|
||||
|
||||
/*!
|
||||
* Constructs an FLAC file from \a file. If \a readProperties is true the
|
||||
* file's audio properties will also be read.
|
||||
*
|
||||
* If this file contains and ID3v2 tag the frames will be created using
|
||||
* If this file contains an ID3v2 tag, the frames will be created using
|
||||
* \a frameFactory.
|
||||
*
|
||||
* \note In the current implementation, \a propertiesStyle is ignored.
|
||||
*/
|
||||
// BIC: merge with the above constructor
|
||||
TAGLIB_DEPRECATED
|
||||
File(FileName file, ID3v2::FrameFactory *frameFactory,
|
||||
bool readProperties = true,
|
||||
Properties::ReadStyle propertiesStyle = Properties::Average);
|
||||
@@ -99,12 +116,28 @@ namespace TagLib {
|
||||
* \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
|
||||
* If this file contains an ID3v2 tag, the frames will be created using
|
||||
* \a frameFactory (default if null).
|
||||
*
|
||||
* \note In the current implementation, \a propertiesStyle is ignored.
|
||||
*/
|
||||
File(IOStream *stream, bool readProperties = true,
|
||||
Properties::ReadStyle propertiesStyle = Properties::Average,
|
||||
ID3v2::FrameFactory *frameFactory = nullptr);
|
||||
|
||||
/*!
|
||||
* Constructs a FLAC file from \a stream. If \a readProperties is true the
|
||||
* file's audio properties will also be read.
|
||||
*
|
||||
* \note TagLib will *not* take ownership of the stream, the caller is
|
||||
* responsible for deleting it after the File object.
|
||||
*
|
||||
* If this file contains an ID3v2 tag, the frames will be created using
|
||||
* \a frameFactory.
|
||||
*
|
||||
* \note In the current implementation, \a propertiesStyle is ignored.
|
||||
*/
|
||||
// BIC: merge with the above constructor
|
||||
TAGLIB_DEPRECATED
|
||||
File(IOStream *stream, ID3v2::FrameFactory *frameFactory,
|
||||
bool readProperties = true,
|
||||
Properties::ReadStyle propertiesStyle = Properties::Average);
|
||||
@@ -112,7 +145,10 @@ namespace TagLib {
|
||||
/*!
|
||||
* Destroys this instance of the File.
|
||||
*/
|
||||
virtual ~File();
|
||||
~File() override;
|
||||
|
||||
File(const File &) = delete;
|
||||
File &operator=(const File &) = delete;
|
||||
|
||||
/*!
|
||||
* Returns the Tag for this file. This will be a union of XiphComment,
|
||||
@@ -122,7 +158,7 @@ namespace TagLib {
|
||||
* \see ID3v1Tag()
|
||||
* \see XiphComment()
|
||||
*/
|
||||
virtual TagLib::Tag *tag() const;
|
||||
TagLib::Tag *tag() const override;
|
||||
|
||||
/*!
|
||||
* Implements the unified property interface -- export function.
|
||||
@@ -130,9 +166,9 @@ namespace TagLib {
|
||||
* only the first one (in the order XiphComment, ID3v2, ID3v1) will be
|
||||
* converted to the PropertyMap.
|
||||
*/
|
||||
PropertyMap properties() const;
|
||||
PropertyMap properties() const override;
|
||||
|
||||
void removeUnsupportedProperties(const StringList &);
|
||||
void removeUnsupportedProperties(const StringList &) override;
|
||||
|
||||
/*!
|
||||
* Implements the unified property interface -- import function.
|
||||
@@ -141,13 +177,30 @@ namespace TagLib {
|
||||
* Ignores any changes to ID3v1 or ID3v2 comments since they are not allowed
|
||||
* in the FLAC specification.
|
||||
*/
|
||||
PropertyMap setProperties(const PropertyMap &);
|
||||
PropertyMap setProperties(const PropertyMap &) override;
|
||||
|
||||
/*!
|
||||
* Returns ["PICTURE"] if any picture is stored in METADATA_BLOCK_PICTURE.
|
||||
*/
|
||||
StringList complexPropertyKeys() const override;
|
||||
|
||||
/*!
|
||||
* Get the pictures stored in METADATA_BLOCK_PICTURE as complex properties
|
||||
* for \a key "PICTURE".
|
||||
*/
|
||||
List<VariantMap> complexProperties(const String &key) const override;
|
||||
|
||||
/*!
|
||||
* Set the complex properties \a value as pictures in METADATA_BLOCK_PICTURE
|
||||
* for \a key "PICTURE".
|
||||
*/
|
||||
bool setComplexProperties(const String &key, const List<VariantMap> &value) override;
|
||||
|
||||
/*!
|
||||
* Returns the FLAC::Properties for this file. If no audio properties
|
||||
* were read then this will return a null pointer.
|
||||
*/
|
||||
virtual Properties *audioProperties() const;
|
||||
Properties *audioProperties() const override;
|
||||
|
||||
/*!
|
||||
* Save the file. This will primarily save the XiphComment, but
|
||||
@@ -155,11 +208,8 @@ namespace TagLib {
|
||||
* has no XiphComment, one will be constructed from the ID3-tags.
|
||||
*
|
||||
* This returns true if the save was successful.
|
||||
*
|
||||
* \warning In the current implementation, it's dangerous to call save()
|
||||
* repeatedly. At worst it will corrupt the file.
|
||||
*/
|
||||
virtual bool save();
|
||||
bool save() override;
|
||||
|
||||
/*!
|
||||
* Returns a pointer to the ID3v2 tag of the file.
|
||||
@@ -218,32 +268,6 @@ 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.
|
||||
*/
|
||||
@@ -268,6 +292,21 @@ namespace TagLib {
|
||||
*/
|
||||
void addPicture(Picture *picture);
|
||||
|
||||
/*!
|
||||
* This will remove the tags that match the OR-ed together TagTypes from
|
||||
* the file. By default it removes all tags.
|
||||
*
|
||||
* \warning This will also invalidate pointers to the tags as their memory
|
||||
* will be freed.
|
||||
*
|
||||
* \note In order to make the removal permanent save() still needs to be
|
||||
* called.
|
||||
*
|
||||
* \note This won't remove the Vorbis comment block completely. The
|
||||
* vendor ID will be preserved.
|
||||
*/
|
||||
void strip(int tags = AllTags);
|
||||
|
||||
/*!
|
||||
* Returns whether or not the file on disk actually has a XiphComment.
|
||||
*
|
||||
@@ -289,19 +328,24 @@ namespace TagLib {
|
||||
*/
|
||||
bool hasID3v2Tag() const;
|
||||
|
||||
private:
|
||||
File(const File &);
|
||||
File &operator=(const File &);
|
||||
/*!
|
||||
* 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:
|
||||
void read(bool readProperties);
|
||||
void scan();
|
||||
long findID3v2();
|
||||
long findID3v1();
|
||||
|
||||
class FilePrivate;
|
||||
FilePrivate *d;
|
||||
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
|
||||
std::unique_ptr<FilePrivate> d;
|
||||
};
|
||||
}
|
||||
}
|
||||
} // namespace FLAC
|
||||
} // namespace TagLib
|
||||
|
||||
#endif
|
||||
|
||||
@@ -23,25 +23,14 @@
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#include <taglib.h>
|
||||
#include <tdebug.h>
|
||||
#include "flacmetadatablock.h"
|
||||
|
||||
using namespace TagLib;
|
||||
|
||||
class FLAC::MetadataBlock::MetadataBlockPrivate
|
||||
{
|
||||
public:
|
||||
MetadataBlockPrivate() {}
|
||||
|
||||
};
|
||||
|
||||
FLAC::MetadataBlock::MetadataBlock()
|
||||
{
|
||||
d = 0;
|
||||
}
|
||||
|
||||
FLAC::MetadataBlock::~MetadataBlock()
|
||||
{
|
||||
}
|
||||
FLAC::MetadataBlock::MetadataBlock() = default;
|
||||
|
||||
FLAC::MetadataBlock::~MetadataBlock() = default;
|
||||
|
||||
@@ -31,15 +31,16 @@
|
||||
#include "taglib_export.h"
|
||||
|
||||
namespace TagLib {
|
||||
|
||||
namespace FLAC {
|
||||
|
||||
class TAGLIB_EXPORT MetadataBlock
|
||||
{
|
||||
public:
|
||||
MetadataBlock();
|
||||
virtual ~MetadataBlock();
|
||||
|
||||
MetadataBlock(const MetadataBlock &item) = delete;
|
||||
MetadataBlock &operator=(const MetadataBlock &item) = delete;
|
||||
|
||||
enum BlockType {
|
||||
StreamInfo = 0,
|
||||
Padding,
|
||||
@@ -61,15 +62,10 @@ namespace TagLib {
|
||||
virtual ByteVector render() const = 0;
|
||||
|
||||
private:
|
||||
MetadataBlock(const MetadataBlock &item);
|
||||
MetadataBlock &operator=(const MetadataBlock &item);
|
||||
|
||||
class MetadataBlockPrivate;
|
||||
MetadataBlockPrivate *d;
|
||||
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
|
||||
std::unique_ptr<MetadataBlockPrivate> d;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} // namespace FLAC
|
||||
} // namespace TagLib
|
||||
#endif
|
||||
|
||||
@@ -23,48 +23,37 @@
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#include <taglib.h>
|
||||
#include <tdebug.h>
|
||||
#include "flacpicture.h"
|
||||
|
||||
#include "tdebug.h"
|
||||
|
||||
using namespace TagLib;
|
||||
|
||||
class FLAC::Picture::PicturePrivate
|
||||
{
|
||||
public:
|
||||
PicturePrivate() :
|
||||
type(FLAC::Picture::Other),
|
||||
width(0),
|
||||
height(0),
|
||||
colorDepth(0),
|
||||
numColors(0)
|
||||
{}
|
||||
|
||||
Type type;
|
||||
Type type { FLAC::Picture::Other };
|
||||
String mimeType;
|
||||
String description;
|
||||
int width;
|
||||
int height;
|
||||
int colorDepth;
|
||||
int numColors;
|
||||
int width { 0 };
|
||||
int height { 0 };
|
||||
int colorDepth { 0 };
|
||||
int numColors { 0 };
|
||||
ByteVector data;
|
||||
};
|
||||
|
||||
FLAC::Picture::Picture()
|
||||
FLAC::Picture::Picture() :
|
||||
d(std::make_unique<PicturePrivate>())
|
||||
{
|
||||
d = new PicturePrivate;
|
||||
}
|
||||
|
||||
FLAC::Picture::Picture(const ByteVector &data)
|
||||
FLAC::Picture::Picture(const ByteVector &data) :
|
||||
d(std::make_unique<PicturePrivate>())
|
||||
{
|
||||
d = new PicturePrivate;
|
||||
parse(data);
|
||||
}
|
||||
|
||||
FLAC::Picture::~Picture()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
FLAC::Picture::~Picture() = default;
|
||||
|
||||
int FLAC::Picture::code() const
|
||||
{
|
||||
@@ -78,10 +67,10 @@ bool FLAC::Picture::parse(const ByteVector &data)
|
||||
return false;
|
||||
}
|
||||
|
||||
uint pos = 0;
|
||||
d->type = FLAC::Picture::Type(data.toUInt(pos));
|
||||
unsigned int pos = 0;
|
||||
d->type = static_cast<FLAC::Picture::Type>(data.toUInt(pos));
|
||||
pos += 4;
|
||||
uint mimeTypeLength = data.toUInt(pos);
|
||||
unsigned int mimeTypeLength = data.toUInt(pos);
|
||||
pos += 4;
|
||||
if(pos + mimeTypeLength + 24 > data.size()) {
|
||||
debug("Invalid picture block.");
|
||||
@@ -89,7 +78,7 @@ bool FLAC::Picture::parse(const ByteVector &data)
|
||||
}
|
||||
d->mimeType = String(data.mid(pos, mimeTypeLength), String::UTF8);
|
||||
pos += mimeTypeLength;
|
||||
uint descriptionLength = data.toUInt(pos);
|
||||
unsigned int descriptionLength = data.toUInt(pos);
|
||||
pos += 4;
|
||||
if(pos + descriptionLength + 20 > data.size()) {
|
||||
debug("Invalid picture block.");
|
||||
@@ -105,7 +94,7 @@ bool FLAC::Picture::parse(const ByteVector &data)
|
||||
pos += 4;
|
||||
d->numColors = data.toUInt(pos);
|
||||
pos += 4;
|
||||
uint dataLength = data.toUInt(pos);
|
||||
unsigned int dataLength = data.toUInt(pos);
|
||||
pos += 4;
|
||||
if(pos + dataLength > data.size()) {
|
||||
debug("Invalid picture block.");
|
||||
@@ -214,4 +203,3 @@ void FLAC::Picture::setData(const ByteVector &data)
|
||||
{
|
||||
d->data = data;
|
||||
}
|
||||
|
||||
|
||||
@@ -29,13 +29,12 @@
|
||||
#include "tlist.h"
|
||||
#include "tstring.h"
|
||||
#include "tbytevector.h"
|
||||
#include "tpicturetype.h"
|
||||
#include "taglib_export.h"
|
||||
#include "flacmetadatablock.h"
|
||||
|
||||
namespace TagLib {
|
||||
|
||||
namespace FLAC {
|
||||
|
||||
class TAGLIB_EXPORT Picture : public MetadataBlock
|
||||
{
|
||||
public:
|
||||
@@ -43,54 +42,14 @@ namespace TagLib {
|
||||
/*!
|
||||
* This describes the function or content of the picture.
|
||||
*/
|
||||
enum Type {
|
||||
//! A type not enumerated below
|
||||
Other = 0x00,
|
||||
//! 32x32 PNG image that should be used as the file icon
|
||||
FileIcon = 0x01,
|
||||
//! File icon of a different size or format
|
||||
OtherFileIcon = 0x02,
|
||||
//! Front cover image of the album
|
||||
FrontCover = 0x03,
|
||||
//! Back cover image of the album
|
||||
BackCover = 0x04,
|
||||
//! Inside leaflet page of the album
|
||||
LeafletPage = 0x05,
|
||||
//! Image from the album itself
|
||||
Media = 0x06,
|
||||
//! Picture of the lead artist or soloist
|
||||
LeadArtist = 0x07,
|
||||
//! Picture of the artist or performer
|
||||
Artist = 0x08,
|
||||
//! Picture of the conductor
|
||||
Conductor = 0x09,
|
||||
//! Picture of the band or orchestra
|
||||
Band = 0x0A,
|
||||
//! Picture of the composer
|
||||
Composer = 0x0B,
|
||||
//! Picture of the lyricist or text writer
|
||||
Lyricist = 0x0C,
|
||||
//! Picture of the recording location or studio
|
||||
RecordingLocation = 0x0D,
|
||||
//! Picture of the artists during recording
|
||||
DuringRecording = 0x0E,
|
||||
//! Picture of the artists during performance
|
||||
DuringPerformance = 0x0F,
|
||||
//! Picture from a movie or video related to the track
|
||||
MovieScreenCapture = 0x10,
|
||||
//! Picture of a large, coloured fish
|
||||
ColouredFish = 0x11,
|
||||
//! Illustration related to the track
|
||||
Illustration = 0x12,
|
||||
//! Logo of the band or performer
|
||||
BandLogo = 0x13,
|
||||
//! Logo of the publisher (record company)
|
||||
PublisherLogo = 0x14
|
||||
};
|
||||
DECLARE_PICTURE_TYPE_ENUM(Type)
|
||||
|
||||
Picture();
|
||||
Picture(const ByteVector &data);
|
||||
~Picture();
|
||||
~Picture() override;
|
||||
|
||||
Picture(const Picture &item) = delete;
|
||||
Picture &operator=(const Picture &item) = delete;
|
||||
|
||||
/*!
|
||||
* Returns the type of the image.
|
||||
@@ -179,12 +138,12 @@ namespace TagLib {
|
||||
/*!
|
||||
* Returns the FLAC metadata block type.
|
||||
*/
|
||||
int code() const;
|
||||
int code() const override;
|
||||
|
||||
/*!
|
||||
* Render the content to the FLAC picture block format.
|
||||
*/
|
||||
ByteVector render() const;
|
||||
ByteVector render() const override;
|
||||
|
||||
/*!
|
||||
* Parse the picture data in the FLAC picture block format.
|
||||
@@ -192,17 +151,12 @@ namespace TagLib {
|
||||
bool parse(const ByteVector &rawData);
|
||||
|
||||
private:
|
||||
Picture(const Picture &item);
|
||||
Picture &operator=(const Picture &item);
|
||||
|
||||
class PicturePrivate;
|
||||
PicturePrivate *d;
|
||||
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
|
||||
std::unique_ptr<PicturePrivate> d;
|
||||
};
|
||||
|
||||
typedef List<Picture> PictureList;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
using PictureList = List<Picture>;
|
||||
} // namespace FLAC
|
||||
} // namespace TagLib
|
||||
#endif
|
||||
|
||||
@@ -23,10 +23,11 @@
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#include <tstring.h>
|
||||
#include <tdebug.h>
|
||||
|
||||
#include "flacproperties.h"
|
||||
|
||||
#include "tstring.h"
|
||||
#include "tdebug.h"
|
||||
|
||||
#include "flacfile.h"
|
||||
|
||||
using namespace TagLib;
|
||||
@@ -34,20 +35,12 @@ using namespace TagLib;
|
||||
class FLAC::Properties::PropertiesPrivate
|
||||
{
|
||||
public:
|
||||
PropertiesPrivate() :
|
||||
length(0),
|
||||
bitrate(0),
|
||||
sampleRate(0),
|
||||
bitsPerSample(0),
|
||||
channels(0),
|
||||
sampleFrames(0) {}
|
||||
|
||||
int length;
|
||||
int bitrate;
|
||||
int sampleRate;
|
||||
int bitsPerSample;
|
||||
int channels;
|
||||
unsigned long long sampleFrames;
|
||||
int length { 0 };
|
||||
int bitrate { 0 };
|
||||
int sampleRate { 0 };
|
||||
int bitsPerSample { 0 };
|
||||
int channels { 0 };
|
||||
unsigned long long sampleFrames { 0 };
|
||||
ByteVector signature;
|
||||
};
|
||||
|
||||
@@ -55,34 +48,14 @@ public:
|
||||
// public members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
FLAC::Properties::Properties(ByteVector data, long streamLength, ReadStyle style) :
|
||||
FLAC::Properties::Properties(const ByteVector &data, offset_t streamLength, ReadStyle style) :
|
||||
AudioProperties(style),
|
||||
d(new PropertiesPrivate())
|
||||
d(std::make_unique<PropertiesPrivate>())
|
||||
{
|
||||
read(data, streamLength);
|
||||
}
|
||||
|
||||
FLAC::Properties::Properties(File *, ReadStyle style) :
|
||||
AudioProperties(style),
|
||||
d(new PropertiesPrivate())
|
||||
{
|
||||
debug("FLAC::Properties::Properties() - This constructor is no longer used.");
|
||||
}
|
||||
|
||||
FLAC::Properties::~Properties()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
|
||||
int FLAC::Properties::length() const
|
||||
{
|
||||
return lengthInSeconds();
|
||||
}
|
||||
|
||||
int FLAC::Properties::lengthInSeconds() const
|
||||
{
|
||||
return d->length / 1000;
|
||||
}
|
||||
FLAC::Properties::~Properties() = default;
|
||||
|
||||
int FLAC::Properties::lengthInMilliseconds() const
|
||||
{
|
||||
@@ -104,11 +77,6 @@ int FLAC::Properties::bitsPerSample() const
|
||||
return d->bitsPerSample;
|
||||
}
|
||||
|
||||
int FLAC::Properties::sampleWidth() const
|
||||
{
|
||||
return bitsPerSample();
|
||||
}
|
||||
|
||||
int FLAC::Properties::channels() const
|
||||
{
|
||||
return d->channels;
|
||||
@@ -128,14 +96,14 @@ ByteVector FLAC::Properties::signature() const
|
||||
// private members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void FLAC::Properties::read(const ByteVector &data, long streamLength)
|
||||
void FLAC::Properties::read(const ByteVector &data, offset_t streamLength)
|
||||
{
|
||||
if(data.size() < 18) {
|
||||
debug("FLAC::Properties::read() - FLAC properties must contain at least 18 bytes.");
|
||||
return;
|
||||
}
|
||||
|
||||
uint pos = 0;
|
||||
unsigned int pos = 0;
|
||||
|
||||
// Minimum block size (in samples)
|
||||
pos += 2;
|
||||
@@ -149,7 +117,7 @@ void FLAC::Properties::read(const ByteVector &data, long streamLength)
|
||||
// Maximum frame size (in bytes)
|
||||
pos += 3;
|
||||
|
||||
const uint flags = data.toUInt(pos, true);
|
||||
const unsigned int flags = data.toUInt(pos, true);
|
||||
pos += 4;
|
||||
|
||||
d->sampleRate = flags >> 12;
|
||||
@@ -159,8 +127,8 @@ void FLAC::Properties::read(const ByteVector &data, long streamLength)
|
||||
// The last 4 bits are the most significant 4 bits for the 36 bit
|
||||
// stream length in samples. (Audio files measured in days)
|
||||
|
||||
const ulonglong hi = flags & 0xf;
|
||||
const ulonglong lo = data.toUInt(pos, true);
|
||||
const unsigned long long hi = flags & 0xf;
|
||||
const unsigned long long lo = data.toUInt(pos, true);
|
||||
pos += 4;
|
||||
|
||||
d->sampleFrames = (hi << 32) | lo;
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#ifndef TAGLIB_FLACPROPERTIES_H
|
||||
#define TAGLIB_FLACPROPERTIES_H
|
||||
|
||||
#include "tbytevector.h"
|
||||
#include "taglib_export.h"
|
||||
#include "audioproperties.h"
|
||||
|
||||
@@ -33,8 +34,6 @@ namespace TagLib {
|
||||
|
||||
namespace FLAC {
|
||||
|
||||
class File;
|
||||
|
||||
//! An implementation of audio property reading for FLAC
|
||||
|
||||
/*!
|
||||
@@ -49,62 +48,37 @@ namespace TagLib {
|
||||
* Create an instance of FLAC::Properties with the data read from the
|
||||
* ByteVector \a data.
|
||||
*/
|
||||
// BIC: switch to const reference
|
||||
Properties(ByteVector data, long streamLength, ReadStyle style = Average);
|
||||
|
||||
/*!
|
||||
* Create an instance of FLAC::Properties with the data read from the
|
||||
* FLAC::File \a file.
|
||||
*/
|
||||
// BIC: remove
|
||||
Properties(File *file, ReadStyle style = Average);
|
||||
Properties(const ByteVector &data, offset_t streamLength, ReadStyle style = Average);
|
||||
|
||||
/*!
|
||||
* Destroys this FLAC::Properties instance.
|
||||
*/
|
||||
virtual ~Properties();
|
||||
~Properties() override;
|
||||
|
||||
/*!
|
||||
* Returns the length of the file in seconds. The length is rounded down to
|
||||
* the nearest whole second.
|
||||
*
|
||||
* \note This method is just an alias of lengthInSeconds().
|
||||
*
|
||||
* \deprecated
|
||||
*/
|
||||
virtual int length() const;
|
||||
|
||||
/*!
|
||||
* Returns the length of the file in seconds. The length is rounded down to
|
||||
* the nearest whole second.
|
||||
*
|
||||
* \see lengthInMilliseconds()
|
||||
*/
|
||||
// BIC: make virtual
|
||||
int lengthInSeconds() const;
|
||||
Properties(const Properties &) = delete;
|
||||
Properties &operator=(const Properties &) = delete;
|
||||
|
||||
/*!
|
||||
* Returns the length of the file in milliseconds.
|
||||
*
|
||||
* \see lengthInSeconds()
|
||||
*/
|
||||
// BIC: make virtual
|
||||
int lengthInMilliseconds() const;
|
||||
int lengthInMilliseconds() const override;
|
||||
|
||||
/*!
|
||||
* Returns the average bit rate of the file in kb/s.
|
||||
*/
|
||||
virtual int bitrate() const;
|
||||
int bitrate() const override;
|
||||
|
||||
/*!
|
||||
* Returns the sample rate in Hz.
|
||||
*/
|
||||
virtual int sampleRate() const;
|
||||
int sampleRate() const override;
|
||||
|
||||
/*!
|
||||
* Returns the number of audio channels.
|
||||
*/
|
||||
virtual int channels() const;
|
||||
int channels() const override;
|
||||
|
||||
/*!
|
||||
* Returns the number of bits per audio sample as read from the FLAC
|
||||
@@ -112,16 +86,6 @@ namespace TagLib {
|
||||
*/
|
||||
int bitsPerSample() const;
|
||||
|
||||
/*!
|
||||
* Returns the sample width as read from the FLAC identification
|
||||
* header.
|
||||
*
|
||||
* \note This method is just an alias of bitsPerSample().
|
||||
*
|
||||
* \deprecated
|
||||
*/
|
||||
int sampleWidth() const;
|
||||
|
||||
/*!
|
||||
* Return the number of sample frames.
|
||||
*/
|
||||
@@ -134,15 +98,13 @@ namespace TagLib {
|
||||
ByteVector signature() const;
|
||||
|
||||
private:
|
||||
Properties(const Properties &);
|
||||
Properties &operator=(const Properties &);
|
||||
|
||||
void read(const ByteVector &data, long streamLength);
|
||||
void read(const ByteVector &data, offset_t streamLength);
|
||||
|
||||
class PropertiesPrivate;
|
||||
PropertiesPrivate *d;
|
||||
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
|
||||
std::unique_ptr<PropertiesPrivate> d;
|
||||
};
|
||||
}
|
||||
}
|
||||
} // namespace FLAC
|
||||
} // namespace TagLib
|
||||
|
||||
#endif
|
||||
|
||||
@@ -23,9 +23,6 @@
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#include <taglib.h>
|
||||
#include <tdebug.h>
|
||||
#include <tstring.h>
|
||||
#include "flacunknownmetadatablock.h"
|
||||
|
||||
using namespace TagLib;
|
||||
@@ -33,24 +30,18 @@ using namespace TagLib;
|
||||
class FLAC::UnknownMetadataBlock::UnknownMetadataBlockPrivate
|
||||
{
|
||||
public:
|
||||
UnknownMetadataBlockPrivate() : code(0) {}
|
||||
|
||||
int code;
|
||||
int code { 0 };
|
||||
ByteVector data;
|
||||
};
|
||||
|
||||
FLAC::UnknownMetadataBlock::UnknownMetadataBlock(int code, const ByteVector &data)
|
||||
FLAC::UnknownMetadataBlock::UnknownMetadataBlock(int code, const ByteVector &data) :
|
||||
d(std::make_unique<UnknownMetadataBlockPrivate>())
|
||||
{
|
||||
d = new UnknownMetadataBlockPrivate;
|
||||
d->code = code;
|
||||
//debug(String(data.toHex()));
|
||||
d->data = data;
|
||||
}
|
||||
|
||||
FLAC::UnknownMetadataBlock::~UnknownMetadataBlock()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
FLAC::UnknownMetadataBlock::~UnknownMetadataBlock() = default;
|
||||
|
||||
int FLAC::UnknownMetadataBlock::code() const
|
||||
{
|
||||
@@ -76,4 +67,3 @@ ByteVector FLAC::UnknownMetadataBlock::render() const
|
||||
{
|
||||
return d->data;
|
||||
}
|
||||
|
||||
|
||||
@@ -32,19 +32,20 @@
|
||||
#include "flacmetadatablock.h"
|
||||
|
||||
namespace TagLib {
|
||||
|
||||
namespace FLAC {
|
||||
|
||||
class TAGLIB_EXPORT UnknownMetadataBlock : public MetadataBlock
|
||||
{
|
||||
public:
|
||||
UnknownMetadataBlock(int blockType, const ByteVector &data);
|
||||
~UnknownMetadataBlock();
|
||||
UnknownMetadataBlock(int code, const ByteVector &data);
|
||||
~UnknownMetadataBlock() override;
|
||||
|
||||
UnknownMetadataBlock(const UnknownMetadataBlock &item) = delete;
|
||||
UnknownMetadataBlock &operator=(const UnknownMetadataBlock &item) = delete;
|
||||
|
||||
/*!
|
||||
* Returns the FLAC metadata block type.
|
||||
*/
|
||||
int code() const;
|
||||
int code() const override;
|
||||
|
||||
/*!
|
||||
* Sets the FLAC metadata block type.
|
||||
@@ -64,18 +65,13 @@ namespace TagLib {
|
||||
/*!
|
||||
* Render the content of the block.
|
||||
*/
|
||||
ByteVector render() const;
|
||||
ByteVector render() const override;
|
||||
|
||||
private:
|
||||
UnknownMetadataBlock(const MetadataBlock &item);
|
||||
UnknownMetadataBlock &operator=(const MetadataBlock &item);
|
||||
|
||||
class UnknownMetadataBlockPrivate;
|
||||
UnknownMetadataBlockPrivate *d;
|
||||
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
|
||||
std::unique_ptr<UnknownMetadataBlockPrivate> d;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} // namespace FLAC
|
||||
} // namespace TagLib
|
||||
#endif
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
/***************************************************************************
|
||||
* This library is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU Lesser General Public License version *
|
||||
* it under the terms of the GNU Lesser General Public License version *
|
||||
* 2.1 as published by the Free Software Foundation. *
|
||||
* *
|
||||
* This library is distributed in the hope that it will be useful, but *
|
||||
@@ -15,15 +15,20 @@
|
||||
* *
|
||||
* You should have received a copy of the GNU Lesser General Public *
|
||||
* License along with this library; if not, write to the Free Software *
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, *
|
||||
* MA 02110-1301 USA *
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
|
||||
* 02110-1301 USA *
|
||||
* *
|
||||
* Alternatively, this file is available under the Mozilla Public *
|
||||
* License Version 1.1. You may obtain a copy of the License at *
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#include "tstringlist.h"
|
||||
#include "itfile.h"
|
||||
|
||||
#include "tstringlist.h"
|
||||
#include "tdebug.h"
|
||||
#include "modfileprivate.h"
|
||||
#include "tpropertymap.h"
|
||||
#include "modfileprivate.h"
|
||||
|
||||
using namespace TagLib;
|
||||
using namespace IT;
|
||||
@@ -32,7 +37,7 @@ class IT::File::FilePrivate
|
||||
{
|
||||
public:
|
||||
FilePrivate(AudioProperties::ReadStyle propertiesStyle)
|
||||
: tag(), properties(propertiesStyle)
|
||||
: properties(propertiesStyle)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -43,7 +48,7 @@ public:
|
||||
IT::File::File(FileName file, bool readProperties,
|
||||
AudioProperties::ReadStyle propertiesStyle) :
|
||||
Mod::FileBase(file),
|
||||
d(new FilePrivate(propertiesStyle))
|
||||
d(std::make_unique<FilePrivate>(propertiesStyle))
|
||||
{
|
||||
if(isOpen())
|
||||
read(readProperties);
|
||||
@@ -52,32 +57,19 @@ IT::File::File(FileName file, bool readProperties,
|
||||
IT::File::File(IOStream *stream, bool readProperties,
|
||||
AudioProperties::ReadStyle propertiesStyle) :
|
||||
Mod::FileBase(stream),
|
||||
d(new FilePrivate(propertiesStyle))
|
||||
d(std::make_unique<FilePrivate>(propertiesStyle))
|
||||
{
|
||||
if(isOpen())
|
||||
read(readProperties);
|
||||
}
|
||||
|
||||
IT::File::~File()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
IT::File::~File() = default;
|
||||
|
||||
Mod::Tag *IT::File::tag() const
|
||||
{
|
||||
return &d->tag;
|
||||
}
|
||||
|
||||
PropertyMap IT::File::properties() const
|
||||
{
|
||||
return d->tag.properties();
|
||||
}
|
||||
|
||||
PropertyMap IT::File::setProperties(const PropertyMap &properties)
|
||||
{
|
||||
return d->tag.setProperties(properties);
|
||||
}
|
||||
|
||||
IT::Properties *IT::File::audioProperties() const
|
||||
{
|
||||
return &d->properties;
|
||||
@@ -96,9 +88,9 @@ bool IT::File::save()
|
||||
|
||||
seek(2, Current);
|
||||
|
||||
ushort length = 0;
|
||||
ushort instrumentCount = 0;
|
||||
ushort sampleCount = 0;
|
||||
unsigned short length = 0;
|
||||
unsigned short instrumentCount = 0;
|
||||
unsigned short sampleCount = 0;
|
||||
|
||||
if(!readU16L(length) || !readU16L(instrumentCount) || !readU16L(sampleCount))
|
||||
return false;
|
||||
@@ -107,9 +99,9 @@ bool IT::File::save()
|
||||
|
||||
// write comment as instrument and sample names:
|
||||
StringList lines = d->tag.comment().split("\n");
|
||||
for(ushort i = 0; i < instrumentCount; ++ i) {
|
||||
seek(192L + length + ((long)i << 2));
|
||||
ulong instrumentOffset = 0;
|
||||
for(unsigned short i = 0; i < instrumentCount; ++ i) {
|
||||
seek(192L + length + (static_cast<long>(i) << 2));
|
||||
unsigned long instrumentOffset = 0;
|
||||
if(!readU32L(instrumentOffset))
|
||||
return false;
|
||||
|
||||
@@ -118,28 +110,28 @@ bool IT::File::save()
|
||||
if(i < lines.size())
|
||||
writeString(lines[i], 25);
|
||||
else
|
||||
writeString(String::null, 25);
|
||||
writeString(String(), 25);
|
||||
writeByte(0);
|
||||
}
|
||||
|
||||
for(ushort i = 0; i < sampleCount; ++ i) {
|
||||
seek(192L + length + ((long)instrumentCount << 2) + ((long)i << 2));
|
||||
ulong sampleOffset = 0;
|
||||
for(unsigned short i = 0; i < sampleCount; ++ i) {
|
||||
seek(192L + length + (static_cast<long>(instrumentCount) << 2) + (static_cast<long>(i) << 2));
|
||||
unsigned long sampleOffset = 0;
|
||||
if(!readU32L(sampleOffset))
|
||||
return false;
|
||||
|
||||
seek(sampleOffset + 20);
|
||||
|
||||
if((TagLib::uint)(i + instrumentCount) < lines.size())
|
||||
if(static_cast<unsigned int>(i + instrumentCount) < lines.size())
|
||||
writeString(lines[i + instrumentCount], 25);
|
||||
else
|
||||
writeString(String::null, 25);
|
||||
writeString(String(), 25);
|
||||
writeByte(0);
|
||||
}
|
||||
|
||||
// write rest as message:
|
||||
StringList messageLines;
|
||||
for(uint i = instrumentCount + sampleCount; i < lines.size(); ++ i)
|
||||
for(unsigned int i = instrumentCount + sampleCount; i < lines.size(); ++ i)
|
||||
messageLines.append(lines[i]);
|
||||
ByteVector message = messageLines.toString("\r").data(String::Latin1);
|
||||
|
||||
@@ -147,17 +139,17 @@ bool IT::File::save()
|
||||
// terminating NUL but it does not hurt to add one:
|
||||
if(message.size() > 7999)
|
||||
message.resize(7999);
|
||||
message.append((char)0);
|
||||
message.append(static_cast<char>(0));
|
||||
|
||||
ushort special = 0;
|
||||
ushort messageLength = 0;
|
||||
ulong messageOffset = 0;
|
||||
unsigned short special = 0;
|
||||
unsigned short messageLength = 0;
|
||||
unsigned long messageOffset = 0;
|
||||
|
||||
seek(46);
|
||||
if(!readU16L(special))
|
||||
return false;
|
||||
|
||||
ulong fileSize = File::length();
|
||||
auto fileSize = static_cast<unsigned long>(File::length());
|
||||
if(special & Properties::MessageAttached) {
|
||||
seek(54);
|
||||
if(!readU16L(messageLength) || !readU32L(messageOffset))
|
||||
@@ -234,7 +226,7 @@ void IT::File::read(bool)
|
||||
seek(messageOffset);
|
||||
ByteVector messageBytes = readBlock(messageLength);
|
||||
READ_ASSERT(messageBytes.size() == messageLength);
|
||||
int index = messageBytes.find((char) 0);
|
||||
int index = messageBytes.find(static_cast<char>(0));
|
||||
if(index > -1)
|
||||
messageBytes.resize(index, 0);
|
||||
messageBytes.replace('\r', '\n');
|
||||
@@ -252,15 +244,15 @@ void IT::File::read(bool)
|
||||
// I don't count disabled and muted channels.
|
||||
// But this always gives 64 channels for all my files anyway.
|
||||
// Strangely VLC does report other values. I wonder how VLC
|
||||
// gets it's values.
|
||||
if((unsigned char) pannings[i] < 128 && volumes[i] > 0)
|
||||
// gets its values.
|
||||
if(static_cast<unsigned char>(pannings[i]) < 128 && volumes[i] > 0)
|
||||
++channels;
|
||||
}
|
||||
d->properties.setChannels(channels);
|
||||
|
||||
// real length might be shorter because of skips and terminator
|
||||
ushort realLength = 0;
|
||||
for(ushort i = 0; i < length; ++ i) {
|
||||
unsigned short realLength = 0;
|
||||
for(unsigned short i = 0; i < length; ++ i) {
|
||||
READ_BYTE_AS(order);
|
||||
if(order == 255) break;
|
||||
if(order != 254) ++ realLength;
|
||||
@@ -272,10 +264,10 @@ void IT::File::read(bool)
|
||||
// in the instrument/sample names and more characters
|
||||
// afterwards. The spec does not mention such a case.
|
||||
// Currently I just discard anything after a nil, but
|
||||
// e.g. VLC seems to interprete a nil as a space. I
|
||||
// e.g. VLC seems to interpret a nil as a space. I
|
||||
// don't know what is the proper behaviour.
|
||||
for(ushort i = 0; i < instrumentCount; ++ i) {
|
||||
seek(192L + length + ((long)i << 2));
|
||||
for(unsigned short i = 0; i < instrumentCount; ++ i) {
|
||||
seek(192L + length + (static_cast<long>(i) << 2));
|
||||
READ_U32L_AS(instrumentOffset);
|
||||
seek(instrumentOffset);
|
||||
|
||||
@@ -290,8 +282,8 @@ void IT::File::read(bool)
|
||||
comment.append(instrumentName);
|
||||
}
|
||||
|
||||
for(ushort i = 0; i < sampleCount; ++ i) {
|
||||
seek(192L + length + ((long)instrumentCount << 2) + ((long)i << 2));
|
||||
for(unsigned short i = 0; i < sampleCount; ++ i) {
|
||||
seek(192L + length + (static_cast<long>(instrumentCount) << 2) + (static_cast<long>(i) << 2));
|
||||
READ_U32L_AS(sampleOffset);
|
||||
|
||||
seek(sampleOffset);
|
||||
@@ -323,7 +315,7 @@ void IT::File::read(bool)
|
||||
comment.append(sampleName);
|
||||
}
|
||||
|
||||
if(message.size() > 0)
|
||||
if(!message.isEmpty())
|
||||
comment.append(message);
|
||||
d->tag.setComment(comment.toString("\n"));
|
||||
d->tag.setTrackerName("Impulse Tracker");
|
||||
|
||||
@@ -23,8 +23,8 @@
|
||||
#define TAGLIB_ITFILE_H
|
||||
|
||||
#include "tfile.h"
|
||||
#include "audioproperties.h"
|
||||
#include "taglib_export.h"
|
||||
#include "audioproperties.h"
|
||||
#include "modfilebase.h"
|
||||
#include "modtag.h"
|
||||
#include "itproperties.h"
|
||||
@@ -63,27 +63,18 @@ namespace TagLib {
|
||||
/*!
|
||||
* Destroys this instance of the File.
|
||||
*/
|
||||
virtual ~File();
|
||||
~File() override;
|
||||
|
||||
Mod::Tag *tag() const;
|
||||
File(const File &) = delete;
|
||||
File &operator=(const File &) = delete;
|
||||
|
||||
/*!
|
||||
* Forwards to Mod::Tag::properties().
|
||||
* BIC: will be removed once File::toDict() is made virtual
|
||||
*/
|
||||
PropertyMap properties() const;
|
||||
|
||||
/*!
|
||||
* Forwards to Mod::Tag::setProperties().
|
||||
* BIC: will be removed once File::setProperties() is made virtual
|
||||
*/
|
||||
PropertyMap setProperties(const PropertyMap &);
|
||||
Mod::Tag *tag() const override;
|
||||
|
||||
/*!
|
||||
* Returns the IT::Properties for this file. If no audio properties
|
||||
* were read then this will return a null pointer.
|
||||
*/
|
||||
IT::Properties *audioProperties() const;
|
||||
IT::Properties *audioProperties() const override;
|
||||
|
||||
/*!
|
||||
* Save the file.
|
||||
@@ -91,19 +82,16 @@ namespace TagLib {
|
||||
*
|
||||
* \note Saving Impulse Tracker tags is not supported.
|
||||
*/
|
||||
bool save();
|
||||
|
||||
bool save() override;
|
||||
|
||||
private:
|
||||
File(const File &);
|
||||
File &operator=(const File &);
|
||||
|
||||
void read(bool readProperties);
|
||||
|
||||
class FilePrivate;
|
||||
FilePrivate *d;
|
||||
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
|
||||
std::unique_ptr<FilePrivate> d;
|
||||
};
|
||||
}
|
||||
}
|
||||
} // namespace IT
|
||||
} // namespace TagLib
|
||||
|
||||
#endif
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
/***************************************************************************
|
||||
* This library is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU Lesser General Public License version *
|
||||
* it under the terms of the GNU Lesser General Public License version *
|
||||
* 2.1 as published by the Free Software Foundation. *
|
||||
* *
|
||||
* This library is distributed in the hope that it will be useful, but *
|
||||
@@ -15,8 +15,12 @@
|
||||
* *
|
||||
* You should have received a copy of the GNU Lesser General Public *
|
||||
* License along with this library; if not, write to the Free Software *
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, *
|
||||
* MA 02110-1301 USA *
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
|
||||
* 02110-1301 USA *
|
||||
* *
|
||||
* Alternatively, this file is available under the Mozilla Public *
|
||||
* License Version 1.1. You may obtain a copy of the License at *
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#include "itproperties.h"
|
||||
@@ -27,84 +31,37 @@ using namespace IT;
|
||||
class IT::Properties::PropertiesPrivate
|
||||
{
|
||||
public:
|
||||
PropertiesPrivate() :
|
||||
channels(0),
|
||||
lengthInPatterns(0),
|
||||
instrumentCount(0),
|
||||
sampleCount(0),
|
||||
patternCount(0),
|
||||
version(0),
|
||||
compatibleVersion(0),
|
||||
flags(0),
|
||||
special(0),
|
||||
globalVolume(0),
|
||||
mixVolume(0),
|
||||
tempo(0),
|
||||
bpmSpeed(0),
|
||||
panningSeparation(0),
|
||||
pitchWheelDepth(0)
|
||||
{
|
||||
}
|
||||
|
||||
int channels;
|
||||
ushort lengthInPatterns;
|
||||
ushort instrumentCount;
|
||||
ushort sampleCount;
|
||||
ushort patternCount;
|
||||
ushort version;
|
||||
ushort compatibleVersion;
|
||||
ushort flags;
|
||||
ushort special;
|
||||
uchar globalVolume;
|
||||
uchar mixVolume;
|
||||
uchar tempo;
|
||||
uchar bpmSpeed;
|
||||
uchar panningSeparation;
|
||||
uchar pitchWheelDepth;
|
||||
int channels { 0 };
|
||||
unsigned short lengthInPatterns { 0 };
|
||||
unsigned short instrumentCount { 0 };
|
||||
unsigned short sampleCount { 0 };
|
||||
unsigned short patternCount { 0 };
|
||||
unsigned short version { 0 };
|
||||
unsigned short compatibleVersion { 0 };
|
||||
unsigned short flags { 0 };
|
||||
unsigned short special { 0 };
|
||||
unsigned char globalVolume { 0 };
|
||||
unsigned char mixVolume { 0 };
|
||||
unsigned char tempo { 0 };
|
||||
unsigned char bpmSpeed { 0 };
|
||||
unsigned char panningSeparation { 0 };
|
||||
unsigned char pitchWheelDepth { 0 };
|
||||
};
|
||||
|
||||
IT::Properties::Properties(AudioProperties::ReadStyle propertiesStyle) :
|
||||
AudioProperties(propertiesStyle),
|
||||
d(new PropertiesPrivate)
|
||||
d(std::make_unique<PropertiesPrivate>())
|
||||
{
|
||||
}
|
||||
|
||||
IT::Properties::~Properties()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
|
||||
int IT::Properties::length() const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int IT::Properties::lengthInSeconds() const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int IT::Properties::lengthInMilliseconds() const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int IT::Properties::bitrate() const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int IT::Properties::sampleRate() const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
IT::Properties::~Properties() = default;
|
||||
|
||||
int IT::Properties::channels() const
|
||||
{
|
||||
return d->channels;
|
||||
}
|
||||
|
||||
TagLib::ushort IT::Properties::lengthInPatterns() const
|
||||
unsigned short IT::Properties::lengthInPatterns() const
|
||||
{
|
||||
return d->lengthInPatterns;
|
||||
}
|
||||
@@ -114,67 +71,67 @@ bool IT::Properties::stereo() const
|
||||
return d->flags & Stereo;
|
||||
}
|
||||
|
||||
TagLib::ushort IT::Properties::instrumentCount() const
|
||||
unsigned short IT::Properties::instrumentCount() const
|
||||
{
|
||||
return d->instrumentCount;
|
||||
}
|
||||
|
||||
TagLib::ushort IT::Properties::sampleCount() const
|
||||
unsigned short IT::Properties::sampleCount() const
|
||||
{
|
||||
return d->sampleCount;
|
||||
}
|
||||
|
||||
TagLib::ushort IT::Properties::patternCount() const
|
||||
unsigned short IT::Properties::patternCount() const
|
||||
{
|
||||
return d->patternCount;
|
||||
}
|
||||
|
||||
TagLib::ushort IT::Properties::version() const
|
||||
unsigned short IT::Properties::version() const
|
||||
{
|
||||
return d->version;
|
||||
}
|
||||
|
||||
TagLib::ushort IT::Properties::compatibleVersion() const
|
||||
unsigned short IT::Properties::compatibleVersion() const
|
||||
{
|
||||
return d->compatibleVersion;
|
||||
}
|
||||
|
||||
TagLib::ushort IT::Properties::flags() const
|
||||
unsigned short IT::Properties::flags() const
|
||||
{
|
||||
return d->flags;
|
||||
}
|
||||
|
||||
TagLib::ushort IT::Properties::special() const
|
||||
unsigned short IT::Properties::special() const
|
||||
{
|
||||
return d->special;
|
||||
}
|
||||
|
||||
uchar IT::Properties::globalVolume() const
|
||||
unsigned char IT::Properties::globalVolume() const
|
||||
{
|
||||
return d->globalVolume;
|
||||
}
|
||||
|
||||
uchar IT::Properties::mixVolume() const
|
||||
unsigned char IT::Properties::mixVolume() const
|
||||
{
|
||||
return d->mixVolume;
|
||||
}
|
||||
|
||||
uchar IT::Properties::tempo() const
|
||||
unsigned char IT::Properties::tempo() const
|
||||
{
|
||||
return d->tempo;
|
||||
}
|
||||
|
||||
uchar IT::Properties::bpmSpeed() const
|
||||
unsigned char IT::Properties::bpmSpeed() const
|
||||
{
|
||||
return d->bpmSpeed;
|
||||
}
|
||||
|
||||
uchar IT::Properties::panningSeparation() const
|
||||
unsigned char IT::Properties::panningSeparation() const
|
||||
{
|
||||
return d->panningSeparation;
|
||||
}
|
||||
|
||||
uchar IT::Properties::pitchWheelDepth() const
|
||||
unsigned char IT::Properties::pitchWheelDepth() const
|
||||
{
|
||||
return d->pitchWheelDepth;
|
||||
}
|
||||
@@ -184,72 +141,72 @@ void IT::Properties::setChannels(int channels)
|
||||
d->channels = channels;
|
||||
}
|
||||
|
||||
void IT::Properties::setLengthInPatterns(ushort lengthInPatterns)
|
||||
void IT::Properties::setLengthInPatterns(unsigned short lengthInPatterns)
|
||||
{
|
||||
d->lengthInPatterns = lengthInPatterns;
|
||||
}
|
||||
|
||||
void IT::Properties::setInstrumentCount(ushort instrumentCount)
|
||||
void IT::Properties::setInstrumentCount(unsigned short instrumentCount)
|
||||
{
|
||||
d->instrumentCount = instrumentCount;
|
||||
}
|
||||
|
||||
void IT::Properties::setSampleCount(ushort sampleCount)
|
||||
void IT::Properties::setSampleCount(unsigned short sampleCount)
|
||||
{
|
||||
d->sampleCount = sampleCount;
|
||||
}
|
||||
|
||||
void IT::Properties::setPatternCount(ushort patternCount)
|
||||
void IT::Properties::setPatternCount(unsigned short patternCount)
|
||||
{
|
||||
d->patternCount = patternCount;
|
||||
}
|
||||
|
||||
void IT::Properties::setFlags(ushort flags)
|
||||
void IT::Properties::setFlags(unsigned short flags)
|
||||
{
|
||||
d->flags = flags;
|
||||
}
|
||||
|
||||
void IT::Properties::setSpecial(ushort special)
|
||||
void IT::Properties::setSpecial(unsigned short special)
|
||||
{
|
||||
d->special = special;
|
||||
}
|
||||
|
||||
void IT::Properties::setCompatibleVersion(ushort compatibleVersion)
|
||||
void IT::Properties::setCompatibleVersion(unsigned short compatibleVersion)
|
||||
{
|
||||
d->compatibleVersion = compatibleVersion;
|
||||
}
|
||||
|
||||
void IT::Properties::setVersion(ushort version)
|
||||
void IT::Properties::setVersion(unsigned short version)
|
||||
{
|
||||
d->version = version;
|
||||
}
|
||||
|
||||
void IT::Properties::setGlobalVolume(uchar globalVolume)
|
||||
void IT::Properties::setGlobalVolume(unsigned char globalVolume)
|
||||
{
|
||||
d->globalVolume = globalVolume;
|
||||
}
|
||||
|
||||
void IT::Properties::setMixVolume(uchar mixVolume)
|
||||
void IT::Properties::setMixVolume(unsigned char mixVolume)
|
||||
{
|
||||
d->mixVolume = mixVolume;
|
||||
}
|
||||
|
||||
void IT::Properties::setTempo(uchar tempo)
|
||||
void IT::Properties::setTempo(unsigned char tempo)
|
||||
{
|
||||
d->tempo = tempo;
|
||||
}
|
||||
|
||||
void IT::Properties::setBpmSpeed(uchar bpmSpeed)
|
||||
void IT::Properties::setBpmSpeed(unsigned char bpmSpeed)
|
||||
{
|
||||
d->bpmSpeed = bpmSpeed;
|
||||
}
|
||||
|
||||
void IT::Properties::setPanningSeparation(uchar panningSeparation)
|
||||
void IT::Properties::setPanningSeparation(unsigned char panningSeparation)
|
||||
{
|
||||
d->panningSeparation = panningSeparation;
|
||||
}
|
||||
|
||||
void IT::Properties::setPitchWheelDepth(uchar pitchWheelDepth)
|
||||
void IT::Properties::setPitchWheelDepth(unsigned char pitchWheelDepth)
|
||||
{
|
||||
d->pitchWheelDepth = pitchWheelDepth;
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
/***************************************************************************
|
||||
* This library is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU Lesser General Public License version *
|
||||
* it under the terms of the GNU Lesser General Public License version *
|
||||
* 2.1 as published by the Free Software Foundation. *
|
||||
* *
|
||||
* This library is distributed in the hope that it will be useful, but *
|
||||
@@ -15,8 +15,12 @@
|
||||
* *
|
||||
* You should have received a copy of the GNU Lesser General Public *
|
||||
* License along with this library; if not, write to the Free Software *
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, *
|
||||
* MA 02110-1301 USA *
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
|
||||
* 02110-1301 USA *
|
||||
* *
|
||||
* Alternatively, this file is available under the Mozilla Public *
|
||||
* License Version 1.1. You may obtain a copy of the License at *
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef TAGLIB_ITPROPERTIES_H
|
||||
@@ -28,7 +32,6 @@
|
||||
namespace TagLib {
|
||||
namespace IT {
|
||||
class TAGLIB_EXPORT Properties : public AudioProperties {
|
||||
friend class File;
|
||||
public:
|
||||
/*! Flag bits. */
|
||||
enum {
|
||||
@@ -49,55 +52,51 @@ namespace TagLib {
|
||||
};
|
||||
|
||||
Properties(AudioProperties::ReadStyle propertiesStyle);
|
||||
virtual ~Properties();
|
||||
~Properties() override;
|
||||
|
||||
int length() const;
|
||||
int lengthInSeconds() const;
|
||||
int lengthInMilliseconds() const;
|
||||
int bitrate() const;
|
||||
int sampleRate() const;
|
||||
int channels() const;
|
||||
Properties(const Properties &) = delete;
|
||||
Properties &operator=(const Properties &) = delete;
|
||||
|
||||
ushort lengthInPatterns() const;
|
||||
bool stereo() const;
|
||||
ushort instrumentCount() const;
|
||||
ushort sampleCount() const;
|
||||
ushort patternCount() const;
|
||||
ushort version() const;
|
||||
ushort compatibleVersion() const;
|
||||
ushort flags() const;
|
||||
ushort special() const;
|
||||
uchar globalVolume() const;
|
||||
uchar mixVolume() const;
|
||||
uchar tempo() const;
|
||||
uchar bpmSpeed() const;
|
||||
uchar panningSeparation() const;
|
||||
uchar pitchWheelDepth() const;
|
||||
int channels() const override;
|
||||
|
||||
unsigned short lengthInPatterns() const;
|
||||
bool stereo() const;
|
||||
unsigned short instrumentCount() const;
|
||||
unsigned short sampleCount() const;
|
||||
unsigned short patternCount() const;
|
||||
unsigned short version() const;
|
||||
unsigned short compatibleVersion() const;
|
||||
unsigned short flags() const;
|
||||
unsigned short special() const;
|
||||
unsigned char globalVolume() const;
|
||||
unsigned char mixVolume() const;
|
||||
unsigned char tempo() const;
|
||||
unsigned char bpmSpeed() const;
|
||||
unsigned char panningSeparation() const;
|
||||
unsigned char pitchWheelDepth() const;
|
||||
|
||||
void setChannels(int channels);
|
||||
void setLengthInPatterns(ushort lengthInPatterns);
|
||||
void setInstrumentCount(ushort instrumentCount);
|
||||
void setSampleCount (ushort sampleCount);
|
||||
void setPatternCount(ushort patternCount);
|
||||
void setVersion (ushort version);
|
||||
void setCompatibleVersion(ushort compatibleVersion);
|
||||
void setFlags (ushort flags);
|
||||
void setSpecial (ushort special);
|
||||
void setGlobalVolume(uchar globalVolume);
|
||||
void setMixVolume (uchar mixVolume);
|
||||
void setTempo (uchar tempo);
|
||||
void setBpmSpeed (uchar bpmSpeed);
|
||||
void setPanningSeparation(uchar panningSeparation);
|
||||
void setPitchWheelDepth (uchar pitchWheelDepth);
|
||||
void setLengthInPatterns(unsigned short lengthInPatterns);
|
||||
void setInstrumentCount(unsigned short instrumentCount);
|
||||
void setSampleCount(unsigned short sampleCount);
|
||||
void setPatternCount(unsigned short patternCount);
|
||||
void setVersion(unsigned short version);
|
||||
void setCompatibleVersion(unsigned short compatibleVersion);
|
||||
void setFlags(unsigned short flags);
|
||||
void setSpecial(unsigned short special);
|
||||
void setGlobalVolume(unsigned char globalVolume);
|
||||
void setMixVolume(unsigned char mixVolume);
|
||||
void setTempo(unsigned char tempo);
|
||||
void setBpmSpeed(unsigned char bpmSpeed);
|
||||
void setPanningSeparation(unsigned char panningSeparation);
|
||||
void setPitchWheelDepth(unsigned char pitchWheelDepth);
|
||||
|
||||
private:
|
||||
Properties(const Properties&);
|
||||
Properties &operator=(const Properties&);
|
||||
|
||||
class PropertiesPrivate;
|
||||
PropertiesPrivate *d;
|
||||
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
|
||||
std::unique_ptr<PropertiesPrivate> d;
|
||||
};
|
||||
}
|
||||
}
|
||||
} // namespace IT
|
||||
} // namespace TagLib
|
||||
|
||||
#endif
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
/***************************************************************************
|
||||
* This library is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU Lesser General Public License version *
|
||||
* it under the terms of the GNU Lesser General Public License version *
|
||||
* 2.1 as published by the Free Software Foundation. *
|
||||
* *
|
||||
* This library is distributed in the hope that it will be useful, but *
|
||||
@@ -15,15 +15,20 @@
|
||||
* *
|
||||
* You should have received a copy of the GNU Lesser General Public *
|
||||
* License along with this library; if not, write to the Free Software *
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, *
|
||||
* MA 02110-1301 USA *
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
|
||||
* 02110-1301 USA *
|
||||
* *
|
||||
* Alternatively, this file is available under the Mozilla Public *
|
||||
* License Version 1.1. You may obtain a copy of the License at *
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#include "modfile.h"
|
||||
|
||||
#include "tstringlist.h"
|
||||
#include "tdebug.h"
|
||||
#include "modfileprivate.h"
|
||||
#include "tpropertymap.h"
|
||||
#include "modfileprivate.h"
|
||||
|
||||
using namespace TagLib;
|
||||
using namespace Mod;
|
||||
@@ -43,7 +48,7 @@ public:
|
||||
Mod::File::File(FileName file, bool readProperties,
|
||||
AudioProperties::ReadStyle propertiesStyle) :
|
||||
Mod::FileBase(file),
|
||||
d(new FilePrivate(propertiesStyle))
|
||||
d(std::make_unique<FilePrivate>(propertiesStyle))
|
||||
{
|
||||
if(isOpen())
|
||||
read(readProperties);
|
||||
@@ -52,16 +57,13 @@ Mod::File::File(FileName file, bool readProperties,
|
||||
Mod::File::File(IOStream *stream, bool readProperties,
|
||||
AudioProperties::ReadStyle propertiesStyle) :
|
||||
Mod::FileBase(stream),
|
||||
d(new FilePrivate(propertiesStyle))
|
||||
d(std::make_unique<FilePrivate>(propertiesStyle))
|
||||
{
|
||||
if(isOpen())
|
||||
read(readProperties);
|
||||
}
|
||||
|
||||
Mod::File::~File()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
Mod::File::~File() = default;
|
||||
|
||||
Mod::Tag *Mod::File::tag() const
|
||||
{
|
||||
@@ -92,14 +94,14 @@ bool Mod::File::save()
|
||||
seek(0);
|
||||
writeString(d->tag.title(), 20);
|
||||
StringList lines = d->tag.comment().split("\n");
|
||||
uint n = std::min(lines.size(), d->properties.instrumentCount());
|
||||
for(uint i = 0; i < n; ++ i) {
|
||||
unsigned int n = std::min(lines.size(), d->properties.instrumentCount());
|
||||
for(unsigned int i = 0; i < n; ++ i) {
|
||||
writeString(lines[i], 22);
|
||||
seek(8, Current);
|
||||
}
|
||||
|
||||
for(uint i = n; i < d->properties.instrumentCount(); ++ i) {
|
||||
writeString(String::null, 22);
|
||||
for(unsigned int i = n; i < d->properties.instrumentCount(); ++ i) {
|
||||
writeString(String(), 22);
|
||||
seek(8, Current);
|
||||
}
|
||||
return true;
|
||||
@@ -114,8 +116,8 @@ void Mod::File::read(bool)
|
||||
ByteVector modId = readBlock(4);
|
||||
READ_ASSERT(modId.size() == 4);
|
||||
|
||||
int channels = 4;
|
||||
uint instruments = 31;
|
||||
int channels = 4;
|
||||
unsigned int instruments = 31;
|
||||
if(modId == "M.K." || modId == "M!K!" || modId == "M&K!" || modId == "N.T.") {
|
||||
d->tag.setTrackerName("ProTracker");
|
||||
channels = 4;
|
||||
@@ -158,25 +160,30 @@ void Mod::File::read(bool)
|
||||
seek(0);
|
||||
READ_STRING(d->tag.setTitle, 20);
|
||||
|
||||
offset_t pos = 20;
|
||||
StringList comment;
|
||||
for(uint i = 0; i < instruments; ++ i) {
|
||||
for(unsigned int i = 0; i < instruments; ++ i) {
|
||||
READ_STRING_AS(instrumentName, 22);
|
||||
// value in words, * 2 (<< 1) for bytes:
|
||||
READ_U16B_AS(sampleLength);
|
||||
// skip unused data
|
||||
pos += 22 + 2 + 1 + 1 + 2 + 2;
|
||||
seek(pos);
|
||||
|
||||
READ_BYTE_AS(fineTuneByte);
|
||||
int fineTune = fineTuneByte & 0xF;
|
||||
// > 7 means negative value
|
||||
if(fineTune > 7) fineTune -= 16;
|
||||
// // value in words, * 2 (<< 1) for bytes:
|
||||
// READ_U16B_AS(sampleLength);
|
||||
|
||||
READ_BYTE_AS(volume);
|
||||
if(volume > 64) volume = 64;
|
||||
// volume in decibels: 20 * log10(volume / 64)
|
||||
// READ_BYTE_AS(fineTuneByte);
|
||||
// int fineTune = fineTuneByte & 0xF;
|
||||
// // > 7 means negative value
|
||||
// if(fineTune > 7) fineTune -= 16;
|
||||
|
||||
// value in words, * 2 (<< 1) for bytes:
|
||||
READ_U16B_AS(repeatStart);
|
||||
// value in words, * 2 (<< 1) for bytes:
|
||||
READ_U16B_AS(repatLength);
|
||||
// READ_BYTE_AS(volume);
|
||||
// if(volume > 64) volume = 64;
|
||||
// // volume in decibels: 20 * log10(volume / 64)
|
||||
|
||||
// // value in words, * 2 (<< 1) for bytes:
|
||||
// READ_U16B_AS(repeatStart);
|
||||
// // value in words, * 2 (<< 1) for bytes:
|
||||
// READ_U16B_AS(repeatLength);
|
||||
|
||||
comment.append(instrumentName);
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
/***************************************************************************
|
||||
* This library is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU Lesser General Public License version *
|
||||
* it under the terms of the GNU Lesser General Public License version *
|
||||
* 2.1 as published by the Free Software Foundation. *
|
||||
* *
|
||||
* This library is distributed in the hope that it will be useful, but *
|
||||
@@ -15,24 +15,26 @@
|
||||
* *
|
||||
* You should have received a copy of the GNU Lesser General Public *
|
||||
* License along with this library; if not, write to the Free Software *
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, *
|
||||
* MA 02110-1301 USA *
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
|
||||
* 02110-1301 USA *
|
||||
* *
|
||||
* Alternatively, this file is available under the Mozilla Public *
|
||||
* License Version 1.1. You may obtain a copy of the License at *
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef TAGLIB_MODFILE_H
|
||||
#define TAGLIB_MODFILE_H
|
||||
|
||||
#include "tfile.h"
|
||||
#include "audioproperties.h"
|
||||
#include "taglib_export.h"
|
||||
#include "audioproperties.h"
|
||||
#include "modfilebase.h"
|
||||
#include "modtag.h"
|
||||
#include "modproperties.h"
|
||||
|
||||
namespace TagLib {
|
||||
|
||||
namespace Mod {
|
||||
|
||||
class TAGLIB_EXPORT File : public TagLib::Mod::FileBase
|
||||
{
|
||||
public:
|
||||
@@ -64,26 +66,29 @@ namespace TagLib {
|
||||
/*!
|
||||
* Destroys this instance of the File.
|
||||
*/
|
||||
virtual ~File();
|
||||
~File() override;
|
||||
|
||||
Mod::Tag *tag() const;
|
||||
File(const File &) = delete;
|
||||
File &operator=(const File &) = delete;
|
||||
|
||||
Mod::Tag *tag() const override;
|
||||
|
||||
/*!
|
||||
* Implements the unified property interface -- export function.
|
||||
* Forwards to Mod::Tag::properties().
|
||||
*/
|
||||
PropertyMap properties() const;
|
||||
PropertyMap properties() const override;
|
||||
|
||||
/*!
|
||||
* Implements the unified property interface -- import function.
|
||||
* Forwards to Mod::Tag::setProperties().
|
||||
*/
|
||||
PropertyMap setProperties(const PropertyMap &);
|
||||
PropertyMap setProperties(const PropertyMap &) override;
|
||||
/*!
|
||||
* 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::Properties *audioProperties() const override;
|
||||
|
||||
/*!
|
||||
* Save the file.
|
||||
@@ -91,20 +96,15 @@ namespace TagLib {
|
||||
*
|
||||
* \note Saving Protracker tags is not supported.
|
||||
*/
|
||||
bool save();
|
||||
bool save() override;
|
||||
|
||||
private:
|
||||
File(const File &);
|
||||
File &operator=(const File &);
|
||||
|
||||
void read(bool readProperties);
|
||||
|
||||
class FilePrivate;
|
||||
FilePrivate *d;
|
||||
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
|
||||
std::unique_ptr<FilePrivate> d;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} // namespace Mod
|
||||
} // namespace TagLib
|
||||
#endif
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
/***************************************************************************
|
||||
* This library is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU Lesser General Public License version *
|
||||
* it under the terms of the GNU Lesser General Public License version *
|
||||
* 2.1 as published by the Free Software Foundation. *
|
||||
* *
|
||||
* This library is distributed in the hope that it will be useful, but *
|
||||
@@ -15,16 +15,25 @@
|
||||
* *
|
||||
* You should have received a copy of the GNU Lesser General Public *
|
||||
* License along with this library; if not, write to the Free Software *
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, *
|
||||
* MA 02110-1301 USA *
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
|
||||
* 02110-1301 USA *
|
||||
* *
|
||||
* Alternatively, this file is available under the Mozilla Public *
|
||||
* License Version 1.1. You may obtain a copy of the License at *
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#include "tdebug.h"
|
||||
#include "modfilebase.h"
|
||||
|
||||
using namespace TagLib;
|
||||
using namespace Mod;
|
||||
|
||||
class Mod::FileBase::FileBasePrivate
|
||||
{
|
||||
};
|
||||
|
||||
Mod::FileBase::~FileBase() = default;
|
||||
|
||||
Mod::FileBase::FileBase(FileName file) : TagLib::File(file)
|
||||
{
|
||||
}
|
||||
@@ -33,55 +42,55 @@ Mod::FileBase::FileBase(IOStream *stream) : TagLib::File(stream)
|
||||
{
|
||||
}
|
||||
|
||||
void Mod::FileBase::writeString(const String &s, ulong size, char padding)
|
||||
void Mod::FileBase::writeString(const String &s, unsigned long size, char padding)
|
||||
{
|
||||
ByteVector data(s.data(String::Latin1));
|
||||
data.resize(size, padding);
|
||||
data.resize(static_cast<unsigned int>(size), padding);
|
||||
writeBlock(data);
|
||||
}
|
||||
|
||||
bool Mod::FileBase::readString(String &s, ulong size)
|
||||
bool Mod::FileBase::readString(String &s, unsigned long size)
|
||||
{
|
||||
ByteVector data(readBlock(size));
|
||||
if(data.size() < size) return false;
|
||||
int index = data.find((char) 0);
|
||||
int index = data.find(static_cast<char>(0));
|
||||
if(index > -1)
|
||||
{
|
||||
data.resize(index);
|
||||
}
|
||||
data.replace((char) 0xff, ' ');
|
||||
data.replace('\xff', ' ');
|
||||
|
||||
s = data;
|
||||
return true;
|
||||
}
|
||||
|
||||
void Mod::FileBase::writeByte(uchar byte)
|
||||
void Mod::FileBase::writeByte(unsigned char byte)
|
||||
{
|
||||
ByteVector data(1, byte);
|
||||
writeBlock(data);
|
||||
}
|
||||
|
||||
void Mod::FileBase::writeU16L(ushort number)
|
||||
void Mod::FileBase::writeU16L(unsigned short number)
|
||||
{
|
||||
writeBlock(ByteVector::fromShort(number, false));
|
||||
}
|
||||
|
||||
void Mod::FileBase::writeU32L(ulong number)
|
||||
void Mod::FileBase::writeU32L(unsigned long number)
|
||||
{
|
||||
writeBlock(ByteVector::fromUInt(number, false));
|
||||
writeBlock(ByteVector::fromUInt(static_cast<unsigned int>(number), false));
|
||||
}
|
||||
|
||||
void Mod::FileBase::writeU16B(ushort number)
|
||||
void Mod::FileBase::writeU16B(unsigned short number)
|
||||
{
|
||||
writeBlock(ByteVector::fromShort(number, true));
|
||||
}
|
||||
|
||||
void Mod::FileBase::writeU32B(ulong number)
|
||||
void Mod::FileBase::writeU32B(unsigned long number)
|
||||
{
|
||||
writeBlock(ByteVector::fromUInt(number, true));
|
||||
writeBlock(ByteVector::fromUInt(static_cast<unsigned int>(number), true));
|
||||
}
|
||||
|
||||
bool Mod::FileBase::readByte(uchar &byte)
|
||||
bool Mod::FileBase::readByte(unsigned char &byte)
|
||||
{
|
||||
ByteVector data(readBlock(1));
|
||||
if(data.size() < 1) return false;
|
||||
@@ -89,7 +98,7 @@ bool Mod::FileBase::readByte(uchar &byte)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Mod::FileBase::readU16L(ushort &number)
|
||||
bool Mod::FileBase::readU16L(unsigned short &number)
|
||||
{
|
||||
ByteVector data(readBlock(2));
|
||||
if(data.size() < 2) return false;
|
||||
@@ -97,14 +106,14 @@ bool Mod::FileBase::readU16L(ushort &number)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Mod::FileBase::readU32L(ulong &number) {
|
||||
bool Mod::FileBase::readU32L(unsigned long &number) {
|
||||
ByteVector data(readBlock(4));
|
||||
if(data.size() < 4) return false;
|
||||
number = data.toUInt(false);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Mod::FileBase::readU16B(ushort &number)
|
||||
bool Mod::FileBase::readU16B(unsigned short &number)
|
||||
{
|
||||
ByteVector data(readBlock(2));
|
||||
if(data.size() < 2) return false;
|
||||
@@ -112,7 +121,7 @@ bool Mod::FileBase::readU16B(ushort &number)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Mod::FileBase::readU32B(ulong &number) {
|
||||
bool Mod::FileBase::readU32B(unsigned long &number) {
|
||||
ByteVector data(readBlock(4));
|
||||
if(data.size() < 4) return false;
|
||||
number = data.toUInt(true);
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
/***************************************************************************
|
||||
* This library is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU Lesser General Public License version *
|
||||
* it under the terms of the GNU Lesser General Public License version *
|
||||
* 2.1 as published by the Free Software Foundation. *
|
||||
* *
|
||||
* This library is distributed in the hope that it will be useful, but *
|
||||
@@ -15,8 +15,12 @@
|
||||
* *
|
||||
* You should have received a copy of the GNU Lesser General Public *
|
||||
* License along with this library; if not, write to the Free Software *
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, *
|
||||
* MA 02110-1301 USA *
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
|
||||
* 02110-1301 USA *
|
||||
* *
|
||||
* Alternatively, this file is available under the Mozilla Public *
|
||||
* License Version 1.1. You may obtain a copy of the License at *
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef TAGLIB_MODFILEBASE_H
|
||||
@@ -31,32 +35,37 @@
|
||||
#include <algorithm>
|
||||
|
||||
namespace TagLib {
|
||||
|
||||
namespace Mod {
|
||||
|
||||
class TAGLIB_EXPORT FileBase : public TagLib::File
|
||||
{
|
||||
public:
|
||||
~FileBase() override;
|
||||
|
||||
FileBase(const FileBase &) = delete;
|
||||
FileBase& operator=(const FileBase &) = delete;
|
||||
|
||||
protected:
|
||||
FileBase(FileName file);
|
||||
FileBase(IOStream *stream);
|
||||
|
||||
void writeString(const String &s, ulong size, char padding = 0);
|
||||
void writeByte(uchar byte);
|
||||
void writeU16L(ushort number);
|
||||
void writeU32L(ulong number);
|
||||
void writeU16B(ushort number);
|
||||
void writeU32B(ulong number);
|
||||
void writeString(const String &s, unsigned long size, char padding = 0);
|
||||
void writeByte(unsigned char byte);
|
||||
void writeU16L(unsigned short number);
|
||||
void writeU32L(unsigned long number);
|
||||
void writeU16B(unsigned short number);
|
||||
void writeU32B(unsigned long number);
|
||||
|
||||
bool readString(String &s, ulong size);
|
||||
bool readByte(uchar &byte);
|
||||
bool readU16L(ushort &number);
|
||||
bool readU32L(ulong &number);
|
||||
bool readU16B(ushort &number);
|
||||
bool readU32B(ulong &number);
|
||||
bool readString(String &s, unsigned long size);
|
||||
bool readByte(unsigned char &byte);
|
||||
bool readU16L(unsigned short &number);
|
||||
bool readU32L(unsigned long &number);
|
||||
bool readU16B(unsigned short &number);
|
||||
bool readU32B(unsigned long &number);
|
||||
private:
|
||||
class FileBasePrivate;
|
||||
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
|
||||
std::unique_ptr<FileBasePrivate> d;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} // namespace Mod
|
||||
} // namespace TagLib
|
||||
#endif
|
||||
|
||||
@@ -23,45 +23,45 @@
|
||||
#define TAGLIB_MODFILEPRIVATE_H
|
||||
|
||||
// some helper-macros only used internally by (s3m|it|xm)file.cpp
|
||||
#define READ_ASSERT(cond) \
|
||||
if(!(cond)) \
|
||||
{ \
|
||||
setValid(false); \
|
||||
return; \
|
||||
}
|
||||
#define READ_ASSERT(cond) \
|
||||
do { \
|
||||
if(!(cond)) { \
|
||||
setValid(false); \
|
||||
return; \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
#define READ(setter,type,read) \
|
||||
{ \
|
||||
type number; \
|
||||
READ_ASSERT(read(number)); \
|
||||
setter(number); \
|
||||
}
|
||||
#define READ(setter, type, read) \
|
||||
do { \
|
||||
type number; \
|
||||
READ_ASSERT(read(number)); \
|
||||
setter(number); \
|
||||
} while(0)
|
||||
|
||||
#define READ_BYTE(setter) READ(setter,uchar,readByte)
|
||||
#define READ_U16L(setter) READ(setter,ushort,readU16L)
|
||||
#define READ_U32L(setter) READ(setter,ulong,readU32L)
|
||||
#define READ_U16B(setter) READ(setter,ushort,readU16B)
|
||||
#define READ_U32B(setter) READ(setter,ulong,readU32B)
|
||||
#define READ_BYTE(setter) READ(setter, unsigned char, readByte)
|
||||
#define READ_U16L(setter) READ(setter, unsigned short, readU16L)
|
||||
#define READ_U32L(setter) READ(setter, unsigned long, readU32L)
|
||||
#define READ_U16B(setter) READ(setter, unsigned short, readU16B)
|
||||
#define READ_U32B(setter) READ(setter, unsigned long, readU32B)
|
||||
|
||||
#define READ_STRING(setter,size) \
|
||||
{ \
|
||||
String s; \
|
||||
READ_ASSERT(readString(s, size)); \
|
||||
setter(s); \
|
||||
}
|
||||
#define READ_STRING(setter, size) \
|
||||
do { \
|
||||
String s; \
|
||||
READ_ASSERT(readString(s, size)); \
|
||||
setter(s); \
|
||||
} while(0)
|
||||
|
||||
#define READ_AS(type,name,read) \
|
||||
type name = 0; \
|
||||
READ_ASSERT(read(name));
|
||||
#define READ_AS(type, name, read) \
|
||||
type name = 0; \
|
||||
READ_ASSERT(read(name))
|
||||
|
||||
#define READ_BYTE_AS(name) READ_AS(uchar,name,readByte)
|
||||
#define READ_U16L_AS(name) READ_AS(ushort,name,readU16L)
|
||||
#define READ_U32L_AS(name) READ_AS(ulong,name,readU32L)
|
||||
#define READ_U16B_AS(name) READ_AS(ushort,name,readU16B)
|
||||
#define READ_U32B_AS(name) READ_AS(ulong,name,readU32B)
|
||||
|
||||
#define READ_STRING_AS(name,size) \
|
||||
String name; \
|
||||
READ_ASSERT(readString(name, size));
|
||||
#define READ_BYTE_AS(name) READ_AS(unsigned char, name, readByte)
|
||||
#define READ_U16L_AS(name) READ_AS(unsigned short, name, readU16L)
|
||||
#define READ_U32L_AS(name) READ_AS(unsigned long, name, readU32L)
|
||||
#define READ_U16B_AS(name) READ_AS(unsigned short, name, readU16B)
|
||||
#define READ_U32B_AS(name) READ_AS(unsigned long, name, readU32B)
|
||||
|
||||
#define READ_STRING_AS(name, size) \
|
||||
String name; \
|
||||
READ_ASSERT(readString(name, size))
|
||||
#endif
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
/***************************************************************************
|
||||
* This library is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU Lesser General Public License version *
|
||||
* it under the terms of the GNU Lesser General Public License version *
|
||||
* 2.1 as published by the Free Software Foundation. *
|
||||
* *
|
||||
* This library is distributed in the hope that it will be useful, but *
|
||||
@@ -15,8 +15,12 @@
|
||||
* *
|
||||
* You should have received a copy of the GNU Lesser General Public *
|
||||
* License along with this library; if not, write to the Free Software *
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, *
|
||||
* MA 02110-1301 USA *
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
|
||||
* 02110-1301 USA *
|
||||
* *
|
||||
* Alternatively, this file is available under the Mozilla Public *
|
||||
* License Version 1.1. You may obtain a copy of the License at *
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#include "modproperties.h"
|
||||
@@ -27,43 +31,18 @@ using namespace Mod;
|
||||
class Mod::Properties::PropertiesPrivate
|
||||
{
|
||||
public:
|
||||
PropertiesPrivate() :
|
||||
channels(0),
|
||||
instrumentCount(0),
|
||||
lengthInPatterns(0)
|
||||
{
|
||||
}
|
||||
|
||||
int channels;
|
||||
uint instrumentCount;
|
||||
uchar lengthInPatterns;
|
||||
int channels { 0 };
|
||||
unsigned int instrumentCount { 0 };
|
||||
unsigned char lengthInPatterns { 0 };
|
||||
};
|
||||
|
||||
Mod::Properties::Properties(AudioProperties::ReadStyle propertiesStyle) :
|
||||
AudioProperties(propertiesStyle),
|
||||
d(new PropertiesPrivate)
|
||||
d(std::make_unique<PropertiesPrivate>())
|
||||
{
|
||||
}
|
||||
|
||||
Mod::Properties::~Properties()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
|
||||
int Mod::Properties::length() const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Mod::Properties::lengthInSeconds() const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Mod::Properties::lengthInMilliseconds() const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
Mod::Properties::~Properties() = default;
|
||||
|
||||
int Mod::Properties::bitrate() const
|
||||
{
|
||||
@@ -80,12 +59,12 @@ int Mod::Properties::channels() const
|
||||
return d->channels;
|
||||
}
|
||||
|
||||
TagLib::uint Mod::Properties::instrumentCount() const
|
||||
unsigned int Mod::Properties::instrumentCount() const
|
||||
{
|
||||
return d->instrumentCount;
|
||||
}
|
||||
|
||||
uchar Mod::Properties::lengthInPatterns() const
|
||||
unsigned char Mod::Properties::lengthInPatterns() const
|
||||
{
|
||||
return d->lengthInPatterns;
|
||||
}
|
||||
@@ -95,12 +74,12 @@ void Mod::Properties::setChannels(int channels)
|
||||
d->channels = channels;
|
||||
}
|
||||
|
||||
void Mod::Properties::setInstrumentCount(uint instrumentCount)
|
||||
void Mod::Properties::setInstrumentCount(unsigned int instrumentCount)
|
||||
{
|
||||
d->instrumentCount = instrumentCount;
|
||||
}
|
||||
|
||||
void Mod::Properties::setLengthInPatterns(uchar lengthInPatterns)
|
||||
void Mod::Properties::setLengthInPatterns(unsigned char lengthInPatterns)
|
||||
{
|
||||
d->lengthInPatterns = lengthInPatterns;
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
/***************************************************************************
|
||||
* This library is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU Lesser General Public License version *
|
||||
* it under the terms of the GNU Lesser General Public License version *
|
||||
* 2.1 as published by the Free Software Foundation. *
|
||||
* *
|
||||
* This library is distributed in the hope that it will be useful, but *
|
||||
@@ -15,8 +15,12 @@
|
||||
* *
|
||||
* You should have received a copy of the GNU Lesser General Public *
|
||||
* License along with this library; if not, write to the Free Software *
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, *
|
||||
* MA 02110-1301 USA *
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
|
||||
* 02110-1301 USA *
|
||||
* *
|
||||
* Alternatively, this file is available under the Mozilla Public *
|
||||
* License Version 1.1. You may obtain a copy of the License at *
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef TAGLIB_MODPROPERTIES_H
|
||||
@@ -26,42 +30,33 @@
|
||||
#include "audioproperties.h"
|
||||
|
||||
namespace TagLib {
|
||||
|
||||
namespace Mod {
|
||||
|
||||
class TAGLIB_EXPORT Properties : public AudioProperties
|
||||
{
|
||||
public:
|
||||
Properties(AudioProperties::ReadStyle propertiesStyle);
|
||||
virtual ~Properties();
|
||||
~Properties() override;
|
||||
|
||||
int length() const;
|
||||
int lengthInSeconds() const;
|
||||
int lengthInMilliseconds() const;
|
||||
int bitrate() const;
|
||||
int sampleRate() const;
|
||||
int channels() const;
|
||||
Properties(const Properties &) = delete;
|
||||
Properties &operator=(const Properties &) = delete;
|
||||
|
||||
uint instrumentCount() const;
|
||||
uchar lengthInPatterns() const;
|
||||
int bitrate() const override;
|
||||
int sampleRate() const override;
|
||||
int channels() const override;
|
||||
|
||||
unsigned int instrumentCount() const;
|
||||
unsigned char lengthInPatterns() const;
|
||||
|
||||
void setChannels(int channels);
|
||||
|
||||
void setInstrumentCount(uint sampleCount);
|
||||
void setLengthInPatterns(uchar lengthInPatterns);
|
||||
void setInstrumentCount(unsigned int instrumentCount);
|
||||
void setLengthInPatterns(unsigned char lengthInPatterns);
|
||||
|
||||
private:
|
||||
friend class File;
|
||||
|
||||
Properties(const Properties&);
|
||||
Properties &operator=(const Properties&);
|
||||
|
||||
class PropertiesPrivate;
|
||||
PropertiesPrivate *d;
|
||||
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
|
||||
std::unique_ptr<PropertiesPrivate> d;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} // namespace Mod
|
||||
} // namespace TagLib
|
||||
#endif
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
/***************************************************************************
|
||||
* This library is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU Lesser General Public License version *
|
||||
* it under the terms of the GNU Lesser General Public License version *
|
||||
* 2.1 as published by the Free Software Foundation. *
|
||||
* *
|
||||
* This library is distributed in the hope that it will be useful, but *
|
||||
@@ -15,11 +15,18 @@
|
||||
* *
|
||||
* You should have received a copy of the GNU Lesser General Public *
|
||||
* License along with this library; if not, write to the Free Software *
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, *
|
||||
* MA 02110-1301 USA *
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
|
||||
* 02110-1301 USA *
|
||||
* *
|
||||
* Alternatively, this file is available under the Mozilla Public *
|
||||
* License Version 1.1. You may obtain a copy of the License at *
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#include "modtag.h"
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include "tstringlist.h"
|
||||
#include "tpropertymap.h"
|
||||
|
||||
@@ -29,24 +36,17 @@ using namespace Mod;
|
||||
class Mod::Tag::TagPrivate
|
||||
{
|
||||
public:
|
||||
TagPrivate()
|
||||
{
|
||||
}
|
||||
|
||||
String title;
|
||||
String comment;
|
||||
String trackerName;
|
||||
};
|
||||
|
||||
Mod::Tag::Tag() : TagLib::Tag()
|
||||
Mod::Tag::Tag() :
|
||||
d(std::make_unique<TagPrivate>())
|
||||
{
|
||||
d = new TagPrivate;
|
||||
}
|
||||
|
||||
Mod::Tag::~Tag()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
Mod::Tag::~Tag() = default;
|
||||
|
||||
String Mod::Tag::title() const
|
||||
{
|
||||
@@ -55,12 +55,12 @@ String Mod::Tag::title() const
|
||||
|
||||
String Mod::Tag::artist() const
|
||||
{
|
||||
return String::null;
|
||||
return String();
|
||||
}
|
||||
|
||||
String Mod::Tag::album() const
|
||||
{
|
||||
return String::null;
|
||||
return String();
|
||||
}
|
||||
|
||||
String Mod::Tag::comment() const
|
||||
@@ -70,15 +70,15 @@ String Mod::Tag::comment() const
|
||||
|
||||
String Mod::Tag::genre() const
|
||||
{
|
||||
return String::null;
|
||||
return String();
|
||||
}
|
||||
|
||||
TagLib::uint Mod::Tag::year() const
|
||||
unsigned int Mod::Tag::year() const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
TagLib::uint Mod::Tag::track() const
|
||||
unsigned int Mod::Tag::track() const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
@@ -110,11 +110,11 @@ void Mod::Tag::setGenre(const String &)
|
||||
{
|
||||
}
|
||||
|
||||
void Mod::Tag::setYear(uint)
|
||||
void Mod::Tag::setYear(unsigned int)
|
||||
{
|
||||
}
|
||||
|
||||
void Mod::Tag::setTrack(uint)
|
||||
void Mod::Tag::setTrack(unsigned int)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -128,41 +128,41 @@ PropertyMap Mod::Tag::properties() const
|
||||
PropertyMap properties;
|
||||
properties["TITLE"] = d->title;
|
||||
properties["COMMENT"] = d->comment;
|
||||
if(!(d->trackerName.isNull()))
|
||||
if(!(d->trackerName.isEmpty()))
|
||||
properties["TRACKERNAME"] = d->trackerName;
|
||||
return properties;
|
||||
}
|
||||
|
||||
PropertyMap Mod::Tag::setProperties(const PropertyMap &origProps)
|
||||
{
|
||||
PropertyMap properties(origProps);
|
||||
properties.removeEmpty();
|
||||
PropertyMap props(origProps);
|
||||
props.removeEmpty();
|
||||
StringList oneValueSet;
|
||||
if(properties.contains("TITLE")) {
|
||||
d->title = properties["TITLE"].front();
|
||||
if(props.contains("TITLE")) {
|
||||
d->title = props["TITLE"].front();
|
||||
oneValueSet.append("TITLE");
|
||||
} else
|
||||
d->title = String::null;
|
||||
d->title.clear();
|
||||
|
||||
if(properties.contains("COMMENT")) {
|
||||
d->comment = properties["COMMENT"].front();
|
||||
if(props.contains("COMMENT")) {
|
||||
d->comment = props["COMMENT"].front();
|
||||
oneValueSet.append("COMMENT");
|
||||
} else
|
||||
d->comment = String::null;
|
||||
d->comment.clear();
|
||||
|
||||
if(properties.contains("TRACKERNAME")) {
|
||||
d->trackerName = properties["TRACKERNAME"].front();
|
||||
if(props.contains("TRACKERNAME")) {
|
||||
d->trackerName = props["TRACKERNAME"].front();
|
||||
oneValueSet.append("TRACKERNAME");
|
||||
} else
|
||||
d->trackerName = String::null;
|
||||
d->trackerName.clear();
|
||||
|
||||
// for each tag that has been set above, remove the first entry in the corresponding
|
||||
// value list. The others will be returned as unsupported by this format.
|
||||
for(StringList::ConstIterator it = oneValueSet.begin(); it != oneValueSet.end(); ++it) {
|
||||
if(properties[*it].size() == 1)
|
||||
properties.erase(*it);
|
||||
for(const auto &entry : std::as_const(oneValueSet)) {
|
||||
if(props[entry].size() == 1)
|
||||
props.erase(entry);
|
||||
else
|
||||
properties[*it].erase( properties[*it].begin() );
|
||||
props[entry].erase(props[entry].begin());
|
||||
}
|
||||
return properties;
|
||||
return props;
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
/***************************************************************************
|
||||
* This library is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU Lesser General Public License version *
|
||||
* it under the terms of the GNU Lesser General Public License version *
|
||||
* 2.1 as published by the Free Software Foundation. *
|
||||
* *
|
||||
* This library is distributed in the hope that it will be useful, but *
|
||||
@@ -15,8 +15,12 @@
|
||||
* *
|
||||
* You should have received a copy of the GNU Lesser General Public *
|
||||
* License along with this library; if not, write to the Free Software *
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, *
|
||||
* MA 02110-1301 USA *
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
|
||||
* 02110-1301 USA *
|
||||
* *
|
||||
* Alternatively, this file is available under the Mozilla Public *
|
||||
* License Version 1.1. You may obtain a copy of the License at *
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef TAGLIB_MODTAG_H
|
||||
@@ -25,9 +29,7 @@
|
||||
#include "tag.h"
|
||||
|
||||
namespace TagLib {
|
||||
|
||||
namespace Mod {
|
||||
|
||||
/*!
|
||||
* Tags for module files (Mod, S3M, IT, XM).
|
||||
*
|
||||
@@ -44,45 +46,48 @@ namespace TagLib {
|
||||
{
|
||||
public:
|
||||
Tag();
|
||||
virtual ~Tag();
|
||||
~Tag() override;
|
||||
|
||||
Tag(const Tag &) = delete;
|
||||
Tag &operator=(const Tag &) = delete;
|
||||
|
||||
/*!
|
||||
* Returns the track name; if no track name is present in the tag
|
||||
* String::null will be returned.
|
||||
*/
|
||||
String title() const;
|
||||
String title() const override;
|
||||
|
||||
/*!
|
||||
* Not supported by module files. Therefore always returns String::null.
|
||||
*/
|
||||
String artist() const;
|
||||
String artist() const override;
|
||||
|
||||
/*!
|
||||
* Not supported by module files. Therefore always returns String::null.
|
||||
*/
|
||||
String album() const;
|
||||
String album() const override;
|
||||
|
||||
/*!
|
||||
* Returns the track comment derived from the instrument/sample/pattern
|
||||
* names; if no comment is present in the tag String::null will be
|
||||
* returned.
|
||||
*/
|
||||
String comment() const;
|
||||
String comment() const override;
|
||||
|
||||
/*!
|
||||
* Not supported by module files. Therefore always returns String::null.
|
||||
*/
|
||||
String genre() const;
|
||||
String genre() const override;
|
||||
|
||||
/*!
|
||||
* Not supported by module files. Therefore always returns 0.
|
||||
*/
|
||||
uint year() const;
|
||||
unsigned int year() const override;
|
||||
|
||||
/*!
|
||||
* Not supported by module files. Therefore always returns 0.
|
||||
*/
|
||||
uint track() const;
|
||||
unsigned int track() const override;
|
||||
|
||||
/*!
|
||||
* Returns the name of the tracker used to create/edit the module file.
|
||||
@@ -101,17 +106,17 @@ namespace TagLib {
|
||||
* Mod 20 characters, S3M 27 characters, IT 25 characters and XM 20
|
||||
* characters.
|
||||
*/
|
||||
void setTitle(const String &title);
|
||||
void setTitle(const String &title) override;
|
||||
|
||||
/*!
|
||||
* Not supported by module files and therefore ignored.
|
||||
*/
|
||||
void setArtist(const String &artist);
|
||||
void setArtist(const String &artist) override;
|
||||
|
||||
/*!
|
||||
* Not supported by module files and therefore ignored.
|
||||
*/
|
||||
void setAlbum(const String &album);
|
||||
void setAlbum(const String &album) override;
|
||||
|
||||
/*!
|
||||
* Sets the comment to \a comment. If \a comment is String::null then
|
||||
@@ -130,22 +135,22 @@ namespace TagLib {
|
||||
* Mod 22 characters, S3M 27 characters, IT 25 characters and XM 22
|
||||
* characters.
|
||||
*/
|
||||
void setComment(const String &comment);
|
||||
void setComment(const String &comment) override;
|
||||
|
||||
/*!
|
||||
* Not supported by module files and therefore ignored.
|
||||
*/
|
||||
void setGenre(const String &genre);
|
||||
void setGenre(const String &genre) override;
|
||||
|
||||
/*!
|
||||
* Not supported by module files and therefore ignored.
|
||||
*/
|
||||
void setYear(uint year);
|
||||
void setYear(unsigned int year) override;
|
||||
|
||||
/*!
|
||||
* Not supported by module files and therefore ignored.
|
||||
*/
|
||||
void setTrack(uint track);
|
||||
void setTrack(unsigned int track) override;
|
||||
|
||||
/*!
|
||||
* Sets the tracker name to \a trackerName. If \a trackerName is
|
||||
@@ -163,7 +168,7 @@ namespace TagLib {
|
||||
* Implements the unified property interface -- export function.
|
||||
* Since the module tag is very limited, the exported map is as well.
|
||||
*/
|
||||
PropertyMap properties() const;
|
||||
PropertyMap properties() const override;
|
||||
|
||||
/*!
|
||||
* Implements the unified property interface -- import function.
|
||||
@@ -173,18 +178,13 @@ namespace TagLib {
|
||||
* all but the first will be contained in the returned map of unsupported
|
||||
* properties.
|
||||
*/
|
||||
PropertyMap setProperties(const PropertyMap &);
|
||||
PropertyMap setProperties(const PropertyMap &) override;
|
||||
|
||||
private:
|
||||
Tag(const Tag &);
|
||||
Tag &operator=(const Tag &);
|
||||
|
||||
class TagPrivate;
|
||||
TagPrivate *d;
|
||||
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
|
||||
std::unique_ptr<TagPrivate> d;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} // namespace Mod
|
||||
} // namespace TagLib
|
||||
#endif
|
||||
|
||||
@@ -23,119 +23,140 @@
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
#include "mp4atom.h"
|
||||
|
||||
#include <climits>
|
||||
#include <utility>
|
||||
|
||||
#include <tdebug.h>
|
||||
#include <tstring.h>
|
||||
#include "mp4atom.h"
|
||||
#include "tdebug.h"
|
||||
|
||||
using namespace TagLib;
|
||||
|
||||
const char *MP4::Atom::containers[11] = {
|
||||
namespace {
|
||||
constexpr std::array containers {
|
||||
"moov", "udta", "mdia", "meta", "ilst",
|
||||
"stbl", "minf", "moof", "traf", "trak",
|
||||
"stsd"
|
||||
};
|
||||
} // namespace
|
||||
|
||||
class MP4::Atom::AtomPrivate
|
||||
{
|
||||
public:
|
||||
explicit AtomPrivate(offset_t ofs) : offset(ofs) {}
|
||||
offset_t offset;
|
||||
offset_t length { 0 };
|
||||
TagLib::ByteVector name;
|
||||
AtomList children;
|
||||
};
|
||||
|
||||
MP4::Atom::Atom(File *file)
|
||||
: d(std::make_unique<AtomPrivate>(file->tell()))
|
||||
{
|
||||
offset = file->tell();
|
||||
d->children.setAutoDelete(true);
|
||||
|
||||
ByteVector header = file->readBlock(8);
|
||||
if (header.size() != 8) {
|
||||
if(header.size() != 8) {
|
||||
// The atom header must be 8 bytes long, otherwise there is either
|
||||
// trailing garbage or the file is truncated
|
||||
debug("MP4: Couldn't read 8 bytes of data for atom header");
|
||||
length = 0;
|
||||
d->length = 0;
|
||||
file->seek(0, File::End);
|
||||
return;
|
||||
}
|
||||
|
||||
length = header.toUInt();
|
||||
d->length = header.toUInt();
|
||||
|
||||
if(length == 1) {
|
||||
if(d->length == 0) {
|
||||
// The last atom which extends to the end of the file.
|
||||
d->length = file->length() - d->offset;
|
||||
}
|
||||
else if(d->length == 1) {
|
||||
// The atom has a 64-bit length.
|
||||
const long long longLength = file->readBlock(8).toLongLong();
|
||||
if(sizeof(long) == sizeof(long long)) {
|
||||
length = static_cast<long>(longLength);
|
||||
if(longLength <= LONG_MAX) {
|
||||
// The actual length fits in long. That's always the case if long is 64-bit.
|
||||
d->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;
|
||||
}
|
||||
debug("MP4: 64-bit atoms are not supported");
|
||||
d->length = 0;
|
||||
file->seek(0, File::End);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if(length < 8) {
|
||||
|
||||
if(d->length < 8 || d->length > file->length() - d->offset) {
|
||||
debug("MP4: Invalid atom size");
|
||||
length = 0;
|
||||
d->length = 0;
|
||||
file->seek(0, File::End);
|
||||
return;
|
||||
}
|
||||
|
||||
name = header.mid(4, 4);
|
||||
d->name = header.mid(4, 4);
|
||||
for(int i = 0; i < 4; ++i) {
|
||||
const char ch = d->name.at(i);
|
||||
if((ch < ' ' || ch > '~') && ch != '\251') {
|
||||
debug("MP4: Invalid atom type");
|
||||
d->length = 0;
|
||||
file->seek(0, File::End);
|
||||
}
|
||||
}
|
||||
|
||||
for(int i = 0; i < numContainers; i++) {
|
||||
if(name == containers[i]) {
|
||||
if(name == "meta") {
|
||||
file->seek(4, File::Current);
|
||||
for(auto c : containers) {
|
||||
if(d->name == c) {
|
||||
if(d->name == "meta") {
|
||||
offset_t posAfterMeta = file->tell();
|
||||
static constexpr std::array metaChildrenNames {
|
||||
"hdlr", "ilst", "mhdr", "ctry", "lang"
|
||||
};
|
||||
// meta is not a full atom (i.e. not followed by version, flags). It
|
||||
// is followed by the size and type of the first child atom.
|
||||
auto metaIsFullAtom = std::none_of(metaChildrenNames.begin(), metaChildrenNames.end(),
|
||||
[nextSize = file->readBlock(8).mid(4, 4)](const auto &child) { return nextSize == child; });
|
||||
// Only skip next four bytes, which contain version and flags, if meta
|
||||
// is a full atom.
|
||||
file->seek(posAfterMeta + (metaIsFullAtom ? 4 : 0));
|
||||
}
|
||||
else if(name == "stsd") {
|
||||
else if(d->name == "stsd") {
|
||||
file->seek(8, File::Current);
|
||||
}
|
||||
while(file->tell() < offset + length) {
|
||||
MP4::Atom *child = new MP4::Atom(file);
|
||||
children.append(child);
|
||||
if (child->length == 0)
|
||||
while(file->tell() < d->offset + d->length) {
|
||||
auto child = new MP4::Atom(file);
|
||||
d->children.append(child);
|
||||
if(child->d->length == 0)
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
file->seek(offset + length);
|
||||
file->seek(d->offset + d->length);
|
||||
}
|
||||
|
||||
MP4::Atom::~Atom()
|
||||
{
|
||||
for(unsigned int i = 0; i < children.size(); i++) {
|
||||
delete children[i];
|
||||
}
|
||||
children.clear();
|
||||
}
|
||||
MP4::Atom::~Atom() = default;
|
||||
|
||||
MP4::Atom *
|
||||
MP4::Atom::find(const char *name1, const char *name2, const char *name3, const char *name4)
|
||||
{
|
||||
if(name1 == 0) {
|
||||
if(name1 == nullptr) {
|
||||
return this;
|
||||
}
|
||||
for(unsigned int i = 0; i < children.size(); i++) {
|
||||
if(children[i]->name == name1) {
|
||||
return children[i]->find(name2, name3, name4);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
auto it = std::find_if(d->children.cbegin(), d->children.cend(),
|
||||
[&name1](Atom *child) { return child->d->name == name1; });
|
||||
return it != d->children.cend() ? (*it)->find(name2, name3, name4) : nullptr;
|
||||
}
|
||||
|
||||
MP4::AtomList
|
||||
MP4::Atom::findall(const char *name, bool recursive)
|
||||
{
|
||||
MP4::AtomList result;
|
||||
for(unsigned int i = 0; i < children.size(); i++) {
|
||||
if(children[i]->name == name) {
|
||||
result.append(children[i]);
|
||||
for(const auto &child : std::as_const(d->children)) {
|
||||
if(child->d->name == name) {
|
||||
result.append(child);
|
||||
}
|
||||
if(recursive) {
|
||||
result.append(children[i]->findall(name, recursive));
|
||||
result.append(child->findall(name, recursive));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
@@ -145,61 +166,140 @@ bool
|
||||
MP4::Atom::path(MP4::AtomList &path, const char *name1, const char *name2, const char *name3)
|
||||
{
|
||||
path.append(this);
|
||||
if(name1 == 0) {
|
||||
if(name1 == nullptr) {
|
||||
return true;
|
||||
}
|
||||
for(unsigned int i = 0; i < children.size(); i++) {
|
||||
if(children[i]->name == name1) {
|
||||
return children[i]->path(path, name2, name3);
|
||||
}
|
||||
auto it = std::find_if(d->children.cbegin(), d->children.cend(),
|
||||
[&name1](Atom *child) { return child->d->name == name1; });
|
||||
return it != d->children.cend() ? (*it)->path(path, name2, name3) : false;
|
||||
}
|
||||
|
||||
void MP4::Atom::addToOffset(offset_t delta)
|
||||
{
|
||||
d->offset += delta;
|
||||
}
|
||||
|
||||
void MP4::Atom::prependChild(Atom *atom)
|
||||
{
|
||||
d->children.prepend(atom);
|
||||
}
|
||||
|
||||
bool MP4::Atom::removeChild(Atom *meta)
|
||||
{
|
||||
auto it = d->children.find(meta);
|
||||
if(it != d->children.end()) {
|
||||
d->children.erase(it);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
MP4::Atoms::Atoms(File *file)
|
||||
offset_t MP4::Atom::offset() const
|
||||
{
|
||||
return d->offset;
|
||||
}
|
||||
|
||||
offset_t MP4::Atom::length() const
|
||||
{
|
||||
return d->length;
|
||||
}
|
||||
|
||||
const ByteVector &MP4::Atom::name() const
|
||||
{
|
||||
return d->name;
|
||||
}
|
||||
|
||||
const MP4::AtomList &MP4::Atom::children() const
|
||||
{
|
||||
return d->children;
|
||||
}
|
||||
|
||||
|
||||
class MP4::Atoms::AtomsPrivate
|
||||
{
|
||||
public:
|
||||
AtomList atoms;
|
||||
};
|
||||
|
||||
MP4::Atoms::Atoms(File *file) :
|
||||
d(std::make_unique<AtomsPrivate>())
|
||||
{
|
||||
d->atoms.setAutoDelete(true);
|
||||
|
||||
file->seek(0, File::End);
|
||||
long end = file->tell();
|
||||
offset_t end = file->tell();
|
||||
file->seek(0);
|
||||
while(file->tell() + 8 <= end) {
|
||||
MP4::Atom *atom = new MP4::Atom(file);
|
||||
atoms.append(atom);
|
||||
if (atom->length == 0)
|
||||
auto atom = new MP4::Atom(file);
|
||||
d->atoms.append(atom);
|
||||
if (atom->length() == 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
MP4::Atoms::~Atoms()
|
||||
{
|
||||
for(unsigned int i = 0; i < atoms.size(); i++) {
|
||||
delete atoms[i];
|
||||
}
|
||||
atoms.clear();
|
||||
}
|
||||
MP4::Atoms::~Atoms() = default;
|
||||
|
||||
MP4::Atom *
|
||||
MP4::Atoms::find(const char *name1, const char *name2, const char *name3, const char *name4)
|
||||
{
|
||||
for(unsigned int i = 0; i < atoms.size(); i++) {
|
||||
if(atoms[i]->name == name1) {
|
||||
return atoms[i]->find(name2, name3, name4);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
auto it = std::find_if(d->atoms.cbegin(), d->atoms.cend(),
|
||||
[&name1](Atom *atom) { return atom->name() == name1; });
|
||||
return it != d->atoms.cend() ? (*it)->find(name2, name3, name4) : nullptr;
|
||||
}
|
||||
|
||||
MP4::AtomList
|
||||
MP4::Atoms::path(const char *name1, const char *name2, const char *name3, const char *name4)
|
||||
{
|
||||
MP4::AtomList path;
|
||||
for(unsigned int i = 0; i < atoms.size(); i++) {
|
||||
if(atoms[i]->name == name1) {
|
||||
if(!atoms[i]->path(path, name2, name3, name4)) {
|
||||
path.clear();
|
||||
}
|
||||
return path;
|
||||
auto it = std::find_if(d->atoms.cbegin(), d->atoms.cend(),
|
||||
[&name1](Atom *atom) { return atom->name() == name1; });
|
||||
if(it != d->atoms.cend()) {
|
||||
if(!(*it)->path(path, name2, name3, name4)) {
|
||||
path.clear();
|
||||
}
|
||||
return path;
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
bool checkValid(const MP4::AtomList &list)
|
||||
{
|
||||
return std::none_of(list.begin(), list.end(),
|
||||
[](const auto &a) { return a->length() == 0 || !checkValid(a->children()); });
|
||||
}
|
||||
} // namespace
|
||||
|
||||
bool MP4::Atoms::checkRootLevelAtoms()
|
||||
{
|
||||
bool moovValid = false;
|
||||
for(auto it = d->atoms.begin(); it != d->atoms.end(); ++it) {
|
||||
bool invalid = (*it)->length() == 0 || !checkValid((*it)->children());
|
||||
if(!moovValid && !invalid && (*it)->name() == "moov") {
|
||||
moovValid = true;
|
||||
}
|
||||
if(invalid) {
|
||||
if(moovValid && (*it)->name() != "moof") {
|
||||
// Only the root level atoms "moov" and (if present) "moof" are
|
||||
// modified. If they are valid, ignore following invalid root level
|
||||
// atoms as trailing garbage.
|
||||
while(it != d->atoms.end()) {
|
||||
delete *it;
|
||||
it = d->atoms.erase(it);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
const MP4::AtomList &MP4::Atoms::atoms() const
|
||||
{
|
||||
return d->atoms;
|
||||
}
|
||||
|
||||
@@ -25,23 +25,22 @@
|
||||
|
||||
// This file is not part of the public API!
|
||||
|
||||
#ifndef DO_NOT_DOCUMENT
|
||||
|
||||
#ifndef TAGLIB_MP4ATOM_H
|
||||
#define TAGLIB_MP4ATOM_H
|
||||
|
||||
#include <array>
|
||||
|
||||
#include "tfile.h"
|
||||
#include "tlist.h"
|
||||
|
||||
#ifndef DO_NOT_DOCUMENT
|
||||
|
||||
namespace TagLib {
|
||||
|
||||
namespace MP4 {
|
||||
|
||||
class Atom;
|
||||
typedef TagLib::List<Atom *> AtomList;
|
||||
using AtomList = TagLib::List<Atom *>;
|
||||
|
||||
enum AtomDataType
|
||||
{
|
||||
enum AtomDataType {
|
||||
TypeImplicit = 0, // for use with tags for which no type needs to be indicated because only one type is allowed
|
||||
TypeUTF8 = 1, // without any count or null terminator
|
||||
TypeUTF16 = 2, // also known as UTF-16BE
|
||||
@@ -66,45 +65,59 @@ namespace TagLib {
|
||||
};
|
||||
|
||||
struct AtomData {
|
||||
AtomData(AtomDataType type, ByteVector data) : type(type), locale(0), data(data) {}
|
||||
AtomData(AtomDataType type, const ByteVector &data) :
|
||||
type(type), data(data) { }
|
||||
AtomDataType type;
|
||||
int locale;
|
||||
int locale { 0 };
|
||||
ByteVector data;
|
||||
};
|
||||
|
||||
typedef TagLib::List<AtomData> AtomDataList;
|
||||
using AtomDataList = TagLib::List<AtomData>;
|
||||
|
||||
class Atom
|
||||
class TAGLIB_EXPORT Atom
|
||||
{
|
||||
public:
|
||||
Atom(File *file);
|
||||
~Atom();
|
||||
Atom *find(const char *name1, const char *name2 = 0, const char *name3 = 0, const char *name4 = 0);
|
||||
bool path(AtomList &path, const char *name1, const char *name2 = 0, const char *name3 = 0);
|
||||
AtomList findall(const char *name, bool recursive = false);
|
||||
long offset;
|
||||
long length;
|
||||
TagLib::ByteVector name;
|
||||
AtomList children;
|
||||
Atom(File *file);
|
||||
~Atom();
|
||||
Atom(const Atom &) = delete;
|
||||
Atom &operator=(const Atom &) = delete;
|
||||
Atom *find(const char *name1, const char *name2 = nullptr, const char *name3 = nullptr, const char *name4 = nullptr);
|
||||
bool path(AtomList &path, const char *name1, const char *name2 = nullptr, const char *name3 = nullptr);
|
||||
AtomList findall(const char *name, bool recursive = false);
|
||||
void addToOffset(offset_t delta);
|
||||
void prependChild(Atom *atom);
|
||||
bool removeChild(Atom *meta);
|
||||
offset_t offset() const;
|
||||
offset_t length() const;
|
||||
const ByteVector &name() const;
|
||||
const AtomList &children() const;
|
||||
|
||||
private:
|
||||
static const int numContainers = 11;
|
||||
static const char *containers[11];
|
||||
class AtomPrivate;
|
||||
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
|
||||
std::unique_ptr<AtomPrivate> d;
|
||||
};
|
||||
|
||||
//! Root-level atoms
|
||||
class Atoms
|
||||
class TAGLIB_EXPORT Atoms
|
||||
{
|
||||
public:
|
||||
Atoms(File *file);
|
||||
~Atoms();
|
||||
Atom *find(const char *name1, const char *name2 = 0, const char *name3 = 0, const char *name4 = 0);
|
||||
AtomList path(const char *name1, const char *name2 = 0, const char *name3 = 0, const char *name4 = 0);
|
||||
AtomList atoms;
|
||||
Atoms(File *file);
|
||||
~Atoms();
|
||||
Atoms(const Atoms &) = delete;
|
||||
Atoms &operator=(const Atoms &) = delete;
|
||||
Atom *find(const char *name1, const char *name2 = nullptr, const char *name3 = nullptr, const char *name4 = nullptr);
|
||||
AtomList path(const char *name1, const char *name2 = nullptr, const char *name3 = nullptr, const char *name4 = nullptr);
|
||||
bool checkRootLevelAtoms();
|
||||
const AtomList &atoms() const;
|
||||
|
||||
private:
|
||||
class AtomsPrivate;
|
||||
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
|
||||
std::unique_ptr<AtomsPrivate> d;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace MP4
|
||||
} // namespace TagLib
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user