mirror of
https://github.com/taglib/taglib.git
synced 2026-06-07 06:50:32 -04:00
Compare commits
1697 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
74d93db166 | ||
|
|
397b6c1de3 | ||
|
|
51f431c96a | ||
|
|
11e3eb05bd | ||
|
|
2c01b63433 | ||
|
|
d83d751443 | ||
|
|
f4e7a742c3 | ||
|
|
68a514f4a0 | ||
|
|
607cd5bdf4 | ||
|
|
b625be5690 | ||
|
|
d2bf7e72d2 | ||
|
|
280d6306d2 | ||
|
|
aef78068b3 | ||
|
|
81815e1091 | ||
|
|
b9122afaca | ||
|
|
1d3a375765 | ||
|
|
241b3b2921 | ||
|
|
48104959b2 | ||
|
|
c817cc0a47 | ||
|
|
7a5a10102e | ||
|
|
d47d28f0f8 | ||
|
|
3566b00596 | ||
|
|
98bc68d16e | ||
|
|
8d02484804 | ||
|
|
cfade6d082 | ||
|
|
f7ab162514 | ||
|
|
5a6f1f96f8 | ||
|
|
6fa3db1e51 | ||
|
|
4546c00417 | ||
|
|
f94843614f | ||
|
|
975eaac3ca | ||
|
|
6d019a894c | ||
|
|
6342f00e8b | ||
|
|
b4e79a4a27 | ||
|
|
80837485cf | ||
|
|
47e9b9a17c | ||
|
|
8c7d3368da | ||
|
|
b4a4b42254 | ||
|
|
70c6a0c75f | ||
|
|
c67e434939 | ||
|
|
5d44f3eeac | ||
|
|
9c042984d2 | ||
|
|
49be371caa | ||
|
|
cbb3de6836 | ||
|
|
11efd2dd10 | ||
|
|
ab31d11c8d | ||
|
|
3c917caf52 | ||
|
|
fa96a6458e | ||
|
|
e831f0929f | ||
|
|
7d86716194 | ||
|
|
eb749ac55b | ||
|
|
148cc9a921 | ||
|
|
88d6b18b4f | ||
|
|
d61a333f27 | ||
|
|
e73517d058 | ||
|
|
6563ceaafa | ||
|
|
c57431e903 | ||
|
|
42dcfec86b | ||
|
|
d48f02030d | ||
|
|
3ccc390155 | ||
|
|
fbbead3efd | ||
|
|
ee1931b811 | ||
|
|
648f5e5882 | ||
|
|
c1c60ebeea | ||
|
|
3bc0ea0ecb | ||
|
|
225c73e181 | ||
|
|
90f62a3c94 | ||
|
|
5b6f9ef848 | ||
|
|
e3de03501f | ||
|
|
1bd0d711ca | ||
|
|
7c85fcaa81 | ||
|
|
cbe54d2f40 | ||
|
|
c4ed590032 | ||
|
|
f3fb4d83a4 | ||
|
|
3d4428726e | ||
|
|
ebf4c5bbb1 | ||
|
|
20cec27ac0 | ||
|
|
99bc87ccff | ||
|
|
7951f572b5 | ||
|
|
59ff35772e | ||
|
|
3784628155 | ||
|
|
e60df53152 | ||
|
|
1ae8e18db5 | ||
|
|
0896fb9092 | ||
|
|
920d97606b | ||
|
|
0d2c31b102 | ||
|
|
c8c4e5faec | ||
|
|
0ebb14b855 | ||
|
|
89af92333c | ||
|
|
b356fabe12 | ||
|
|
8a65068f3b | ||
|
|
e1ac724cfe | ||
|
|
a08acdcf23 | ||
|
|
1799b98c17 | ||
|
|
e49390c7c5 | ||
|
|
7bcfb96098 | ||
|
|
580b0b0c82 | ||
|
|
5fc3e5c192 | ||
|
|
570b40bdcd | ||
|
|
c3d73a26ff | ||
|
|
790815bcf4 | ||
|
|
1a5c417558 | ||
|
|
d87b2dad48 | ||
|
|
613355665c | ||
|
|
dfe2aa5253 | ||
|
|
5d921c6325 | ||
|
|
710522e6e1 | ||
|
|
6b17aa3694 | ||
|
|
b4f77a4d52 | ||
|
|
c907d8b273 | ||
|
|
98175168f3 | ||
|
|
73aff544b3 | ||
|
|
9cbb6615d5 | ||
|
|
8b7b48cc9b | ||
|
|
be8d71dad8 | ||
|
|
6abbe579a2 | ||
|
|
208fc93aaa | ||
|
|
769feafbe0 | ||
|
|
dfef09f134 | ||
|
|
3a003c1229 | ||
|
|
8b9a260d18 | ||
|
|
c2eb6b59b5 | ||
|
|
99ba7635be | ||
|
|
82b5ded8ee | ||
|
|
0318201fbd | ||
|
|
6c1ba88eab | ||
|
|
fb0f7dfa57 | ||
|
|
c5d798a50d | ||
|
|
ef013b76db | ||
|
|
a6dbc70644 | ||
|
|
9a026976ae | ||
|
|
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 | ||
|
|
9f697fce8e | ||
|
|
320d0f5ad7 | ||
|
|
77087cf865 | ||
|
|
8b4a27beb4 | ||
|
|
998ebf4ce6 | ||
|
|
ccaf650214 | ||
|
|
9fad0b28a5 | ||
|
|
153820bf12 | ||
|
|
29be00dc59 | ||
|
|
ab30ec3a6f | ||
|
|
0a90687805 | ||
|
|
e750cb491d | ||
|
|
1f7715a5da | ||
|
|
8e7504a66f | ||
|
|
9ad5bb1d62 | ||
|
|
5ca4cd2f52 | ||
|
|
70f8fb1bae | ||
|
|
030e177640 | ||
|
|
3bb0b11bff | ||
|
|
60558d6a4a | ||
|
|
12f59d774e | ||
|
|
e2466a72f8 | ||
|
|
9666b64f28 | ||
|
|
15a1505880 | ||
|
|
fbd3b71690 | ||
|
|
6a76f491f8 | ||
|
|
6d925da75e | ||
|
|
e178875b40 | ||
|
|
bf45cfd84a | ||
|
|
b49e3e5620 | ||
|
|
fa6f33e552 | ||
|
|
021c539002 | ||
|
|
aedfeba66b | ||
|
|
29ed265281 | ||
|
|
b6adb1f108 | ||
|
|
c7231c58a3 | ||
|
|
c5cf9b93bc | ||
|
|
1bb06b1f7a | ||
|
|
35aa6c4e84 | ||
|
|
54cea3edc3 | ||
|
|
0178d47c85 | ||
|
|
71bc17b5e6 | ||
|
|
ac38f4ade1 | ||
|
|
b9f898698d | ||
|
|
bd564546f4 | ||
|
|
d90617959b | ||
|
|
bc106ad81e | ||
|
|
a23f808627 | ||
|
|
e4cd963b12 | ||
|
|
eff92fed98 | ||
|
|
80441ff754 | ||
|
|
35d5ba4eff | ||
|
|
edbafdbd88 | ||
|
|
98ac8ba569 | ||
|
|
173c58cf49 | ||
|
|
ec3d050adc | ||
|
|
19a7e45997 | ||
|
|
49563a9cd0 | ||
|
|
1abe61640a | ||
|
|
65a6572299 | ||
|
|
46e74c9391 | ||
|
|
7c17d32b3a | ||
|
|
bfe4ec5df5 | ||
|
|
81b7d0046e | ||
|
|
5990c72a01 | ||
|
|
018e969026 | ||
|
|
04ec7eae25 | ||
|
|
b6e7bb2c84 | ||
|
|
f112d538ea | ||
|
|
ac5ef0291c | ||
|
|
8fa86162c7 | ||
|
|
fa17b4da6b | ||
|
|
076e845912 | ||
|
|
f830177b3b | ||
|
|
0650dc77a1 | ||
|
|
c0f537a155 | ||
|
|
44d9f2bf25 | ||
|
|
89e6ad96a4 | ||
|
|
aa1dd0278d | ||
|
|
13dab99af0 | ||
|
|
03ec83ecca | ||
|
|
ce02910c6b | ||
|
|
f25e30d33f | ||
|
|
4dcccfbd6a | ||
|
|
7ca1b0c4d3 | ||
|
|
11f4e4e1ca | ||
|
|
9759bd2dd7 | ||
|
|
b28784538a | ||
|
|
fdea096c8d | ||
|
|
a9d030544a | ||
|
|
8344c4d7f8 | ||
|
|
8b1e872f81 | ||
|
|
6a778751ee | ||
|
|
4328a830f9 | ||
|
|
ae99cbe64e | ||
|
|
692ce897cb | ||
|
|
0ed1c29acc | ||
|
|
3c59b7858a | ||
|
|
2c9f63d9b0 | ||
|
|
0b43b7136d | ||
|
|
1e752a1e8f | ||
|
|
29a31859ff | ||
|
|
0b01461d50 | ||
|
|
a77abedf63 | ||
|
|
21412e2ba2 | ||
|
|
dbf9644c8d | ||
|
|
592e14f950 | ||
|
|
1fb3727c4c | ||
|
|
0501fbdd72 | ||
|
|
5235abc498 | ||
|
|
d944009904 | ||
|
|
f729f863cd | ||
|
|
c715ec09e4 | ||
|
|
4f621140ce | ||
|
|
bd251aed37 | ||
|
|
34ab65aa57 | ||
|
|
7f0c547ba6 | ||
|
|
0ff38ed52b | ||
|
|
2b1116cec1 | ||
|
|
88947e7a48 | ||
|
|
5ad69a81dc | ||
|
|
c9963af848 | ||
|
|
f38e32163e | ||
|
|
c69364d831 | ||
|
|
3142330bee | ||
|
|
9b849c5da8 | ||
|
|
a197f45ca8 | ||
|
|
6dcecf0e71 | ||
|
|
47800d1000 | ||
|
|
cc8c3cd1fd | ||
|
|
58994e330e | ||
|
|
572afd437d | ||
|
|
1eef4177e7 | ||
|
|
64fac517ed | ||
|
|
f79c766ba4 | ||
|
|
6f944b0291 | ||
|
|
04bee3faec | ||
|
|
e8c1a11730 | ||
|
|
d8e8ec69fe | ||
|
|
6d0712c8df | ||
|
|
00b2f6a386 | ||
|
|
a3dccdc7a3 | ||
|
|
b698c73690 | ||
|
|
801c9db810 | ||
|
|
409b135dd5 | ||
|
|
4dd14d4d73 | ||
|
|
44e6419644 | ||
|
|
467658e463 | ||
|
|
3fcb21642c | ||
|
|
472ce9f42c | ||
|
|
be33340383 | ||
|
|
91ed3548f1 | ||
|
|
b56f4c4372 | ||
|
|
2155b4fd50 | ||
|
|
e605e96835 | ||
|
|
dfee7020da | ||
|
|
e90b5e5f2f | ||
|
|
642baca4ed | ||
|
|
da01fa5745 | ||
|
|
78c70cf5bb | ||
|
|
8f6af3f020 | ||
|
|
22f250eaa4 | ||
|
|
eb73612a2b | ||
|
|
9c8e36d3be | ||
|
|
ed25204d75 | ||
|
|
03fd0a3ead | ||
|
|
aede4ac851 | ||
|
|
3823afcc87 | ||
|
|
4dba88fa31 | ||
|
|
f3d8100c7b | ||
|
|
b2c79bc084 | ||
|
|
f82be353b4 | ||
|
|
9ec6d28239 | ||
|
|
3a1c784eec | ||
|
|
4a014c8113 | ||
|
|
5d77553759 | ||
|
|
da14f67e2c | ||
|
|
9226fa76b3 | ||
|
|
ff36648e92 | ||
|
|
447a4739c5 | ||
|
|
f15fe869a5 | ||
|
|
125d887b85 | ||
|
|
9a8e41b9d6 | ||
|
|
68ef160dbc | ||
|
|
6d71bdf8b7 | ||
|
|
b37eaace15 | ||
|
|
c1c70edb76 | ||
|
|
75159d5d8a | ||
|
|
46862bf537 | ||
|
|
2b260fd2e8 | ||
|
|
b1a35a8b31 | ||
|
|
88ef556d4e | ||
|
|
00c6e7ea79 | ||
|
|
0c0f123a08 | ||
|
|
48311cca14 | ||
|
|
db90f4b358 | ||
|
|
b5d65edab7 | ||
|
|
a06d55ae6f | ||
|
|
c5f2e9342d | ||
|
|
2d778d5a0a | ||
|
|
0aa75b2e8d | ||
|
|
618a939c56 | ||
|
|
2337fbcfc6 | ||
|
|
9da20a8a52 | ||
|
|
4129b919c1 | ||
|
|
6d6f544010 | ||
|
|
93da3ba6d9 | ||
|
|
e064b88049 | ||
|
|
3094540a4b | ||
|
|
8e21dcc3d4 | ||
|
|
0ea22348cb | ||
|
|
48ca54de1f | ||
|
|
54de66f275 | ||
|
|
d4b0ba2a7a | ||
|
|
abc5743222 | ||
|
|
525396d9c2 | ||
|
|
bb9679b51a | ||
|
|
e44cba56b5 | ||
|
|
e4cf012522 | ||
|
|
a37d423ea8 | ||
|
|
88a4cf34b8 | ||
|
|
81261dd128 | ||
|
|
3bea9f6bee | ||
|
|
9cc2d7cfee | ||
|
|
fe1bf015bb | ||
|
|
287078566f | ||
|
|
60966518e2 | ||
|
|
2268efb49e | ||
|
|
29f535dc8d | ||
|
|
592522d34c | ||
|
|
6b9e4e4403 | ||
|
|
089e44f3db | ||
|
|
bd3c4dc1b2 | ||
|
|
bba562b557 | ||
|
|
451d23ca37 | ||
|
|
d248f77ab9 | ||
|
|
727a11573a | ||
|
|
34da0c0dab | ||
|
|
7316cd331d | ||
|
|
3a977c55c4 | ||
|
|
a094ce7dd2 | ||
|
|
941efcba18 | ||
|
|
ffb543acbb | ||
|
|
fc24b3d22b | ||
|
|
a9e064c58e | ||
|
|
1a5d89d29e | ||
|
|
650b581f9e | ||
|
|
0739dd232a | ||
|
|
41a250a15d | ||
|
|
8da0013482 | ||
|
|
0288059495 | ||
|
|
6e40361c0e | ||
|
|
c2570fe115 | ||
|
|
bd91da55ba | ||
|
|
3a3a6a6fda | ||
|
|
64c43b0d3f | ||
|
|
db2dfa9515 | ||
|
|
0069debf0c | ||
|
|
69563d83a0 | ||
|
|
60775306ea | ||
|
|
4f77420248 | ||
|
|
b021ed44e9 | ||
|
|
1f99c93a61 | ||
|
|
a924ca0db7 | ||
|
|
b5c0ab58ba | ||
|
|
e43c1a3c09 | ||
|
|
1e9529380d | ||
|
|
ff8443f33a | ||
|
|
62ab41fa07 | ||
|
|
00e3504264 | ||
|
|
852da79899 | ||
|
|
ab047f6054 | ||
|
|
5bad35c4cb | ||
|
|
8d708c03e1 | ||
|
|
f9a0b50830 | ||
|
|
8491266b12 | ||
|
|
066b5aa57e | ||
|
|
5f0a7da481 | ||
|
|
d33d684fab | ||
|
|
4c4be0a263 | ||
|
|
f476cf2b45 | ||
|
|
68c0b0591b | ||
|
|
8fccaf30d2 | ||
|
|
b69973bcf2 | ||
|
|
30eac7569f | ||
|
|
f6081ed32e | ||
|
|
0839a23902 | ||
|
|
f35a279dd1 | ||
|
|
e463d14f2e | ||
|
|
829ae2112a | ||
|
|
65664e6855 | ||
|
|
2193d6dd84 | ||
|
|
ef786188ad | ||
|
|
2b5ee8deb9 | ||
|
|
ed09c9cf87 | ||
|
|
9d91610fc0 | ||
|
|
aed689c145 | ||
|
|
c6a63a3a2f | ||
|
|
2b0a540228 | ||
|
|
2685ec1842 | ||
|
|
fdea6dcd40 | ||
|
|
ed253d3691 | ||
|
|
57729b834a | ||
|
|
caa53e8de5 | ||
|
|
31982660c8 | ||
|
|
bd7b8cc36a | ||
|
|
bc9bbfe3fa | ||
|
|
7adea3df22 | ||
|
|
5ebb2ece80 | ||
|
|
0d2e01df61 | ||
|
|
695fb5ec16 | ||
|
|
3170d47ec3 | ||
|
|
79b7c14129 | ||
|
|
977fb2aeb0 | ||
|
|
3b8c7d4e3a | ||
|
|
be33389884 | ||
|
|
59ae61de6e | ||
|
|
16ac2cd240 | ||
|
|
61543432c0 | ||
|
|
4a9614bfc3 | ||
|
|
1a917a38cd | ||
|
|
735e17f504 | ||
|
|
0731bc7b2e | ||
|
|
fb1c744daf | ||
|
|
1bc5acd7a7 | ||
|
|
f30f5ccce5 | ||
|
|
421eb5e97e | ||
|
|
5e60ec48e9 | ||
|
|
68eca440c9 | ||
|
|
7b03403791 | ||
|
|
d80c4c96c3 | ||
|
|
73b9b9b58d | ||
|
|
072851869a | ||
|
|
269e78f1a0 | ||
|
|
71acf3b6f7 | ||
|
|
e41dc68a6b | ||
|
|
cfad951442 | ||
|
|
23ffb24472 | ||
|
|
a8bfcd81be | ||
|
|
bd7419f0bd | ||
|
|
71c1ce375f | ||
|
|
cff8d22818 | ||
|
|
0051351ebb | ||
|
|
82315276db | ||
|
|
8ea07be47e | ||
|
|
eae4605e6e | ||
|
|
b6289c64dd | ||
|
|
e6d7dd08f2 | ||
|
|
ee283789b7 | ||
|
|
205569c8d2 | ||
|
|
4b67beffc8 | ||
|
|
b7a514886d | ||
|
|
0e6d8617ae | ||
|
|
dfbaee4103 | ||
|
|
f29c5f45f8 | ||
|
|
b1a8205561 | ||
|
|
2971891c69 | ||
|
|
d5e3d6ea7c | ||
|
|
20ce66f98b | ||
|
|
aff3351a25 | ||
|
|
387890d956 | ||
|
|
8765d40c2c | ||
|
|
9bb0eb7ee9 | ||
|
|
d34c922c75 | ||
|
|
3c727e091e | ||
|
|
a055933e10 | ||
|
|
20c0aac309 | ||
|
|
929829b2b5 | ||
|
|
1944588595 | ||
|
|
31a74927e4 | ||
|
|
972aa1feef | ||
|
|
c23ee5331f | ||
|
|
0f605be198 | ||
|
|
590cb29f99 | ||
|
|
286fd2abec | ||
|
|
f76d76728d | ||
|
|
80f5436370 | ||
|
|
043d2bbef5 | ||
|
|
cf59d51d93 | ||
|
|
9569b99e4c | ||
|
|
590405d878 | ||
|
|
c03b91aed3 | ||
|
|
ee2908a6cf | ||
|
|
e29f1d39e7 | ||
|
|
b509106031 | ||
|
|
47cd6fad4f | ||
|
|
fb24dfe7cf | ||
|
|
f42263e04a | ||
|
|
43872f362d | ||
|
|
73d3436570 | ||
|
|
1e50b8dc25 | ||
|
|
4a585c5258 | ||
|
|
5f738a9819 | ||
|
|
d7995b807f | ||
|
|
3e60e339a4 | ||
|
|
5feabe0988 | ||
|
|
29a5129096 | ||
|
|
fadb57e4cd | ||
|
|
653b631aea | ||
|
|
cb3abf15ad | ||
|
|
29377fc8f1 | ||
|
|
663f10d51d | ||
|
|
a192db07c0 | ||
|
|
459adc69ad | ||
|
|
500b3e630b | ||
|
|
5c5c89e8d9 | ||
|
|
140fb2b3f6 | ||
|
|
98ed58f910 | ||
|
|
7d99b8276a | ||
|
|
17841e89ae | ||
|
|
c5f9258462 | ||
|
|
3a1040d55b | ||
|
|
0864634ea6 | ||
|
|
f27e0aaecf | ||
|
|
ecc768d459 | ||
|
|
fabc411cb9 | ||
|
|
2d528d844a | ||
|
|
c65753c766 | ||
|
|
7e10b13962 | ||
|
|
05e26119f7 | ||
|
|
95a59c416b | ||
|
|
3f557be608 | ||
|
|
9ca7877587 | ||
|
|
a93ee10134 | ||
|
|
1723e9b18a | ||
|
|
16b8688c1a | ||
|
|
3b60af2c0b | ||
|
|
eba99c3a70 | ||
|
|
6bcd422afa | ||
|
|
c357d293a1 | ||
|
|
cfb43223dc | ||
|
|
72cd379f30 | ||
|
|
a3d724af27 | ||
|
|
05e0081414 | ||
|
|
b658f95e06 | ||
|
|
2758aed34d | ||
|
|
12b25868b7 | ||
|
|
1deff52d95 | ||
|
|
69eaa75f64 | ||
|
|
3e0c73bdbc | ||
|
|
9e94f8bb6c | ||
|
|
aa49723b48 | ||
|
|
167513ae57 | ||
|
|
d0f3e9b186 | ||
|
|
4fc020e0d3 | ||
|
|
db147e672e | ||
|
|
c45a0694f9 | ||
|
|
584bbc7c78 | ||
|
|
10b804e0ae | ||
|
|
4f8ab4b63f | ||
|
|
5593c251c8 | ||
|
|
7d7a7be115 | ||
|
|
f733077917 | ||
|
|
670493f97f | ||
|
|
d4adc22922 | ||
|
|
34f8cd8a90 | ||
|
|
4a7d31c87b | ||
|
|
3bf30af66c | ||
|
|
ee9720a997 | ||
|
|
69e58b5f3f | ||
|
|
de17843d8c | ||
|
|
944143a91b | ||
|
|
1ce5385e30 | ||
|
|
5ed2d88f78 | ||
|
|
aa61823432 | ||
|
|
2f7af42092 | ||
|
|
2d5abd46d2 | ||
|
|
9b5869ea37 | ||
|
|
02ebd0bcbe | ||
|
|
5693ab0403 | ||
|
|
27000438f5 | ||
|
|
a6f759cc9a | ||
|
|
a175b8356b | ||
|
|
873df184fe | ||
|
|
079e3e0b87 | ||
|
|
95776b5905 | ||
|
|
dcfb71bcb4 | ||
|
|
f2c9ed4f36 | ||
|
|
2407933087 | ||
|
|
2af43ec8bf | ||
|
|
85d76a2428 | ||
|
|
5a4a05d9bc | ||
|
|
a2188e6cf9 | ||
|
|
e6f9a06894 | ||
|
|
e125bcb78b | ||
|
|
23b418b4e9 | ||
|
|
cf892f2cb8 | ||
|
|
c12b6697f9 | ||
|
|
89fcab5669 | ||
|
|
35ca010df6 | ||
|
|
0f58646bfb | ||
|
|
4d126c49e9 | ||
|
|
3a636c752b | ||
|
|
ff5ab030c5 | ||
|
|
ff3b1466e1 | ||
|
|
89cb785f22 | ||
|
|
30f62ba887 | ||
|
|
1503909824 | ||
|
|
75b685fa53 | ||
|
|
62d55223b2 | ||
|
|
418a6c79cb | ||
|
|
ab417fd9e3 | ||
|
|
35cdcd3b95 | ||
|
|
a845f70c49 | ||
|
|
7e866e11ad | ||
|
|
6acbcfc68a | ||
|
|
02c81aaac2 | ||
|
|
550510ff3f | ||
|
|
5051010835 | ||
|
|
fdb8a6b065 | ||
|
|
6d4299ea94 | ||
|
|
b84b3afc9c | ||
|
|
451616f99a | ||
|
|
496b58e0c9 | ||
|
|
886236b978 | ||
|
|
6d2e0e8050 | ||
|
|
2f29ed003c | ||
|
|
12953b3fdc | ||
|
|
448648d61b | ||
|
|
241465eaac | ||
|
|
dce00b96b8 | ||
|
|
3b2d620671 | ||
|
|
e18546560e | ||
|
|
fc9abc7a33 | ||
|
|
98d010f460 | ||
|
|
36d7f9ba32 | ||
|
|
c4c5b06643 | ||
|
|
767a6ec4a2 | ||
|
|
860a605c8d | ||
|
|
df5bf232eb | ||
|
|
90a4bae6cc | ||
|
|
3ae452ee2a | ||
|
|
bc2d3ea72e | ||
|
|
9ccc4878d0 | ||
|
|
6365f36c75 | ||
|
|
da3d9b1c55 | ||
|
|
f77a84486e | ||
|
|
cf9f2a436b | ||
|
|
e73afa3325 | ||
|
|
dfee045d46 | ||
|
|
93f304a91d | ||
|
|
ddabffc7ef | ||
|
|
42d268c2c9 | ||
|
|
e7c0f3322d | ||
|
|
b3f3eeeec7 | ||
|
|
54fbe15611 | ||
|
|
d7523d7843 | ||
|
|
ca801c5411 | ||
|
|
4667ba02e5 | ||
|
|
3151336050 | ||
|
|
848f8c316e | ||
|
|
2303da48a2 | ||
|
|
bbec1c7f81 | ||
|
|
79f3edebc0 | ||
|
|
d49d0a6888 | ||
|
|
5c3f096fe4 | ||
|
|
7060d53cf3 | ||
|
|
dcf11b9586 | ||
|
|
229d69864d | ||
|
|
36d9fc1973 | ||
|
|
d06f480f82 | ||
|
|
f9efcfb8d6 | ||
|
|
cbd6f73431 | ||
|
|
db06166330 | ||
|
|
5d8f781467 | ||
|
|
03adafbfd9 | ||
|
|
198530547d | ||
|
|
4b6745b59b | ||
|
|
6f0bf734d5 | ||
|
|
23bd3784a1 | ||
|
|
8f8ef3788f | ||
|
|
b0938a3cf1 | ||
|
|
a2688a1ff0 | ||
|
|
d6fc2ef4aa | ||
|
|
9f29804f60 | ||
|
|
289b6abb43 | ||
|
|
55f1224d6e | ||
|
|
395743eb49 | ||
|
|
62827269b6 | ||
|
|
d2273a7218 | ||
|
|
bd85cf8928 | ||
|
|
f3cbb883f2 | ||
|
|
65006952f3 | ||
|
|
4815dbba68 | ||
|
|
bcad792e75 | ||
|
|
a80222efa5 | ||
|
|
5e13e0c838 | ||
|
|
58765ac40a | ||
|
|
6e35e56d7f | ||
|
|
a9cdbb7e75 | ||
|
|
8c71428d4f | ||
|
|
2c85b4d178 | ||
|
|
3293cee11e | ||
|
|
27990d0623 | ||
|
|
986ee3c44a | ||
|
|
dfb3962511 | ||
|
|
3e89f7cb40 | ||
|
|
8c233f4552 | ||
|
|
c2896fd629 | ||
|
|
fcfd9f59fe | ||
|
|
49b07a2662 | ||
|
|
529d78f54b | ||
|
|
dc89bdd3f0 | ||
|
|
4be1279430 | ||
|
|
08ae0e8c63 | ||
|
|
81a9f0474d | ||
|
|
8a7d1dd796 | ||
|
|
457b1abac8 | ||
|
|
e8498b9264 | ||
|
|
3eeff8b933 | ||
|
|
d959ab89f1 | ||
|
|
b081fb2833 | ||
|
|
e0805b039c | ||
|
|
c352b5e0c7 | ||
|
|
a71749a6b5 | ||
|
|
a188778eb8 | ||
|
|
439f27640a | ||
|
|
c9209cc99e | ||
|
|
1370a1cc83 | ||
|
|
266e8f5ae4 | ||
|
|
ccaac6c336 | ||
|
|
88005640d5 | ||
|
|
1f4e06ea7c | ||
|
|
b6c9fb2da1 | ||
|
|
66f5f396ff | ||
|
|
c9628aae49 | ||
|
|
bb5d3f0600 | ||
|
|
1f819ce2c5 | ||
|
|
593eda7d9d | ||
|
|
14dab7c649 | ||
|
|
94a07fa39a | ||
|
|
492a0f8199 | ||
|
|
24736b919a | ||
|
|
38220a57ce | ||
|
|
b14dc1572d | ||
|
|
21964f3cbc | ||
|
|
c13921b7c7 | ||
|
|
39fef2705c | ||
|
|
8c427c7de9 | ||
|
|
0bb995abd0 | ||
|
|
0195eef865 | ||
|
|
cc3dbd84ce | ||
|
|
5e6285afab | ||
|
|
df28a1335a | ||
|
|
88a0871784 | ||
|
|
f0edca2f8c | ||
|
|
f5462e3e19 | ||
|
|
53c5a97b4c | ||
|
|
a3352fd899 | ||
|
|
4a85e1e1ca | ||
|
|
94efe5c187 | ||
|
|
bef59a0b9a | ||
|
|
86142343ee | ||
|
|
5b2458ed66 | ||
|
|
b52cd44c25 | ||
|
|
40997e7fc9 | ||
|
|
a4e68a0304 | ||
|
|
703736fbcb | ||
|
|
fd45808555 | ||
|
|
e9fec47411 | ||
|
|
6029352c09 | ||
|
|
077208d17a | ||
|
|
08863dec0b | ||
|
|
80af92a715 | ||
|
|
237e0ec23c | ||
|
|
d52e97dfcd | ||
|
|
8329d6ac1a | ||
|
|
f1d723077f | ||
|
|
a095c468b2 | ||
|
|
b14e6a3570 | ||
|
|
6e3391a846 | ||
|
|
c0ca5c97d5 | ||
|
|
d5cf6d72e2 | ||
|
|
9eb0f2941f | ||
|
|
3fa295d99d | ||
|
|
812f63502b | ||
|
|
353eb9f00f | ||
|
|
1b813d9d6c | ||
|
|
c5dade5ee7 | ||
|
|
e75d6f616c | ||
|
|
15b601f053 | ||
|
|
45317ef7f2 | ||
|
|
3da792152a | ||
|
|
3f6da779d2 | ||
|
|
57b8ae6e1c | ||
|
|
ade8dc1a21 | ||
|
|
c6f7ad3e83 | ||
|
|
dbe6be778b | ||
|
|
45b0279b41 | ||
|
|
3a5aeb4573 | ||
|
|
72745846f4 | ||
|
|
9e788bb8c2 | ||
|
|
56fbe7e14d | ||
|
|
44155f6771 | ||
|
|
341711c04b | ||
|
|
be6187e893 | ||
|
|
c42bdeab43 | ||
|
|
7d7c58cb8e | ||
|
|
044da877e6 | ||
|
|
2d7686b5fa | ||
|
|
074f6db6d8 | ||
|
|
7a884af0ef | ||
|
|
e568e1019d | ||
|
|
ca543039a5 | ||
|
|
5e7b1da632 | ||
|
|
6b9ef6421f | ||
|
|
d15c8453ac | ||
|
|
1e660dda71 | ||
|
|
2a77afc593 | ||
|
|
a9df3e48f7 | ||
|
|
c8994ede3f | ||
|
|
7e255733e0 | ||
|
|
72f9a96cce | ||
|
|
1308ff6479 | ||
|
|
b7a15092d8 | ||
|
|
ad9ffc62e6 | ||
|
|
46e613dcca | ||
|
|
2d7414733e | ||
|
|
9f597bab1b | ||
|
|
321b9b5a8b | ||
|
|
60a3a4e455 | ||
|
|
57e5cc8c17 | ||
|
|
5250673fa0 | ||
|
|
ab5e19a016 | ||
|
|
8790e9de01 | ||
|
|
1086d4476d | ||
|
|
7b80418122 | ||
|
|
02d034f53f | ||
|
|
e7126db97c | ||
|
|
4582ea3b27 | ||
|
|
43e100fd37 | ||
|
|
071a1477b5 | ||
|
|
61c8013f2c | ||
|
|
9c8c215c30 | ||
|
|
aa34afda79 | ||
|
|
942ec58de5 | ||
|
|
082a36147b | ||
|
|
f11b206fe8 | ||
|
|
e37f6ed752 | ||
|
|
d2f20e8d2a | ||
|
|
f194a55c0f | ||
|
|
719187794e | ||
|
|
74b94613a0 | ||
|
|
33d0be110b | ||
|
|
df12b4ffc6 | ||
|
|
d16c24ae21 | ||
|
|
1c35918834 | ||
|
|
d163f36d35 | ||
|
|
590cd4c9f6 | ||
|
|
6c0227ee13 | ||
|
|
9bb57fe0a7 | ||
|
|
3fecdbf428 | ||
|
|
356c7a5d6e | ||
|
|
4b4f70253b | ||
|
|
8b61a06fda | ||
|
|
6801ac2515 | ||
|
|
29d17bb8e9 | ||
|
|
fe8053c7d5 | ||
|
|
eb63ee8ec6 | ||
|
|
e86e5f906b | ||
|
|
60e82e6694 | ||
|
|
fc6e02da35 | ||
|
|
4140c5f2eb | ||
|
|
fc3fc10f60 | ||
|
|
3bc123aed6 |
18
.astylerc
Normal file
18
.astylerc
Normal file
@@ -0,0 +1,18 @@
|
||||
--suffix=none
|
||||
--style=kr
|
||||
--indent=spaces=2
|
||||
--indent-col1-comments
|
||||
--min-conditional-indent=0
|
||||
--attach-extern-c
|
||||
--attach-namespaces
|
||||
--indent-namespaces
|
||||
--pad-oper
|
||||
--unpad-paren
|
||||
--align-pointer=name
|
||||
--align-reference=name
|
||||
--max-instatement-indent=40
|
||||
--break-closing-brackets
|
||||
--remove-brackets
|
||||
--convert-tabs
|
||||
--max-code-length=100
|
||||
--break-after-logical
|
||||
21
.editorconfig
Normal file
21
.editorconfig
Normal file
@@ -0,0 +1,21 @@
|
||||
# EditorConfig is awesome: http://EditorConfig.org
|
||||
|
||||
# top-most EditorConfig file
|
||||
root = true
|
||||
|
||||
# Non-specified newlines with a newline ending every file
|
||||
[*]
|
||||
insert_final_newline = true
|
||||
|
||||
# 2 space indentation
|
||||
[*.{h,cpp,tcc,cmake,yml}]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
|
||||
# Trim trailing whitespaces
|
||||
[*.{h,cpp,tcc,cmake,yml}]
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
# UTF-8 without BOM
|
||||
[*]
|
||||
charset = utf-8
|
||||
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"
|
||||
88
.github/workflows/build.yml
vendored
Normal file
88
.github/workflows/build.yml
vendored
Normal file
@@ -0,0 +1,88 @@
|
||||
name: Build
|
||||
|
||||
on: [push, pull_request, workflow_dispatch]
|
||||
|
||||
env:
|
||||
BUILD_TYPE: Release
|
||||
|
||||
jobs:
|
||||
build:
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, macos-latest, windows-latest, windows-2025]
|
||||
include:
|
||||
- os: windows-latest
|
||||
cmake_extra_args: '-DCMAKE_TOOLCHAIN_FILE="$env:VCPKG_INSTALLATION_ROOT/scripts/buildsystems/vcpkg.cmake"'
|
||||
- os: windows-2025
|
||||
cmake_extra_args: '-G "MinGW Makefiles" -DCPPUNIT_INCLUDE_DIR="$env:GITHUB_WORKSPACE/pkg/usr/local/include" -DCPPUNIT_LIBRARIES="$($env:GITHUB_WORKSPACE -replace "\\", "/")/pkg/usr/local/lib/libcppunit.dll.a" -DCPPUNIT_INSTALLED_VERSION="1.15.1"'
|
||||
# The windows-2025 runner is used for MinGW.
|
||||
name: ${{ matrix.os == 'windows-2025' && 'mingw' || matrix.os }}
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- 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: Set up MinGW
|
||||
shell: bash
|
||||
run: |
|
||||
# Fetch utf8cpp, build cppunit
|
||||
git submodule update --init
|
||||
# Fails with fatal error: cppunit/config-auto.h: No such file or directory
|
||||
# vcpkg install cppunit utfcpp zlib --triplet x64-mingw-dynamic
|
||||
# Probably not working with CMake and MinGW, so we have to build it ourselves.
|
||||
curl -sL "https://dev-www.libreoffice.org/src/cppunit-1.15.1.tar.gz" | tar xz
|
||||
mkdir -p build_cppunit
|
||||
(cd build_cppunit && MAKE=mingw32-make ../cppunit-1.15.1/configure \
|
||||
--enable-shared=yes --enable-static=no \
|
||||
--disable-dependency-tracking --disable-doxygen)
|
||||
find build_cppunit -name Makefile -exec sed -i 's/\($[({]SHELL[)}]\)/"\1"/' {} \;
|
||||
sed -i 's/^\(SUBDIRS.*\) examples\(.*\)$/\1\2/' build_cppunit/Makefile
|
||||
(cd build_cppunit && mingw32-make -j$(nproc) install DESTDIR=$(cd .. && pwd)/pkg)
|
||||
if: matrix.os == 'windows-2025'
|
||||
|
||||
- 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}} --parallel
|
||||
|
||||
- name: Test
|
||||
working-directory: ${{github.workspace}}/build
|
||||
run: ctest -C ${{env.BUILD_TYPE}} -V --no-tests=error
|
||||
if: matrix.os != 'windows-latest' && matrix.os != 'windows-2025'
|
||||
|
||||
- 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'
|
||||
|
||||
- name: Test MinGW
|
||||
working-directory: ${{github.workspace}}/build
|
||||
run: |
|
||||
$env:Path += ";$PWD/taglib;$PWD/bindings/c;${{github.workspace}}/pkg/usr/local/bin"
|
||||
ctest -C ${{env.BUILD_TYPE}} -V --no-tests=error
|
||||
if: matrix.os == 'windows-2025'
|
||||
17
.gitignore
vendored
17
.gitignore
vendored
@@ -1,6 +1,9 @@
|
||||
cmake_install.cmake
|
||||
cmake_uninstall.cmake
|
||||
Makefile
|
||||
CTestTestfile.cmake
|
||||
CMakeFiles/
|
||||
CMakeLists.txt.user*
|
||||
*.so
|
||||
*.so.*
|
||||
*.dylib
|
||||
@@ -10,14 +13,17 @@ CMakeFiles/
|
||||
*.suo
|
||||
*.user
|
||||
.*
|
||||
!.github/workflows/
|
||||
*~
|
||||
/CMakeCache.txt
|
||||
/Doxyfile
|
||||
/config.h
|
||||
/taglib.pc
|
||||
/tests/test_runner
|
||||
/taglib_config.h
|
||||
/tests/Testing
|
||||
/taglib/libtag.a
|
||||
/taglib-config
|
||||
/bindings/c/libtag_c.a
|
||||
/bindings/c/taglib_c.pc
|
||||
/bindings/c/Debug
|
||||
/bindings/c/MinSizeRel
|
||||
@@ -39,3 +45,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
|
||||
1
3rdparty/utfcpp
vendored
Submodule
1
3rdparty/utfcpp
vendored
Submodule
Submodule 3rdparty/utfcpp added at df857efc5b
8
AUTHORS
8
AUTHORS
@@ -2,6 +2,10 @@ Scott Wheeler <wheeler@kde.org>
|
||||
Author, maintainer
|
||||
Lukas Lalinsky <lalinsky@gmail.com>
|
||||
Implementation of multiple new file formats, many bug fixes, maintainer
|
||||
Tsuda Kageyu <tsuda.kageyu@gmail.com>
|
||||
A lot of bug fixes and performance improvements, maintainer.
|
||||
Stephen F. Booth <me@sbooth.org>
|
||||
DSF metadata implementation, bug fixes, maintainer.
|
||||
Ismael Orenstein <orenstein@kde.org>
|
||||
Xing header implementation
|
||||
Allan Sandfeld Jensen <kde@carewolf.org>
|
||||
@@ -10,6 +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
|
||||
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!
|
||||
|
||||
556
CHANGELOG.md
Normal file
556
CHANGELOG.md
Normal file
@@ -0,0 +1,556 @@
|
||||
TagLib 2.2 (Feb 18, 2026)
|
||||
=========================
|
||||
|
||||
* Support for Matroska (MKA, MKV) and WebM files.
|
||||
* Support for NI STEM in MP4 files.
|
||||
* Stricter verification of ID3v2 frames.
|
||||
* Fix setting the last header flag in Ogg FLAC files.
|
||||
* Fix reading of the last page in Ogg streams.
|
||||
* Avoid corrupting invalid Ogg FLAC files without Vorbis comment.
|
||||
* Windows: Support MP4 files with 64-bit atoms.
|
||||
* Fix use of property keys with non-ASCII characters in C bindings.
|
||||
* Fix building with Android NDK 29.
|
||||
|
||||
TagLib 2.1.1 (June 30, 2025)
|
||||
============================
|
||||
|
||||
* Map ID3v2.3 IPLS frames to both ID3v2.4 TIPL and TMCL to have a consistent
|
||||
behavior when using MusicBrainz tags with the property map interface.
|
||||
* Fix missing include for `wchar_t` when using C bindings with MinGW.
|
||||
|
||||
TagLib 2.1 (May 31, 2025)
|
||||
=========================
|
||||
|
||||
* Support for Shorten (SHN) files.
|
||||
* Compile time configuration of supported formats: WITH_APE, WITH_ASF, ...
|
||||
* Compile time configuration of data and temporary directories for unit tests:
|
||||
TESTS_DIR and TESTS_TMPDIR.
|
||||
* C bindings: Added taglib_file_new_wchar() and taglib_file_new_type_wchar().
|
||||
* Preserve unicode encoding when downgrading to ID3v2.3.
|
||||
* Do not store FLAC metadata blocks which are too large.
|
||||
* Fix segfaults with String and ByteVector nullptr arguments.
|
||||
|
||||
TagLib 2.0.2 (Aug 24, 2024)
|
||||
===========================
|
||||
|
||||
* Fix parsing of ID3v2.2 frames.
|
||||
* Tolerate MP4 files with unknown atom types as generated by Android tools.
|
||||
* Support setting properties with arbitrary names in MP4 tags.
|
||||
* Windows: Fix "-p" option in tagwriter example.
|
||||
* Support building with older utfcpp versions.
|
||||
|
||||
TagLib 2.0.1 (Apr 9, 2024)
|
||||
==========================
|
||||
|
||||
* Fix aborting when _GLIBCXX_ASSERTIONS are enabled.
|
||||
* Fall back to utf8cpp header detection in the case that its CMake
|
||||
configuration is removed.
|
||||
* Improve compatibility with the SWIG interface compiler.
|
||||
* Build system fixes for testing without bindings, Emscripten and Illumos.
|
||||
* C bindings: Fix setting UTF-8 encoded property values.
|
||||
* Windows: Fix opening long paths.
|
||||
|
||||
TagLib 2.0 (Jan 24, 2024)
|
||||
=========================
|
||||
|
||||
* New major version, binary incompatible, but mostly source-compatible
|
||||
with the latest 1.x release if no deprecated features are used.
|
||||
Simple applications should build without changes, more complex
|
||||
applications (e.g. extending classes of TagLib) will have to be adapted.
|
||||
* 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)
|
||||
* Made methods const: Frame::Header::size(), Frame::headerSize(),
|
||||
MP4::Atom::findall(), MP4::Atoms::find(), MP4::Atoms::path().
|
||||
* Made classes non-virtual: APE::Footer, APE::Item, ASF::Attribute,
|
||||
ASF::Picture, MP4::CoverArt, MP4::Item, ID3v2::ExtendedHeader, ID3v2::Footer,
|
||||
ID3v2::Header, MPEG::Header, MPEG::XingHeader, Ogg::Page, Ogg::PageHeader.
|
||||
* Removed type definitions in TagLib namespace: wchar, uchar, ushort, uint,
|
||||
ulong, ulonglong, wstring: Use the standard types.
|
||||
* Removed include file taglib_config.h and its defines TAGLIB_WITH_ASF,
|
||||
TAGLIB_WITH_MP4: They were always 1 since version 1.8.
|
||||
* 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.
|
||||
282
CMakeLists.txt
282
CMakeLists.txt
@@ -1,108 +1,254 @@
|
||||
cmake_minimum_required(VERSION 3.10...3.31)
|
||||
|
||||
project(taglib)
|
||||
|
||||
cmake_minimum_required(VERSION 2.6.0 FATAL_ERROR)
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules")
|
||||
|
||||
option(ENABLE_STATIC "Make static version of libtag" OFF)
|
||||
if(ENABLE_STATIC)
|
||||
add_definitions(-DTAGLIB_STATIC)
|
||||
set(BUILD_SHARED_LIBS OFF)
|
||||
else()
|
||||
set(BUILD_SHARED_LIBS ON)
|
||||
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()
|
||||
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/")
|
||||
set(TESTS_DIR "${CMAKE_CURRENT_SOURCE_DIR}/tests/" CACHE STRING
|
||||
"Tests directory, is path to unit test data when 'data' is appended")
|
||||
set(TESTS_TMPDIR "" CACHE STRING
|
||||
"Directory for temporary files created during unit tests, system tmpdir is used if undefined")
|
||||
|
||||
## 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 "8")
|
||||
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)
|
||||
|
||||
set(TAGLIB_LIB_VERSION_STRING "${TAGLIB_LIB_MAJOR_VERSION}.${TAGLIB_LIB_MINOR_VERSION}.${TAGLIB_LIB_PATCH_VERSION}")
|
||||
# Only used to force cmake rerun when taglib.h changes.
|
||||
configure_file(taglib/toolkit/taglib.h ${CMAKE_CURRENT_BINARY_DIR}/taglib.h.stamp)
|
||||
|
||||
# 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 12)
|
||||
set(TAGLIB_SOVERSION_REVISION 0)
|
||||
set(TAGLIB_SOVERSION_AGE 11)
|
||||
if("${TAGLIB_LIB_PATCH_VERSION}" EQUAL "0")
|
||||
set(TAGLIB_LIB_VERSION_STRING "${TAGLIB_LIB_MAJOR_VERSION}.${TAGLIB_LIB_MINOR_VERSION}")
|
||||
else()
|
||||
set(TAGLIB_LIB_VERSION_STRING "${TAGLIB_LIB_MAJOR_VERSION}.${TAGLIB_LIB_MINOR_VERSION}.${TAGLIB_LIB_PATCH_VERSION}")
|
||||
endif()
|
||||
|
||||
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 2)
|
||||
set(TAGLIB_SOVERSION_PATCH 0)
|
||||
|
||||
include(ConfigureChecks.cmake)
|
||||
|
||||
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})
|
||||
option(WITH_APE "Build with APE, MPC, WavPack" ON)
|
||||
option(WITH_ASF "Build with ASF" ON)
|
||||
option(WITH_DSF "Build with DSF" ON)
|
||||
option(WITH_MATROSKA "Build with Matroska" ON)
|
||||
option(WITH_MOD "Build with Tracker modules" ON)
|
||||
option(WITH_MP4 "Build with MP4" ON)
|
||||
option(WITH_RIFF "Build with AIFF, RIFF, WAV" ON)
|
||||
option(WITH_SHORTEN "Build with Shorten" ON)
|
||||
option(WITH_TRUEAUDIO "Build with TrueAudio" ON)
|
||||
option(WITH_VORBIS "Build with Vorbis, FLAC, Ogg, Opus" ON)
|
||||
|
||||
if(NOT WIN32 AND NOT BUILD_FRAMEWORK)
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/taglib.pc.cmake ${CMAKE_CURRENT_BINARY_DIR}/taglib.pc )
|
||||
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/taglib.pc DESTINATION ${LIB_INSTALL_DIR}/pkgconfig)
|
||||
# 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" @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 "${CMAKE_INSTALL_BINDIR}"
|
||||
RENAME "taglib${TAGLIB_INSTALL_SUFFIX}-config.cmd")
|
||||
endif()
|
||||
|
||||
if(NOT BUILD_FRAMEWORK)
|
||||
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-taglib.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config.h)
|
||||
configure_file(config.h.cmake "${CMAKE_CURRENT_BINARY_DIR}/config.h")
|
||||
|
||||
option(TRACE_IN_RELEASE "Output debug messages even in release mode" OFF)
|
||||
if(TRACE_IN_RELEASE)
|
||||
set(TRACE_IN_RELEASE TRUE)
|
||||
endif()
|
||||
|
||||
find_package(utf8cpp QUIET)
|
||||
if(utf8cpp_FOUND)
|
||||
message(STATUS "Using utfcpp ${utf8cpp_VERSION} from ${utf8cpp_CONFIG}")
|
||||
else()
|
||||
find_path(utf8cpp_INCLUDE_DIR NAMES utf8.h PATH_SUFFIXES utf8cpp
|
||||
DOC "utf8cpp include directory")
|
||||
mark_as_advanced(utf8cpp_INCLUDE_DIR)
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(utf8cpp REQUIRED_VARS utf8cpp_INCLUDE_DIR)
|
||||
if(utf8cpp_FOUND)
|
||||
set(utf8cpp_INCLUDE_DIRS "${utf8cpp_INCLUDE_DIR}")
|
||||
if(NOT TARGET utf8::cpp)
|
||||
add_library(utf8::cpp INTERFACE IMPORTED)
|
||||
set_target_properties(utf8::cpp PROPERTIES
|
||||
INTERFACE_INCLUDE_DIRECTORIES "${utf8cpp_INCLUDE_DIR}")
|
||||
endif()
|
||||
message(STATUS "Using utfcpp from ${utf8cpp_INCLUDE_DIR}")
|
||||
else()
|
||||
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()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(WITH_APE)
|
||||
set(TAGLIB_WITH_APE TRUE)
|
||||
endif()
|
||||
if(WITH_ASF)
|
||||
set(TAGLIB_WITH_ASF TRUE)
|
||||
set(TAGLIB_WITH_ASF TRUE)
|
||||
endif()
|
||||
if(WITH_DSF)
|
||||
set(TAGLIB_WITH_DSF TRUE)
|
||||
endif()
|
||||
if(WITH_MATROSKA)
|
||||
set(TAGLIB_WITH_MATROSKA TRUE)
|
||||
endif()
|
||||
if(WITH_MOD)
|
||||
set(TAGLIB_WITH_MOD TRUE)
|
||||
endif()
|
||||
if(WITH_MP4)
|
||||
set(TAGLIB_WITH_MP4 TRUE)
|
||||
set(TAGLIB_WITH_MP4 TRUE)
|
||||
endif()
|
||||
if(WITH_RIFF)
|
||||
set(TAGLIB_WITH_RIFF TRUE)
|
||||
endif()
|
||||
if(WITH_SHORTEN)
|
||||
set(TAGLIB_WITH_SHORTEN TRUE)
|
||||
endif()
|
||||
if(WITH_TRUEAUDIO)
|
||||
set(TAGLIB_WITH_TRUEAUDIO TRUE)
|
||||
endif()
|
||||
if(WITH_VORBIS)
|
||||
set(TAGLIB_WITH_VORBIS TRUE)
|
||||
endif()
|
||||
|
||||
configure_file(taglib/taglib_config.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/taglib_config.h)
|
||||
configure_file(taglib/taglib_config.h.cmake "${CMAKE_CURRENT_BINARY_DIR}/taglib_config.h")
|
||||
|
||||
add_subdirectory(taglib)
|
||||
add_subdirectory(bindings)
|
||||
if(BUILD_TESTS)
|
||||
enable_testing()
|
||||
add_subdirectory(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()
|
||||
|
||||
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/Doxyfile.cmake ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile)
|
||||
file(COPY doc/taglib.png DESTINATION doc)
|
||||
if(BUILD_EXAMPLES)
|
||||
add_subdirectory(examples)
|
||||
endif()
|
||||
|
||||
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/Doxyfile.cmake" "${CMAKE_CURRENT_BINARY_DIR}/Doxyfile")
|
||||
add_custom_target(docs doxygen)
|
||||
|
||||
# uninstall target
|
||||
configure_file(
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/cmake_uninstall.cmake.in"
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake"
|
||||
IMMEDIATE @ONLY)
|
||||
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/cmake_uninstall.cmake.in" "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake" IMMEDIATE @ONLY)
|
||||
|
||||
add_custom_target(uninstall
|
||||
COMMAND ${CMAKE_COMMAND} -P "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake")
|
||||
if(NOT TARGET uninstall)
|
||||
add_custom_target(uninstall COMMAND "${CMAKE_COMMAND}" -P "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake")
|
||||
endif()
|
||||
|
||||
feature_summary(WHAT ALL FATAL_ON_MISSING_REQUIRED_PACKAGES)
|
||||
|
||||
@@ -1,23 +1,109 @@
|
||||
include(CheckIncludeFile)
|
||||
include(CheckIncludeFiles)
|
||||
include(CheckSymbolExists)
|
||||
include(CheckFunctionExists)
|
||||
include(CheckLibraryExists)
|
||||
include(CheckTypeSize)
|
||||
include(CheckCXXSourceCompiles)
|
||||
|
||||
# check for libz using the cmake supplied FindZLIB.cmake
|
||||
find_package(ZLIB)
|
||||
if(ZLIB_FOUND)
|
||||
set(HAVE_ZLIB 1)
|
||||
else()
|
||||
set(HAVE_ZLIB 0)
|
||||
# Check if the size of numeric types are suitable.
|
||||
|
||||
check_type_size("short" SIZEOF_SHORT)
|
||||
if(NOT ${SIZEOF_SHORT} EQUAL 2)
|
||||
message(FATAL_ERROR "TagLib requires that short is 16-bit wide.")
|
||||
endif()
|
||||
|
||||
set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules)
|
||||
find_package(CppUnit)
|
||||
if(NOT CppUnit_FOUND AND BUILD_TESTS)
|
||||
message(STATUS "CppUnit not found, disabling tests.")
|
||||
set(BUILD_TESTS OFF)
|
||||
check_type_size("int" SIZEOF_INT)
|
||||
if(NOT ${SIZEOF_INT} EQUAL 4)
|
||||
message(FATAL_ERROR "TagLib requires that int is 32-bit wide.")
|
||||
endif()
|
||||
|
||||
check_type_size("long long" SIZEOF_LONGLONG)
|
||||
if(NOT ${SIZEOF_LONGLONG} EQUAL 8)
|
||||
message(FATAL_ERROR "TagLib requires that long long is 64-bit wide.")
|
||||
endif()
|
||||
|
||||
check_type_size("wchar_t" SIZEOF_WCHAR_T)
|
||||
if(NOT ${SIZEOF_WCHAR_T} GREATER 1)
|
||||
message(FATAL_ERROR "TagLib requires that wchar_t is sufficient to store a UTF-16 char.")
|
||||
endif()
|
||||
|
||||
check_type_size("float" SIZEOF_FLOAT)
|
||||
if(NOT ${SIZEOF_FLOAT} EQUAL 4)
|
||||
message(FATAL_ERROR "TagLib requires that float is 32-bit wide.")
|
||||
endif()
|
||||
|
||||
check_type_size("double" SIZEOF_DOUBLE)
|
||||
if(NOT ${SIZEOF_DOUBLE} EQUAL 8)
|
||||
message(FATAL_ERROR "TagLib requires that double is 64-bit wide.")
|
||||
endif()
|
||||
|
||||
# Determine which kind of byte swap functions your compiler supports.
|
||||
|
||||
check_cxx_source_compiles("
|
||||
int main() {
|
||||
__builtin_bswap16(0);
|
||||
__builtin_bswap32(0);
|
||||
__builtin_bswap64(0);
|
||||
return 0;
|
||||
}
|
||||
" HAVE_GCC_BYTESWAP)
|
||||
|
||||
if(NOT HAVE_GCC_BYTESWAP)
|
||||
check_cxx_source_compiles("
|
||||
#include <byteswap.h>
|
||||
int main() {
|
||||
__bswap_16(0);
|
||||
__bswap_32(0);
|
||||
__bswap_64(0);
|
||||
return 0;
|
||||
}
|
||||
" HAVE_GLIBC_BYTESWAP)
|
||||
|
||||
if(NOT HAVE_GLIBC_BYTESWAP)
|
||||
check_cxx_source_compiles("
|
||||
#include <cstdlib>
|
||||
int main() {
|
||||
_byteswap_ushort(0);
|
||||
_byteswap_ulong(0);
|
||||
_byteswap_uint64(0);
|
||||
return 0;
|
||||
}
|
||||
" HAVE_MSC_BYTESWAP)
|
||||
|
||||
if(NOT HAVE_MSC_BYTESWAP)
|
||||
check_cxx_source_compiles("
|
||||
#include <libkern/OSByteOrder.h>
|
||||
int main() {
|
||||
OSSwapInt16(0);
|
||||
OSSwapInt32(0);
|
||||
OSSwapInt64(0);
|
||||
return 0;
|
||||
}
|
||||
" HAVE_MAC_BYTESWAP)
|
||||
|
||||
if(NOT HAVE_MAC_BYTESWAP)
|
||||
check_cxx_source_compiles("
|
||||
#include <sys/endian.h>
|
||||
int main() {
|
||||
swap16(0);
|
||||
swap32(0);
|
||||
swap64(0);
|
||||
return 0;
|
||||
}
|
||||
" HAVE_OPENBSD_BYTESWAP)
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Determine whether your compiler supports ISO _strdup.
|
||||
|
||||
check_cxx_source_compiles("
|
||||
#include <cstring>
|
||||
int main() {
|
||||
_strdup(0);
|
||||
return 0;
|
||||
}
|
||||
" HAVE_ISO_STRDUP)
|
||||
|
||||
# Detect WinRT mode
|
||||
if(CMAKE_SYSTEM_NAME STREQUAL "WindowsStore")
|
||||
set(PLATFORM_WINRT 1)
|
||||
endif()
|
||||
|
||||
261
Doxyfile.cmake
261
Doxyfile.cmake
@@ -1,37 +1,78 @@
|
||||
# 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
|
||||
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 +80,264 @@ 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_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 =
|
||||
#---------------------------------------------------------------------------
|
||||
# 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
|
||||
#---------------------------------------------------------------------------
|
||||
# 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
|
||||
|
||||
70
INSTALL
70
INSTALL
@@ -1,70 +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_RELEASE_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
|
||||
-------
|
||||
|
||||
For building a static library on Windows with Visual Studio 2010, cd to
|
||||
the TagLib folder then:
|
||||
|
||||
cmake -DENABLE_STATIC=ON -DENABLE_STATIC_RUNTIME=ON -G "Visual Studio 10" ...
|
||||
|
||||
Including ENABLE_STATIC_RUNTIME=ON indicates you want TagLib built using the
|
||||
static runtime library, rather than the DLL form of the runtime.
|
||||
CMake will create a Visual Studio solution, taglib.sln that you can open and
|
||||
build as normal.
|
||||
|
||||
Unit Tests
|
||||
----------
|
||||
|
||||
If you want to run the test suite to make sure TagLib works properly on your
|
||||
system, you need to have cppunit installed. 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
|
||||
|
||||
519
INSTALL.md
Normal file
519
INSTALL.md
Normal file
@@ -0,0 +1,519 @@
|
||||
# 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 |
|
||||
| `TESTS_DIR` | Where to find unit test data (with data appended) |
|
||||
| `TESTS_TMPDIR` | Where to create temporary files in unit tests |
|
||||
|
||||
|
||||
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".
|
||||
|
||||
### Compile Time Configuration of Supported Formats
|
||||
|
||||
To reduce the size of the library, it is possible to switch off supported file
|
||||
formats. By default, all formats are enabled. Support for MPEG files (MP3, AAC)
|
||||
and ID3 tags cannot be disabled. The following CMake options are available:
|
||||
|
||||
| Option | Description |
|
||||
|-------------------------|----------------------------------------------------|
|
||||
| `WITH_APE` | Build with APE, MPC, WavPack (default ON) |
|
||||
| `WITH_ASF` | Build with ASF (default ON) |
|
||||
| `WITH_DSF` | Build with DSF (default ON) |
|
||||
| `WITH_MATROSKA` | Build with Matroska (default ON) |
|
||||
| `WITH_MOD` | Build with Tracker modules (default ON) |
|
||||
| `WITH_MP4` | Build with MP4 (default ON) |
|
||||
| `WITH_RIFF` | Build with AIFF, RIFF, WAV (default ON) |
|
||||
| `WITH_SHORTEN` | Build with Shorten (default ON) |
|
||||
| `WITH_TRUEAUDIO` | Build with TrueAudio (default ON) |
|
||||
| `WITH_VORBIS` | Build with Vorbis, FLAC, Ogg, Opus (default ON) |
|
||||
|
||||
Note that disabling formats will remove exported symbols from the library and
|
||||
thus break binary compatibility. These options should therefore only be used
|
||||
if the library is built specifically for a certain project. The public header
|
||||
files still contain the full API, if you use TagLib with a reduced set of
|
||||
formats, you can include taglib_config.h and use its definitions (prefixed with
|
||||
`TAGLIB_`, e.g. `TAGLIB_WITH_APE`), as it is done in examples/framelist.cpp.
|
||||
|
||||
## 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
|
||||
```
|
||||
|
||||
## Android
|
||||
|
||||
### Using vcpkg
|
||||
|
||||
The bash script below can be used to build TagLib for Android using vcpkg.
|
||||
It must be started in the parent folder of the taglib source folder and will
|
||||
build in a folder _android_build_ and install into _android_pkg_.
|
||||
The package and the unit tests are then transferred to an Android device
|
||||
and the unit tests are run on the device.
|
||||
|
||||
Note that `TESTS_TMPDIR` is set because there is no system-wide temporary folder
|
||||
on Android. `TESTS_DIR` is set to run the tests on the target.
|
||||
|
||||
```
|
||||
# You may have to adapt the NDK and vcpkg paths and the ABI/triplet.
|
||||
|
||||
export ANDROID_NDK_HOME=$HOME/Development/android-sdk/ndk/23.1.7779620
|
||||
export VCPKG_ROOT=$HOME/Development/vcpkg
|
||||
PATH=$PATH:$VCPKG_ROOT
|
||||
|
||||
# armeabi-v7a/arm-android or arm64-v8a/arm64-android or x86/x86-android or x86_64/x64-android
|
||||
android_abi=armeabi-v7a
|
||||
vcpkg_target_triplet=arm-android
|
||||
|
||||
vcpkg_toolchain_file=$VCPKG_ROOT/scripts/buildsystems/vcpkg.cmake
|
||||
android_toolchain_file=$ANDROID_NDK_HOME/build/cmake/android.toolchain.cmake
|
||||
|
||||
vcpkg install --triplet $vcpkg_target_triplet utfcpp zlib cppunit
|
||||
|
||||
cmake -B android_build -S taglib \
|
||||
-DCMAKE_TOOLCHAIN_FILE=$vcpkg_toolchain_file \
|
||||
-DVCPKG_CHAINLOAD_TOOLCHAIN_FILE=$android_toolchain_file \
|
||||
-DVCPKG_TARGET_TRIPLET=$vcpkg_target_triplet \
|
||||
-DANDROID_ABI=$android_abi \
|
||||
-GNinja -DBUILD_SHARED_LIBS=ON -DCMAKE_BUILD_TYPE=Release \
|
||||
-DVISIBILITY_HIDDEN=ON -DENABLE_CCACHE=ON -DBUILD_EXAMPLES=ON -DBUILD_TESTING=ON \
|
||||
-DTESTS_DIR=/data/local/tmp/tests/ -DTESTS_TMPDIR=/data/local/tmp
|
||||
cmake --build android_build --config Release
|
||||
cmake --install android_build --config Release --prefix android_pkg --strip
|
||||
cp -a android_build/tests/test_runner android_pkg/bin/
|
||||
|
||||
if hash adb 2>/dev/null; then
|
||||
adb push android_pkg /data/local/tmp/
|
||||
adb push android_build/tests/test_runner /data/local/tmp/tests/test_runner
|
||||
adb push taglib/tests/data /data/local/tmp/tests/
|
||||
adb shell "env LD_LIBRARY_PATH=/data/local/tmp/android_pkg/lib /data/local/tmp/tests/test_runner"
|
||||
# You could also try an example binary:
|
||||
# adb shell "env LD_LIBRARY_PATH=/data/local/tmp/android_pkg/lib /data/local/tmp/android_pkg/bin/tagreader '/sdcard/Music/Some Album/A Track.mp3'"
|
||||
fi
|
||||
```
|
||||
143
NEWS
143
NEWS
@@ -1,143 +0,0 @@
|
||||
TagLib 1.8 BETA (Jul 14, 2012)
|
||||
==============================
|
||||
|
||||
* 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.
|
||||
24
README.md
Normal file
24
README.md
Normal file
@@ -0,0 +1,24 @@
|
||||
# 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, ASF,
|
||||
DSF, DFF and AAC 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.
|
||||
|
||||
[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)
|
||||
|
||||
@@ -2,5 +2,4 @@ There are a few other people that have done bindings externally that I have
|
||||
been made aware of. I have not personally reviewed these bindings, but I'm
|
||||
listing them here so that those who find them useful are able to find them:
|
||||
|
||||
http://developer.kde.org/~wheeler/taglib.html#bindings
|
||||
|
||||
https://taglib.org/#language-bindings
|
||||
|
||||
@@ -1,27 +1,92 @@
|
||||
include_directories(
|
||||
set(tag_c_HDR_DIRS
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/toolkit
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/asf
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/mpeg
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/ogg
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/ogg/vorbis
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/ogg/flac
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/flac
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/mpc
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/mp4
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/mpeg/id3v2
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/mpeg/id3v2/frames
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/wavpack
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/ogg/speex
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/trueaudio
|
||||
)
|
||||
if(WITH_ASF)
|
||||
set(tag_c_HDR_DIRS ${tag_c_HDR_DIRS}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/asf
|
||||
)
|
||||
endif()
|
||||
if(WITH_VORBIS)
|
||||
set(tag_c_HDR_DIRS ${tag_c_HDR_DIRS}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/ogg
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/ogg/vorbis
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/ogg/flac
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/flac
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/ogg/speex
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/ogg/opus
|
||||
)
|
||||
endif()
|
||||
if(WITH_APE)
|
||||
set(tag_c_HDR_DIRS ${tag_c_HDR_DIRS}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/mpc
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/wavpack
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/ape
|
||||
)
|
||||
endif()
|
||||
if(WITH_MP4)
|
||||
set(tag_c_HDR_DIRS ${tag_c_HDR_DIRS}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/mp4
|
||||
)
|
||||
endif()
|
||||
if(WITH_TRUEAUDIO)
|
||||
set(tag_c_HDR_DIRS ${tag_c_HDR_DIRS}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/trueaudio
|
||||
)
|
||||
endif()
|
||||
if(WITH_RIFF)
|
||||
set(tag_c_HDR_DIRS ${tag_c_HDR_DIRS}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/riff
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/riff/aiff
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/riff/wav
|
||||
)
|
||||
endif()
|
||||
if(WITH_MOD)
|
||||
set(tag_c_HDR_DIRS ${tag_c_HDR_DIRS}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/it
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/mod
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/s3m
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/xm
|
||||
)
|
||||
endif()
|
||||
if(WITH_DSF)
|
||||
set(tag_c_HDR_DIRS ${tag_c_HDR_DIRS}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/dsf
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/dsdiff
|
||||
)
|
||||
endif()
|
||||
if(WITH_SHORTEN)
|
||||
set(tag_c_HDR_DIRS ${tag_c_HDR_DIRS}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/shorten
|
||||
)
|
||||
endif()
|
||||
if(WITH_MATROSKA)
|
||||
set(tag_c_HDR_DIRS ${tag_c_HDR_DIRS}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/matroska
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../taglib/matroska/ebml
|
||||
)
|
||||
endif()
|
||||
include_directories(${tag_c_HDR_DIRS})
|
||||
|
||||
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 +103,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 +113,45 @@ 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}
|
||||
)
|
||||
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
|
||||
)
|
||||
|
||||
if(NOT WIN32 AND 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(NOT BUILD_SHARED_LIBS)
|
||||
target_compile_definitions(tag_c PUBLIC TAGLIB_STATIC)
|
||||
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_c PROPERTIES SUFFIX ${TAGLIB_LIBRARY_SUFFIX})
|
||||
endif()
|
||||
|
||||
install(TARGETS tag_c
|
||||
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)
|
||||
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,43 +19,108 @@
|
||||
* USA *
|
||||
***************************************************************************/
|
||||
|
||||
#include "tag_c.h"
|
||||
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <sstream>
|
||||
#include <utility>
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
# include "config.h"
|
||||
#endif
|
||||
|
||||
#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 "taglib_config.h"
|
||||
#include "tstringlist.h"
|
||||
#include "tbytevectorstream.h"
|
||||
#include "tiostream.h"
|
||||
#include "tfile.h"
|
||||
#include "tpropertymap.h"
|
||||
#include "fileref.h"
|
||||
#include "mpegfile.h"
|
||||
#include "tag.h"
|
||||
#include "id3v2framefactory.h"
|
||||
#ifdef TAGLIB_WITH_ASF
|
||||
#include "asffile.h"
|
||||
#endif
|
||||
#ifdef TAGLIB_WITH_VORBIS
|
||||
#include "vorbisfile.h"
|
||||
#include "flacfile.h"
|
||||
#include "oggflacfile.h"
|
||||
#include "speexfile.h"
|
||||
#include "opusfile.h"
|
||||
#endif
|
||||
#ifdef TAGLIB_WITH_APE
|
||||
#include "mpcfile.h"
|
||||
#include "wavpackfile.h"
|
||||
#endif
|
||||
#ifdef TAGLIB_WITH_TRUEAUDIO
|
||||
#include "trueaudiofile.h"
|
||||
#endif
|
||||
#ifdef TAGLIB_WITH_MP4
|
||||
#include "mp4file.h"
|
||||
#endif
|
||||
#ifdef TAGLIB_WITH_RIFF
|
||||
#include "aifffile.h"
|
||||
#include "wavfile.h"
|
||||
#endif
|
||||
#ifdef TAGLIB_WITH_APE
|
||||
#include "apefile.h"
|
||||
#endif
|
||||
#ifdef TAGLIB_WITH_MOD
|
||||
#include "itfile.h"
|
||||
#include "modfile.h"
|
||||
#include "s3mfile.h"
|
||||
#include "xmfile.h"
|
||||
#endif
|
||||
#ifdef TAGLIB_WITH_DSF
|
||||
#include "dsffile.h"
|
||||
#include "dsdifffile.h"
|
||||
#endif
|
||||
#ifdef TAGLIB_WITH_SHORTEN
|
||||
#include "shortenfile.h"
|
||||
#endif
|
||||
#ifdef TAGLIB_WITH_MATROSKA
|
||||
#include "matroskafile.h"
|
||||
#endif
|
||||
|
||||
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)
|
||||
{
|
||||
unicodeStrings = bool(unicode);
|
||||
unicodeStrings = (unicode != 0);
|
||||
}
|
||||
|
||||
void taglib_set_string_management_enabled(BOOL management)
|
||||
{
|
||||
stringManagementEnabled = bool(management);
|
||||
stringManagementEnabled = (management != 0);
|
||||
}
|
||||
|
||||
void taglib_free(void* pointer)
|
||||
@@ -64,69 +129,176 @@ 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));
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
TagLib_File *taglib_file_new_wchar(const wchar_t *filename)
|
||||
{
|
||||
return reinterpret_cast<TagLib_File *>(new FileRef(filename));
|
||||
}
|
||||
#endif
|
||||
|
||||
template<typename T>
|
||||
TagLib_File *taglib_file_new_type_any_char(const T *filename, TagLib_File_Type type)
|
||||
{
|
||||
File *file = NULL;
|
||||
switch(type) {
|
||||
case TagLib_File_MPEG:
|
||||
file = new MPEG::File(filename);
|
||||
break;
|
||||
#ifdef TAGLIB_WITH_VORBIS
|
||||
case TagLib_File_OggVorbis:
|
||||
file = new Ogg::Vorbis::File(filename);
|
||||
break;
|
||||
case TagLib_File_FLAC:
|
||||
file = new FLAC::File(filename);
|
||||
break;
|
||||
case TagLib_File_OggFlac:
|
||||
file = new Ogg::FLAC::File(filename);
|
||||
break;
|
||||
case TagLib_File_Speex:
|
||||
file = new Ogg::Speex::File(filename);
|
||||
break;
|
||||
case TagLib_File_Opus:
|
||||
file = new Ogg::Opus::File(filename);
|
||||
break;
|
||||
#endif
|
||||
#ifdef TAGLIB_WITH_APE
|
||||
case TagLib_File_MPC:
|
||||
file = new MPC::File(filename);
|
||||
break;
|
||||
case TagLib_File_WavPack:
|
||||
file = new WavPack::File(filename);
|
||||
break;
|
||||
case TagLib_File_APE:
|
||||
file = new APE::File(filename);
|
||||
break;
|
||||
#endif
|
||||
#ifdef TAGLIB_WITH_TRUEAUDIO
|
||||
case TagLib_File_TrueAudio:
|
||||
file = new TrueAudio::File(filename);
|
||||
break;
|
||||
#endif
|
||||
#ifdef TAGLIB_WITH_MP4
|
||||
case TagLib_File_MP4:
|
||||
file = new MP4::File(filename);
|
||||
break;
|
||||
#endif
|
||||
#ifdef TAGLIB_WITH_ASF
|
||||
case TagLib_File_ASF:
|
||||
file = new ASF::File(filename);
|
||||
break;
|
||||
#endif
|
||||
#ifdef TAGLIB_WITH_RIFF
|
||||
case TagLib_File_AIFF:
|
||||
file = new RIFF::AIFF::File(filename);
|
||||
break;
|
||||
case TagLib_File_WAV:
|
||||
file = new RIFF::WAV::File(filename);
|
||||
break;
|
||||
#endif
|
||||
#ifdef TAGLIB_WITH_MOD
|
||||
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;
|
||||
#endif
|
||||
#ifdef TAGLIB_WITH_DSF
|
||||
case TagLib_File_DSF:
|
||||
file = new DSF::File(filename);
|
||||
break;
|
||||
case TagLib_File_DSDIFF:
|
||||
file = new DSDIFF::File(filename);
|
||||
break;
|
||||
#endif
|
||||
#ifdef TAGLIB_WITH_SHORTEN
|
||||
case TagLib_File_SHORTEN:
|
||||
file = new Shorten::File(filename);
|
||||
break;
|
||||
#endif
|
||||
#ifdef TAGLIB_WITH_MATROSKA
|
||||
case TagLib_File_MATROSKA:
|
||||
file = new Matroska::File(filename);
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return file ? reinterpret_cast<TagLib_File *>(new FileRef(file)) : NULL;
|
||||
}
|
||||
|
||||
TagLib_File *taglib_file_new_type(const char *filename, TagLib_File_Type type)
|
||||
{
|
||||
switch(type) {
|
||||
case TagLib_File_MPEG:
|
||||
return reinterpret_cast<TagLib_File *>(new MPEG::File(filename));
|
||||
case TagLib_File_OggVorbis:
|
||||
return reinterpret_cast<TagLib_File *>(new Ogg::Vorbis::File(filename));
|
||||
case TagLib_File_FLAC:
|
||||
return reinterpret_cast<TagLib_File *>(new FLAC::File(filename));
|
||||
case TagLib_File_MPC:
|
||||
return reinterpret_cast<TagLib_File *>(new MPC::File(filename));
|
||||
case TagLib_File_OggFlac:
|
||||
return reinterpret_cast<TagLib_File *>(new Ogg::FLAC::File(filename));
|
||||
case TagLib_File_WavPack:
|
||||
return reinterpret_cast<TagLib_File *>(new WavPack::File(filename));
|
||||
case TagLib_File_Speex:
|
||||
return reinterpret_cast<TagLib_File *>(new Ogg::Speex::File(filename));
|
||||
case TagLib_File_TrueAudio:
|
||||
return reinterpret_cast<TagLib_File *>(new TrueAudio::File(filename));
|
||||
case TagLib_File_MP4:
|
||||
return reinterpret_cast<TagLib_File *>(new MP4::File(filename));
|
||||
case TagLib_File_ASF:
|
||||
return reinterpret_cast<TagLib_File *>(new ASF::File(filename));
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
return taglib_file_new_type_any_char(filename, type);
|
||||
}
|
||||
|
||||
return 0;
|
||||
#ifdef _WIN32
|
||||
TagLib_File *taglib_file_new_type_wchar(const wchar_t *filename, TagLib_File_Type type)
|
||||
{
|
||||
return taglib_file_new_type_any_char(filename, type);
|
||||
}
|
||||
#endif
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -135,8 +307,8 @@ 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));
|
||||
auto t = reinterpret_cast<const Tag *>(tag);
|
||||
char *s = stringToCharArray(t->title());
|
||||
if(stringManagementEnabled)
|
||||
strings.append(s);
|
||||
return s;
|
||||
@@ -144,8 +316,8 @@ 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));
|
||||
auto t = reinterpret_cast<const Tag *>(tag);
|
||||
char *s = stringToCharArray(t->artist());
|
||||
if(stringManagementEnabled)
|
||||
strings.append(s);
|
||||
return s;
|
||||
@@ -153,8 +325,8 @@ 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));
|
||||
auto t = reinterpret_cast<const Tag *>(tag);
|
||||
char *s = stringToCharArray(t->album());
|
||||
if(stringManagementEnabled)
|
||||
strings.append(s);
|
||||
return s;
|
||||
@@ -162,8 +334,8 @@ 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));
|
||||
auto t = reinterpret_cast<const Tag *>(tag);
|
||||
char *s = stringToCharArray(t->comment());
|
||||
if(stringManagementEnabled)
|
||||
strings.append(s);
|
||||
return s;
|
||||
@@ -171,8 +343,8 @@ 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));
|
||||
auto t = reinterpret_cast<const Tag *>(tag);
|
||||
char *s = stringToCharArray(t->genre());
|
||||
if(stringManagementEnabled)
|
||||
strings.append(s);
|
||||
return s;
|
||||
@@ -180,55 +352,55 @@ char *taglib_tag_genre(const TagLib_Tag *tag)
|
||||
|
||||
unsigned int taglib_tag_year(const TagLib_Tag *tag)
|
||||
{
|
||||
const Tag *t = reinterpret_cast<const Tag *>(tag);
|
||||
auto t = reinterpret_cast<const Tag *>(tag);
|
||||
return t->year();
|
||||
}
|
||||
|
||||
unsigned int taglib_tag_track(const TagLib_Tag *tag)
|
||||
{
|
||||
const Tag *t = reinterpret_cast<const Tag *>(tag);
|
||||
auto t = reinterpret_cast<const Tag *>(tag);
|
||||
return t->track();
|
||||
}
|
||||
|
||||
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));
|
||||
auto t = reinterpret_cast<Tag *>(tag);
|
||||
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));
|
||||
auto t = reinterpret_cast<Tag *>(tag);
|
||||
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));
|
||||
auto t = reinterpret_cast<Tag *>(tag);
|
||||
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));
|
||||
auto t = reinterpret_cast<Tag *>(tag);
|
||||
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));
|
||||
auto t = reinterpret_cast<Tag *>(tag);
|
||||
t->setGenre(charArrayToString(genre));
|
||||
}
|
||||
|
||||
void taglib_tag_set_year(TagLib_Tag *tag, unsigned int year)
|
||||
{
|
||||
Tag *t = reinterpret_cast<Tag *>(tag);
|
||||
auto t = reinterpret_cast<Tag *>(tag);
|
||||
t->setYear(year);
|
||||
}
|
||||
|
||||
void taglib_tag_set_track(TagLib_Tag *tag, unsigned int track)
|
||||
{
|
||||
Tag *t = reinterpret_cast<Tag *>(tag);
|
||||
auto t = reinterpret_cast<Tag *>(tag);
|
||||
t->setTrack(track);
|
||||
}
|
||||
|
||||
@@ -237,8 +409,8 @@ void taglib_tag_free_strings()
|
||||
if(!stringManagementEnabled)
|
||||
return;
|
||||
|
||||
for(List<char *>::Iterator it = strings.begin(); it != strings.end(); ++it)
|
||||
free(*it);
|
||||
for(auto &string : std::as_const(strings))
|
||||
free(string);
|
||||
strings.clear();
|
||||
}
|
||||
|
||||
@@ -248,25 +420,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();
|
||||
}
|
||||
|
||||
@@ -292,3 +464,422 @@ 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);
|
||||
auto propStr = charArrayToString(prop);
|
||||
PropertyMap map = tfile->tag()->properties();
|
||||
|
||||
if(value) {
|
||||
auto property = map.find(propStr);
|
||||
if(property == map.end()) {
|
||||
map.insert(propStr, StringList(charArrayToString(value)));
|
||||
}
|
||||
else {
|
||||
if(append) {
|
||||
property->second.append(charArrayToString(value));
|
||||
}
|
||||
else {
|
||||
property->second = StringList(charArrayToString(value));
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
map.erase(propStr);
|
||||
}
|
||||
|
||||
tfile->setProperties(map);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void taglib_property_set(TagLib_File *file, const char *prop, const char *value)
|
||||
{
|
||||
_taglib_property_set(file, prop, value, false);
|
||||
}
|
||||
|
||||
void taglib_property_set_append(TagLib_File *file, const char *prop, const char *value)
|
||||
{
|
||||
_taglib_property_set(file, prop, value, true);
|
||||
}
|
||||
|
||||
char** taglib_property_keys(const 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(const 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(charArrayToString(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);
|
||||
auto keyStr = charArrayToString(key);
|
||||
|
||||
if(value == NULL) {
|
||||
return tfile->setComplexProperties(keyStr, {});
|
||||
}
|
||||
|
||||
VariantMap map;
|
||||
const TagLib_Complex_Property_Attribute** attrPtr = value;
|
||||
while(*attrPtr) {
|
||||
const TagLib_Complex_Property_Attribute *attr = *attrPtr;
|
||||
auto attrKey = charArrayToString(attr->key);
|
||||
switch(attr->value.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, charArrayToString(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(charArrayToString(*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(keyStr, tfile->complexProperties(keyStr).append(map))
|
||||
: tfile->setComplexProperties(keyStr, {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(const 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(
|
||||
const TagLib_File *file, const char *key)
|
||||
{
|
||||
if(file == NULL || key == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const auto variantMaps = reinterpret_cast<const FileRef *>(file)->complexProperties(charArrayToString(key));
|
||||
if(variantMaps.isEmpty()) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
auto 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()) {
|
||||
auto attrs = static_cast<TagLib_Complex_Property_Attribute **>(
|
||||
malloc(sizeof(TagLib_Complex_Property_Attribute *) * (variantMap.size() + 1)));
|
||||
auto 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;
|
||||
auto 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;
|
||||
switch(attr->value.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;
|
||||
switch(attr->value.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);
|
||||
}
|
||||
|
||||
@@ -29,7 +29,9 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#if defined(_WIN32) || defined(_WIN64)
|
||||
#if defined(TAGLIB_STATIC)
|
||||
#define TAGLIB_C_EXPORT
|
||||
#elif defined(_WIN32) || defined(_WIN64)
|
||||
#ifdef MAKE_TAGLIB_C_LIB
|
||||
#define TAGLIB_C_EXPORT __declspec(dllexport)
|
||||
#else
|
||||
@@ -41,7 +43,11 @@ extern "C" {
|
||||
#define TAGLIB_C_EXPORT
|
||||
#endif
|
||||
|
||||
#ifndef BOOL
|
||||
#include <wchar.h>
|
||||
#ifdef _MSC_VER
|
||||
/* minwindef.h contains typedef int BOOL */
|
||||
#include <windows.h>
|
||||
#elif !defined BOOL
|
||||
#define BOOL int
|
||||
#endif
|
||||
|
||||
@@ -55,14 +61,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.
|
||||
@@ -84,6 +91,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
|
||||
******************************************************************************/
|
||||
@@ -98,7 +121,19 @@ 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_SHORTEN,
|
||||
TagLib_File_MATROSKA
|
||||
} TagLib_File_Type;
|
||||
|
||||
/*!
|
||||
@@ -109,12 +144,28 @@ typedef enum {
|
||||
* be opened.
|
||||
*/
|
||||
TAGLIB_C_EXPORT TagLib_File *taglib_file_new(const char *filename);
|
||||
#ifdef _WIN32
|
||||
TAGLIB_C_EXPORT TagLib_File *taglib_file_new_wchar(const wchar_t *filename);
|
||||
#endif
|
||||
|
||||
/*!
|
||||
* Creates a TagLib file based on \a filename. Rather than attempting to guess
|
||||
* the type, it will use the one specified by \a type.
|
||||
*/
|
||||
TAGLIB_C_EXPORT TagLib_File *taglib_file_new_type(const char *filename, TagLib_File_Type type);
|
||||
#ifdef _WIN32
|
||||
TAGLIB_C_EXPORT TagLib_File *taglib_file_new_type_wchar(const wchar_t *filename, TagLib_File_Type type);
|
||||
#endif
|
||||
|
||||
/*!
|
||||
* 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.
|
||||
@@ -122,7 +173,7 @@ TAGLIB_C_EXPORT TagLib_File *taglib_file_new_type(const char *filename, TagLib_F
|
||||
TAGLIB_C_EXPORT void taglib_file_free(TagLib_File *file);
|
||||
|
||||
/*!
|
||||
* Returns true if the file is open and readble and valid information for
|
||||
* Returns \c true if the file is open and readable and valid information for
|
||||
* the Tag and / or AudioProperties was found.
|
||||
*/
|
||||
|
||||
@@ -135,7 +186,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);
|
||||
@@ -190,12 +241,12 @@ TAGLIB_C_EXPORT char *taglib_tag_comment(const TagLib_Tag *tag);
|
||||
TAGLIB_C_EXPORT char *taglib_tag_genre(const TagLib_Tag *tag);
|
||||
|
||||
/*!
|
||||
* Returns the tag's year or 0 if year is not set.
|
||||
* Returns the tag's year or 0 if the year is not set.
|
||||
*/
|
||||
TAGLIB_C_EXPORT unsigned int taglib_tag_year(const TagLib_Tag *tag);
|
||||
|
||||
/*!
|
||||
* Returns the tag's track number or 0 if track number is not set.
|
||||
* Returns the tag's track number or 0 if the track number is not set.
|
||||
*/
|
||||
TAGLIB_C_EXPORT unsigned int taglib_tag_track(const TagLib_Tag *tag);
|
||||
|
||||
@@ -290,6 +341,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(const 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(const 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(const 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(
|
||||
const 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_MAJOR_VERSION}.${TAGLIB_LIB_MINOR_VERSION}.${TAGLIB_LIB_PATCH_VERSION}
|
||||
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,11 +0,0 @@
|
||||
/* config-taglib.h. Generated by cmake from config-taglib.h.cmake */
|
||||
|
||||
/* Define if you have libz */
|
||||
#cmakedefine HAVE_ZLIB 1
|
||||
|
||||
#cmakedefine NO_ITUNES_HACKS 1
|
||||
#cmakedefine WITH_ASF 1
|
||||
#cmakedefine WITH_MP4 1
|
||||
|
||||
#cmakedefine TESTS_DIR "@TESTS_DIR@"
|
||||
|
||||
25
config.h.cmake
Normal file
25
config.h.cmake
Normal file
@@ -0,0 +1,25 @@
|
||||
/* 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
|
||||
#cmakedefine HAVE_MSC_BYTESWAP 1
|
||||
#cmakedefine HAVE_MAC_BYTESWAP 1
|
||||
#cmakedefine HAVE_OPENBSD_BYTESWAP 1
|
||||
|
||||
/* Defined if your compiler supports ISO _strdup */
|
||||
#cmakedefine HAVE_ISO_STRDUP 1
|
||||
|
||||
/* 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@"
|
||||
#cmakedefine TESTS_TMPDIR "@TESTS_TMPDIR@"
|
||||
|
||||
#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,58 @@
|
||||
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/matroska
|
||||
${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)
|
||||
add_executable(strip-id3v1 strip-id3v1.cpp)
|
||||
target_link_libraries(strip-id3v1 tag)
|
||||
|
||||
TARGET_LINK_LIBRARIES(strip-id3v1 tag )
|
||||
if(WITH_MATROSKA)
|
||||
########### next target ###############
|
||||
|
||||
add_executable(matroskareader matroskareader.cpp)
|
||||
target_link_libraries(matroskareader tag)
|
||||
|
||||
endif(BUILD_EXAMPLES)
|
||||
########### next target ###############
|
||||
|
||||
add_executable(matroskawriter matroskawriter.cpp)
|
||||
target_link_libraries(matroskawriter tag)
|
||||
endif()
|
||||
|
||||
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,21 +23,20 @@
|
||||
*/
|
||||
|
||||
#include <iostream>
|
||||
#include <stdlib.h>
|
||||
#include <cstdlib>
|
||||
|
||||
#include <tbytevector.h>
|
||||
#include "taglib_config.h"
|
||||
#include "tbytevector.h"
|
||||
#include "mpegfile.h"
|
||||
#include "id3v2tag.h"
|
||||
#include "id3v2frame.h"
|
||||
#include "id3v2header.h"
|
||||
#include "commentsframe.h"
|
||||
#include "id3v1tag.h"
|
||||
#ifdef TAGLIB_WITH_APE
|
||||
#include "apetag.h"
|
||||
#endif
|
||||
|
||||
#include <mpegfile.h>
|
||||
|
||||
#include <id3v2tag.h>
|
||||
#include <id3v2frame.h>
|
||||
#include <id3v2header.h>
|
||||
|
||||
#include <id3v1tag.h>
|
||||
|
||||
#include <apetag.h>
|
||||
|
||||
using namespace std;
|
||||
using namespace TagLib;
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
@@ -47,7 +46,7 @@ int main(int argc, char *argv[])
|
||||
|
||||
for(int i = 1; i < argc; i++) {
|
||||
|
||||
cout << "******************** \"" << argv[i] << "\"********************" << endl;
|
||||
std::cout << "******************** \"" << argv[i] << "\"********************" << std::endl;
|
||||
|
||||
MPEG::File f(argv[i]);
|
||||
|
||||
@@ -55,52 +54,64 @@ int main(int argc, char *argv[])
|
||||
|
||||
if(id3v2tag) {
|
||||
|
||||
cout << "ID3v2."
|
||||
std::cout << "ID3v2."
|
||||
<< id3v2tag->header()->majorVersion()
|
||||
<< "."
|
||||
<< id3v2tag->header()->revisionNumber()
|
||||
<< ", "
|
||||
<< id3v2tag->header()->tagSize()
|
||||
<< " bytes in tag"
|
||||
<< endl;
|
||||
<< std::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++) {
|
||||
std::cout << (*it)->frameID();
|
||||
|
||||
if(auto comment = dynamic_cast<ID3v2::CommentsFrame *>(*it))
|
||||
if(!comment->description().isEmpty())
|
||||
std::cout << " [" << comment->description() << "]";
|
||||
|
||||
std::cout << " - \"" << (*it)->toString() << "\"" << std::endl;
|
||||
}
|
||||
}
|
||||
else
|
||||
cout << "file does not have a valid id3v2 tag" << endl;
|
||||
std::cout << "file does not have a valid id3v2 tag" << std::endl;
|
||||
|
||||
cout << endl << "ID3v1" << endl;
|
||||
std::cout << std::endl << "ID3v1" << std::endl;
|
||||
|
||||
ID3v1::Tag *id3v1tag = f.ID3v1Tag();
|
||||
|
||||
if(id3v1tag) {
|
||||
cout << "title - \"" << id3v1tag->title() << "\"" << endl;
|
||||
cout << "artist - \"" << id3v1tag->artist() << "\"" << endl;
|
||||
cout << "album - \"" << id3v1tag->album() << "\"" << endl;
|
||||
cout << "year - \"" << id3v1tag->year() << "\"" << endl;
|
||||
cout << "comment - \"" << id3v1tag->comment() << "\"" << endl;
|
||||
cout << "track - \"" << id3v1tag->track() << "\"" << endl;
|
||||
cout << "genre - \"" << id3v1tag->genre() << "\"" << endl;
|
||||
std::cout << "title - \"" << id3v1tag->title() << "\"" << std::endl;
|
||||
std::cout << "artist - \"" << id3v1tag->artist() << "\"" << std::endl;
|
||||
std::cout << "album - \"" << id3v1tag->album() << "\"" << std::endl;
|
||||
std::cout << "year - \"" << id3v1tag->year() << "\"" << std::endl;
|
||||
std::cout << "comment - \"" << id3v1tag->comment() << "\"" << std::endl;
|
||||
std::cout << "track - \"" << id3v1tag->track() << "\"" << std::endl;
|
||||
std::cout << "genre - \"" << id3v1tag->genre() << "\"" << std::endl;
|
||||
}
|
||||
else
|
||||
cout << "file does not have a valid id3v1 tag" << endl;
|
||||
std::cout << "file does not have a valid id3v1 tag" << std::endl;
|
||||
|
||||
#ifdef TAGLIB_WITH_APE
|
||||
APE::Tag *ape = f.APETag();
|
||||
|
||||
cout << endl << "APE" << endl;
|
||||
std::cout << std::endl << "APE" << std::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)
|
||||
{
|
||||
cout << (*it).first << " - \"" << (*it).second.toString() << "\"" << endl;
|
||||
if((*it).second.type() != APE::Item::Binary)
|
||||
std::cout << (*it).first << " - \"" << (*it).second.toString() << "\"" << std::endl;
|
||||
else
|
||||
std::cout << (*it).first << " - Binary data (" << (*it).second.binaryData().size() << " bytes)" << std::endl;
|
||||
}
|
||||
}
|
||||
else
|
||||
cout << "file does not have a valid APE tag" << endl;
|
||||
std::cout << "file does not have a valid APE tag" << std::endl;
|
||||
#endif
|
||||
|
||||
cout << endl;
|
||||
std::cout << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
124
examples/matroskareader.cpp
Normal file
124
examples/matroskareader.cpp
Normal file
@@ -0,0 +1,124 @@
|
||||
#include <cstdio>
|
||||
#include "matroskafile.h"
|
||||
#include "matroskatag.h"
|
||||
#include "matroskasimpletag.h"
|
||||
#include "matroskaattachments.h"
|
||||
#include "matroskaattachedfile.h"
|
||||
#include "matroskachapters.h"
|
||||
#include "matroskachapteredition.h"
|
||||
#include "tstring.h"
|
||||
#include "tutils.h"
|
||||
#include "tbytevector.h"
|
||||
#define GREEN_TEXT(s) "[1;32m" s "[0m"
|
||||
#define PRINT_PRETTY(label, value) printf(" " GREEN_TEXT(label) ": %s\n", value)
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
if(argc != 2) {
|
||||
printf("Usage: matroskareader FILE\n");
|
||||
return 1;
|
||||
}
|
||||
TagLib::Matroska::File file(argv[1]);
|
||||
if(!file.isValid()) {
|
||||
printf("File is not valid\n");
|
||||
return 1;
|
||||
}
|
||||
auto tag = dynamic_cast<TagLib::Matroska::Tag*>(file.tag());
|
||||
if(!tag) {
|
||||
printf("File has no tag\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
const TagLib::Matroska::SimpleTagsList &list = tag->simpleTagsList();
|
||||
printf("Found %u tag(s):\n", list.size());
|
||||
|
||||
for(const TagLib::Matroska::SimpleTag &t : list) {
|
||||
PRINT_PRETTY("Tag Name", t.name().toCString(true));
|
||||
|
||||
if(t.type() == TagLib::Matroska::SimpleTag::StringType)
|
||||
PRINT_PRETTY("Tag Value", t.toString().toCString(true));
|
||||
else if(t.type() == TagLib::Matroska::SimpleTag::BinaryType)
|
||||
PRINT_PRETTY("Tag Value",
|
||||
TagLib::Utils::formatString("Binary with size %i", t.toByteVector().size()).toCString(false)
|
||||
);
|
||||
|
||||
auto targetTypeValue = static_cast<unsigned int>(t.targetTypeValue());
|
||||
PRINT_PRETTY("Target Type Value",
|
||||
targetTypeValue == 0 ? "None" : TagLib::Utils::formatString("%i", targetTypeValue).toCString(false)
|
||||
);
|
||||
if(auto trackUid = t.trackUid()) {
|
||||
PRINT_PRETTY("Track UID",
|
||||
TagLib::Utils::formatString("%llu",trackUid).toCString(false)
|
||||
);
|
||||
}
|
||||
const TagLib::String &language = t.language();
|
||||
PRINT_PRETTY("Language", !language.isEmpty() ? language.toCString(false) : "Not set");
|
||||
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
TagLib::Matroska::Attachments *attachments = file.attachments();
|
||||
if(attachments) {
|
||||
const TagLib::Matroska::Attachments::AttachedFileList &list = attachments->attachedFileList();
|
||||
printf("Found %u attachment(s)\n", list.size());
|
||||
for(const auto &attachedFile : list) {
|
||||
PRINT_PRETTY("Filename", attachedFile.fileName().toCString(true));
|
||||
const TagLib::String &description = attachedFile.description();
|
||||
PRINT_PRETTY("Description", !description.isEmpty() ? description.toCString(true) : "None");
|
||||
const TagLib::String &mediaType = attachedFile.mediaType();
|
||||
PRINT_PRETTY("Media Type", !mediaType.isEmpty() ? mediaType.toCString(false) : "None");
|
||||
PRINT_PRETTY("Data Size",
|
||||
TagLib::Utils::formatString("%u byte(s)", attachedFile.data().size()).toCString(false)
|
||||
);
|
||||
PRINT_PRETTY("UID",
|
||||
TagLib::Utils::formatString("%llu", attachedFile.uid()).toCString(false)
|
||||
);
|
||||
}
|
||||
}
|
||||
else
|
||||
printf("File has no attachments\n");
|
||||
|
||||
TagLib::Matroska::Chapters *chapters = file.chapters();
|
||||
if(chapters) {
|
||||
printf("Chapters:\n");
|
||||
const TagLib::Matroska::Chapters::ChapterEditionList &editions = chapters->chapterEditionList();
|
||||
for(const auto &edition : editions) {
|
||||
if(edition.uid()) {
|
||||
PRINT_PRETTY("Edition UID", TagLib::Utils::formatString("%llu", edition.uid())
|
||||
.toCString(false));
|
||||
}
|
||||
PRINT_PRETTY("Edition Flags", TagLib::Utils::formatString("default=%d, ordered=%d",
|
||||
edition.isDefault(), edition.isOrdered())
|
||||
.toCString(false));
|
||||
printf("\n");
|
||||
for(const auto &chapter : edition.chapterList()) {
|
||||
PRINT_PRETTY("Chapter UID", TagLib::Utils::formatString("%llu", chapter.uid())
|
||||
.toCString(false));
|
||||
PRINT_PRETTY("Chapter flags", TagLib::Utils::formatString("hidden=%d", chapter.isHidden())
|
||||
.toCString(false));
|
||||
PRINT_PRETTY("Start-End", TagLib::Utils::formatString("%llu-%llu",
|
||||
chapter.timeStart(), chapter.timeEnd()).toCString(false));
|
||||
for(const auto &display : chapter.displayList()) {
|
||||
PRINT_PRETTY("Display", display.string().toCString(false));
|
||||
PRINT_PRETTY("Language", !display.language().isEmpty()
|
||||
? display.language().toCString(false) : "Not set");
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
printf("File has no chapters\n");
|
||||
|
||||
if(auto properties = dynamic_cast<const TagLib::Matroska::Properties *>(file.audioProperties())) {
|
||||
printf("Properties:\n");
|
||||
PRINT_PRETTY("Doc Type", properties->docType().toCString(false));
|
||||
PRINT_PRETTY("Doc Type Version", TagLib::String::number(properties->docTypeVersion()).toCString(false));
|
||||
PRINT_PRETTY("Codec Name", properties->codecName().toCString(true));
|
||||
PRINT_PRETTY("Bitrate", TagLib::String::number(properties->bitrate()).toCString(false));
|
||||
PRINT_PRETTY("Sample Rate", TagLib::String::number(properties->sampleRate()).toCString(false));
|
||||
PRINT_PRETTY("Channels", TagLib::String::number(properties->channels()).toCString(false));
|
||||
PRINT_PRETTY("Length [ms]", TagLib::String::number(properties->lengthInMilliseconds()).toCString(false));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
45
examples/matroskawriter.cpp
Normal file
45
examples/matroskawriter.cpp
Normal file
@@ -0,0 +1,45 @@
|
||||
#include <cstdio>
|
||||
#include "matroskafile.h"
|
||||
#include "matroskatag.h"
|
||||
#include "matroskasimpletag.h"
|
||||
#include "matroskaattachments.h"
|
||||
#include "matroskaattachedfile.h"
|
||||
#include "tfilestream.h"
|
||||
#include "tstring.h"
|
||||
#include "tutils.h"
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
if(argc != 3) {
|
||||
printf("Usage: matroskawriter FILE ARTWORK\n");
|
||||
return 1;
|
||||
}
|
||||
TagLib::Matroska::File file(argv[1]);
|
||||
if(!file.isValid()) {
|
||||
printf("File is not valid\n");
|
||||
return 1;
|
||||
}
|
||||
auto tag = file.tag(true);
|
||||
tag->clearSimpleTags();
|
||||
|
||||
tag->addSimpleTag(TagLib::Matroska::SimpleTag(
|
||||
"Test Name 1", TagLib::String("Test Value 1"),
|
||||
TagLib::Matroska::SimpleTag::TargetTypeValue::Track, "en"));
|
||||
|
||||
tag->addSimpleTag(TagLib::Matroska::SimpleTag(
|
||||
"Test Name 2", TagLib::String("Test Value 2"),
|
||||
TagLib::Matroska::SimpleTag::TargetTypeValue::Album));
|
||||
tag->setTitle("Test title");
|
||||
tag->setArtist("Test artist");
|
||||
tag->setYear(1969);
|
||||
|
||||
TagLib::FileStream image(argv[2]);
|
||||
auto data = image.readBlock(image.length());
|
||||
auto attachments = file.attachments(true);
|
||||
attachments->addAttachedFile(TagLib::Matroska::AttachedFile(
|
||||
data, "cover.jpg", "image/jpeg"));
|
||||
|
||||
file.save();
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -23,8 +23,9 @@
|
||||
*/
|
||||
|
||||
#include <iostream>
|
||||
#include <mpegfile.h>
|
||||
#include <tstring.h>
|
||||
|
||||
#include "tstring.h"
|
||||
#include "mpegfile.h"
|
||||
|
||||
using namespace TagLib;
|
||||
|
||||
|
||||
@@ -23,25 +23,20 @@
|
||||
*/
|
||||
|
||||
#include <iostream>
|
||||
#include <stdio.h>
|
||||
#include <iomanip>
|
||||
#include <cstdio>
|
||||
|
||||
#include <fileref.h>
|
||||
#include <tag.h>
|
||||
|
||||
using namespace std;
|
||||
|
||||
TagLib::String formatSeconds(int seconds)
|
||||
{
|
||||
char secondsString[3];
|
||||
sprintf(secondsString, "%02i", seconds);
|
||||
return secondsString;
|
||||
}
|
||||
#include "tpropertymap.h"
|
||||
#include "tstringlist.h"
|
||||
#include "tvariant.h"
|
||||
#include "fileref.h"
|
||||
#include "tag.h"
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
for(int i = 1; i < argc; i++) {
|
||||
|
||||
cout << "******************** \"" << argv[i] << "\" ********************" << endl;
|
||||
std::cout << "******************** \"" << argv[i] << "\" ********************" << std::endl;
|
||||
|
||||
TagLib::FileRef f(argv[i]);
|
||||
|
||||
@@ -49,29 +44,77 @@ int main(int argc, char *argv[])
|
||||
|
||||
TagLib::Tag *tag = f.tag();
|
||||
|
||||
cout << "-- TAG --" << endl;
|
||||
cout << "title - \"" << tag->title() << "\"" << endl;
|
||||
cout << "artist - \"" << tag->artist() << "\"" << endl;
|
||||
cout << "album - \"" << tag->album() << "\"" << endl;
|
||||
cout << "year - \"" << tag->year() << "\"" << endl;
|
||||
cout << "comment - \"" << tag->comment() << "\"" << endl;
|
||||
cout << "track - \"" << tag->track() << "\"" << endl;
|
||||
cout << "genre - \"" << tag->genre() << "\"" << endl;
|
||||
std::cout << "-- TAG (basic) --" << std::endl;
|
||||
std::cout << "title - \"" << tag->title() << "\"" << std::endl;
|
||||
std::cout << "artist - \"" << tag->artist() << "\"" << std::endl;
|
||||
std::cout << "album - \"" << tag->album() << "\"" << std::endl;
|
||||
std::cout << "year - \"" << tag->year() << "\"" << std::endl;
|
||||
std::cout << "comment - \"" << tag->comment() << "\"" << std::endl;
|
||||
std::cout << "track - \"" << tag->track() << "\"" << std::endl;
|
||||
std::cout << "genre - \"" << tag->genre() << "\"" << std::endl;
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
std::cout << "-- TAG (properties) --" << std::endl;
|
||||
for(auto j = tags.cbegin(); j != tags.cend(); ++j) {
|
||||
for(auto k = j->second.begin(); k != j->second.end(); ++k) {
|
||||
std::cout << std::left << std::setfill(' ') << std::setw(longest) << j->first << " - " << '"' << *k << '"' << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TagLib::StringList names = f.complexPropertyKeys();
|
||||
for(const auto &name : names) {
|
||||
const auto& properties = f.complexProperties(name);
|
||||
for(const auto &property : properties) {
|
||||
std::cout << name << ":" << std::endl;
|
||||
for(const auto &[key, value] : property) {
|
||||
std::cout << " " << std::left << std::setfill(' ') << std::setw(11) << key << " - ";
|
||||
if(value.type() == TagLib::Variant::ByteVector) {
|
||||
std::cout << "(" << value.value<TagLib::ByteVector>().size() << " bytes)" << std::endl;
|
||||
/* The picture could be extracted using:
|
||||
std::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(), std::ios_base::out | std::ios_base::binary);
|
||||
picture << value.value<TagLib::ByteVector>();
|
||||
picture.close();
|
||||
*/
|
||||
}
|
||||
else {
|
||||
std::cout << value << std::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 << ":" << formatSeconds(seconds) << endl;
|
||||
std::cout << "-- AUDIO --" << std::endl;
|
||||
std::cout << "bitrate - " << properties->bitrate() << std::endl;
|
||||
std::cout << "sample rate - " << properties->sampleRate() << std::endl;
|
||||
std::cout << "channels - " << properties->channels() << std::endl;
|
||||
std::cout << "length - " << minutes << ":" << std::setfill('0') << std::setw(2) << std::right << seconds << std::endl;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -22,8 +22,10 @@
|
||||
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "tag_c.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <tag_c.h>
|
||||
#include <string.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);
|
||||
}
|
||||
|
||||
@@ -23,19 +23,21 @@
|
||||
*/
|
||||
|
||||
#include <iostream>
|
||||
#include <string.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <iomanip>
|
||||
#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>
|
||||
|
||||
using namespace std;
|
||||
#include "tlist.h"
|
||||
#include "tfile.h"
|
||||
#include "tpropertymap.h"
|
||||
#include "tvariant.h"
|
||||
#include "fileref.h"
|
||||
#include "tag.h"
|
||||
|
||||
bool isArgument(const char *s)
|
||||
{
|
||||
@@ -44,32 +46,152 @@ bool isArgument(const char *s)
|
||||
|
||||
bool isFile(const char *s)
|
||||
{
|
||||
struct stat st;
|
||||
#ifdef _WIN32
|
||||
return ::stat(s, &st) == 0 && (st.st_mode & (S_IFREG));
|
||||
struct _stat64 st;
|
||||
return ::_stat64(s, &st) == 0 && (st.st_mode & S_IFREG);
|
||||
#else
|
||||
struct stat st;
|
||||
return ::stat(s, &st) == 0 && (st.st_mode & (S_IFREG | S_IFLNK));
|
||||
#endif
|
||||
}
|
||||
|
||||
void usage()
|
||||
{
|
||||
cout << endl;
|
||||
cout << "Usage: tagwriter <fields> <files>" << endl;
|
||||
cout << endl;
|
||||
cout << "Where the valid fields are:" << endl;
|
||||
cout << " -t <title>" << endl;
|
||||
cout << " -a <artist>" << endl;
|
||||
cout << " -A <album>" << endl;
|
||||
cout << " -c <comment>" << endl;
|
||||
cout << " -g <genre>" << endl;
|
||||
cout << " -y <year>" << endl;
|
||||
cout << " -T <track>" << endl;
|
||||
cout << endl;
|
||||
std::cout << std::endl;
|
||||
std::cout << "Usage: tagwriter <fields> <files>" << std::endl;
|
||||
std::cout << std::endl;
|
||||
std::cout << "Where the valid fields are:" << std::endl;
|
||||
std::cout << " -t <title>" << std::endl;
|
||||
std::cout << " -a <artist>" << std::endl;
|
||||
std::cout << " -A <album>" << std::endl;
|
||||
std::cout << " -c <comment>" << std::endl;
|
||||
std::cout << " -g <genre>" << std::endl;
|
||||
std::cout << " -y <year>" << std::endl;
|
||||
std::cout << " -T <track>" << std::endl;
|
||||
std::cout << " -R <tagname> <tagvalue>" << std::endl;
|
||||
std::cout << " -I <tagname> <tagvalue>" << std::endl;
|
||||
std::cout << " -D <tagname>" << std::endl;
|
||||
std::cout << " -C <complex-property-key> <key1=val1,key2=val2,...>" << std::endl;
|
||||
std::cout << " -p <picturefile> <description> (\"\" \"\" to remove)" << std::endl;
|
||||
std::cout << std::endl;
|
||||
|
||||
exit(1);
|
||||
}
|
||||
|
||||
void checkForRejectedProperties(const TagLib::PropertyMap &tags)
|
||||
{ // stolen from tagreader.cpp
|
||||
if(tags.size() > 0) {
|
||||
unsigned int longest = 0;
|
||||
for(auto i = tags.begin(); i != tags.end(); ++i) {
|
||||
if(i->first.size() > longest) {
|
||||
longest = i->first.size();
|
||||
}
|
||||
}
|
||||
std::cout << "-- rejected TAGs (properties) --" << std::endl;
|
||||
for(auto i = tags.begin(); i != tags.end(); ++i) {
|
||||
for(auto j = i->second.begin(); j != i->second.end(); ++j) {
|
||||
std::cout << std::left << std::setw(longest) << i->first << " - " << '"' << *j << '"' << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
* Create a list of variant maps from a string.
|
||||
* The shorthand syntax in the string is kept simple, but should be sufficient
|
||||
* for testing. Multiple maps are separated by ';', values within a map are
|
||||
* assigned with key=value and separated by a ','. Types are detected, use
|
||||
* double quotes to force a string. A ByteVector can be constructed from the
|
||||
* contents of a file, the path is given after "file://". There is no escape
|
||||
* character, use hex codes for ',' (\x2C) or ';' (\x3B).
|
||||
*/
|
||||
TagLib::List<TagLib::VariantMap> parseComplexPropertyValues(const TagLib::String &str)
|
||||
{
|
||||
if(str.isEmpty() || str == "\"\"" || str == "''") {
|
||||
return {};
|
||||
}
|
||||
TagLib::List<TagLib::VariantMap> values;
|
||||
const auto valueStrs = str.split(";");
|
||||
for(const auto &valueStr : valueStrs) {
|
||||
TagLib::VariantMap value;
|
||||
const auto keyValStrs = valueStr.split(",");
|
||||
for(const auto &keyValStr : keyValStrs) {
|
||||
if(int equalPos = keyValStr.find('='); equalPos != -1) {
|
||||
TagLib::String key = keyValStr.substr(0, equalPos);
|
||||
TagLib::String valStr = keyValStr.substr(equalPos + 1);
|
||||
bool hasDot = false;
|
||||
bool hasNonNumeric = false;
|
||||
bool hasSign = false;
|
||||
for(auto it = valStr.cbegin(); it != valStr.cend(); ++it) {
|
||||
if(it == valStr.cbegin() && (*it == '-' || *it == '+')) {
|
||||
hasSign = true;
|
||||
}
|
||||
else if(*it == '.') {
|
||||
hasDot = true;
|
||||
}
|
||||
else if(*it < '0' || *it > '9') {
|
||||
hasNonNumeric = true;
|
||||
}
|
||||
}
|
||||
TagLib::Variant val;
|
||||
if(valStr == "null") {
|
||||
// keep empty variant
|
||||
}
|
||||
else if(valStr == "true" || valStr == "false") {
|
||||
val = TagLib::Variant(valStr == "true");
|
||||
}
|
||||
else if(!hasNonNumeric && hasDot) {
|
||||
val = TagLib::Variant(std::stod(valStr.to8Bit()));
|
||||
}
|
||||
else if(!hasNonNumeric && hasSign) {
|
||||
val = valStr.toLongLong(nullptr);
|
||||
}
|
||||
else if(!hasNonNumeric) {
|
||||
val = valStr.toULongLong(nullptr);
|
||||
}
|
||||
else if(valStr.startsWith("file://")) {
|
||||
auto filePath = valStr.substr(7 );
|
||||
if(isFile(filePath.toCString())) {
|
||||
std::ifstream fs;
|
||||
fs.open(filePath.toCString(), std::ios::in | std::ios::binary);
|
||||
std::stringstream buffer;
|
||||
buffer << fs.rdbuf();
|
||||
fs.close();
|
||||
TagLib::String buf(buffer.str());
|
||||
val = TagLib::Variant(buf.data(TagLib::String::Latin1));
|
||||
}
|
||||
else {
|
||||
std::cout << filePath.toCString() << " not found." << std::endl;
|
||||
val = TagLib::Variant(TagLib::ByteVector());
|
||||
}
|
||||
}
|
||||
else {
|
||||
int len = valStr.size();
|
||||
if(len >= 2 && valStr[0] == '"' && valStr[len - 1] == '"') {
|
||||
valStr = valStr.substr(1, len - 2);
|
||||
}
|
||||
int hexPos = 0;
|
||||
while((hexPos = valStr.find("\\x", hexPos)) != -1) {
|
||||
char ch;
|
||||
bool ok;
|
||||
if(static_cast<int>(valStr.length()) < hexPos + 4 ||
|
||||
(ch = static_cast<char>(
|
||||
valStr.substr(hexPos + 2, 2).toLongLong(&ok, 16)), !ok)) {
|
||||
break;
|
||||
}
|
||||
valStr = valStr.substr(0, hexPos) + ch + valStr.substr(hexPos + 4);
|
||||
++hexPos;
|
||||
}
|
||||
val = TagLib::Variant(valStr);
|
||||
}
|
||||
value.insert(key, val);
|
||||
}
|
||||
}
|
||||
values.append(value);
|
||||
}
|
||||
return values;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
TagLib::List<TagLib::FileRef> fileList;
|
||||
@@ -87,17 +209,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>::Iterator 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':
|
||||
@@ -121,19 +244,92 @@ int main(int argc, char *argv[])
|
||||
case 'T':
|
||||
t->setTrack(value.toInt());
|
||||
break;
|
||||
case 'R':
|
||||
case 'I':
|
||||
if(i + 2 < argc) {
|
||||
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]));
|
||||
}
|
||||
numArgsConsumed = 3;
|
||||
checkForRejectedProperties(f.setProperties(map));
|
||||
}
|
||||
else {
|
||||
usage();
|
||||
}
|
||||
break;
|
||||
case 'D': {
|
||||
TagLib::PropertyMap map = f.properties();
|
||||
map.erase(value);
|
||||
checkForRejectedProperties(f.setProperties(map));
|
||||
break;
|
||||
}
|
||||
case 'C': {
|
||||
if(i + 2 < argc) {
|
||||
numArgsConsumed = 3;
|
||||
if(!value.isEmpty()) {
|
||||
TagLib::List<TagLib::VariantMap> values = parseComplexPropertyValues(argv[i + 2]);
|
||||
f.setComplexProperties(value, values);
|
||||
}
|
||||
}
|
||||
else {
|
||||
usage();
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'p': {
|
||||
if(i + 2 < argc) {
|
||||
numArgsConsumed = 3;
|
||||
if(!value.isEmpty()) {
|
||||
if(!isFile(value.toCString())) {
|
||||
std::cout << value.toCString() << " not found." << std::endl;
|
||||
return 1;
|
||||
}
|
||||
std::ifstream picture;
|
||||
picture.open(value.toCString(), std::ios::in | std::ios::binary);
|
||||
std::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:
|
||||
usage();
|
||||
break;
|
||||
}
|
||||
}
|
||||
i += numArgsConsumed;
|
||||
}
|
||||
else
|
||||
usage();
|
||||
}
|
||||
|
||||
TagLib::List<TagLib::FileRef>::Iterator 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_MAJOR_VERSION}.${TAGLIB_LIB_MINOR_VERSION}.${TAGLIB_LIB_PATCH_VERSION}
|
||||
;;
|
||||
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()
|
||||
35
taglib-config.cmd.cmake
Normal file
35
taglib-config.cmd.cmake
Normal file
@@ -0,0 +1,35 @@
|
||||
@echo off
|
||||
goto beginning
|
||||
*
|
||||
* It is what it is, you can do with it as you please.
|
||||
*
|
||||
* Just don't blame me if it teaches your computer to smoke!
|
||||
*
|
||||
* -Enjoy
|
||||
* fh :)_~
|
||||
*
|
||||
:beginning
|
||||
if /i "%1#" == "--libs#" goto doit
|
||||
if /i "%1#" == "--cflags#" goto doit
|
||||
if /i "%1#" == "--version#" goto doit
|
||||
if /i "%1#" == "--prefix#" goto doit
|
||||
|
||||
echo usage: %0 [OPTIONS]
|
||||
echo [--libs]
|
||||
echo [--cflags]
|
||||
echo [--version]
|
||||
echo [--prefix]
|
||||
goto theend
|
||||
|
||||
*
|
||||
* NOTE: Windows does not assume libraries are prefixed with 'lib'.
|
||||
* NOTE: If '-llibtag' is the last element, it is easily appended in the users installation/makefile process
|
||||
* to allow for static, shared or debug builds.
|
||||
* It would be preferable if the top level CMakeLists.txt provided the library name during config. ??
|
||||
:doit
|
||||
if /i "%1#" == "--libs#" echo -L${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_MAJOR_VERSION}.${TAGLIB_LIB_MINOR_VERSION}.${TAGLIB_LIB_PATCH_VERSION}
|
||||
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@
|
||||
|
||||
@@ -1,40 +1,84 @@
|
||||
set(CMAKE_INCLUDE_CURRENT_DIR ON)
|
||||
include_directories(
|
||||
set(tag_HDR_DIRS
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/toolkit
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/asf
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/mpeg
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/ogg
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/ogg/flac
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/flac
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/mpc
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/mp4
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/ogg/vorbis
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/ogg/speex
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/mpeg/id3v2
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/mpeg/id3v2/frames
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/mpeg/id3v1
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/ape
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/wavpack
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/trueaudio
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/riff
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/riff/aiff
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/riff/wav
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/mod
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/s3m
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/it
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/xm
|
||||
)
|
||||
|
||||
if(ZLIB_FOUND)
|
||||
include_directories(${ZLIB_INCLUDE_DIR})
|
||||
if(WITH_ASF)
|
||||
set(tag_HDR_DIRS ${tag_HDR_DIRS}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/asf
|
||||
)
|
||||
endif()
|
||||
if(WITH_VORBIS)
|
||||
set(tag_HDR_DIRS ${tag_HDR_DIRS}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/ogg
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/ogg/flac
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/flac
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/ogg/vorbis
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/ogg/speex
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/ogg/opus
|
||||
)
|
||||
endif()
|
||||
if(WITH_APE)
|
||||
set(tag_HDR_DIRS ${tag_HDR_DIRS}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/mpc
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/ape
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/wavpack
|
||||
)
|
||||
endif()
|
||||
if(WITH_MP4)
|
||||
set(tag_HDR_DIRS ${tag_HDR_DIRS}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/mp4
|
||||
)
|
||||
endif()
|
||||
if(WITH_TRUEAUDIO)
|
||||
set(tag_HDR_DIRS ${tag_HDR_DIRS}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/trueaudio
|
||||
)
|
||||
endif()
|
||||
if(WITH_RIFF)
|
||||
set(tag_HDR_DIRS ${tag_HDR_DIRS}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/riff
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/riff/aiff
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/riff/wav
|
||||
)
|
||||
endif()
|
||||
if(WITH_MOD)
|
||||
set(tag_HDR_DIRS ${tag_HDR_DIRS}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/mod
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/s3m
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/it
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/xm
|
||||
)
|
||||
endif()
|
||||
if(WITH_DSF)
|
||||
set(tag_HDR_DIRS ${tag_HDR_DIRS}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/dsf
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/dsdiff
|
||||
)
|
||||
endif()
|
||||
if(WITH_SHORTEN)
|
||||
set(tag_HDR_DIRS ${tag_HDR_DIRS}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/shorten
|
||||
)
|
||||
endif()
|
||||
if(WITH_MATROSKA)
|
||||
set(tag_HDR_DIRS ${tag_HDR_DIRS}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/matroska
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/matroska/ebml
|
||||
)
|
||||
endif()
|
||||
include_directories(${tag_HDR_DIRS})
|
||||
|
||||
set(tag_PRIVATE_HDRS)
|
||||
set(tag_HDRS
|
||||
tag.h
|
||||
fileref.h
|
||||
audioproperties.h
|
||||
taglib_export.h
|
||||
${CMAKE_BINARY_DIR}/taglib_config.h
|
||||
${CMAKE_CURRENT_BINARY_DIR}/../taglib_config.h
|
||||
toolkit/taglib.h
|
||||
toolkit/tstring.h
|
||||
toolkit/tlist.h
|
||||
@@ -42,19 +86,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/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
|
||||
@@ -64,66 +113,156 @@ set(tag_HDRS
|
||||
mpeg/id3v2/id3v2tag.h
|
||||
mpeg/id3v2/frames/attachedpictureframe.h
|
||||
mpeg/id3v2/frames/commentsframe.h
|
||||
mpeg/id3v2/frames/eventtimingcodesframe.h
|
||||
mpeg/id3v2/frames/generalencapsulatedobjectframe.h
|
||||
mpeg/id3v2/frames/ownershipframe.h
|
||||
mpeg/id3v2/frames/popularimeterframe.h
|
||||
mpeg/id3v2/frames/privateframe.h
|
||||
mpeg/id3v2/frames/relativevolumeframe.h
|
||||
mpeg/id3v2/frames/synchronizedlyricsframe.h
|
||||
mpeg/id3v2/frames/textidentificationframe.h
|
||||
mpeg/id3v2/frames/uniquefileidentifierframe.h
|
||||
mpeg/id3v2/frames/unknownframe.h
|
||||
mpeg/id3v2/frames/unsynchronizedlyricsframe.h
|
||||
mpeg/id3v2/frames/urllinkframe.h
|
||||
ogg/oggfile.h
|
||||
ogg/oggpage.h
|
||||
ogg/oggpageheader.h
|
||||
ogg/xiphcomment.h
|
||||
ogg/vorbis/vorbisfile.h
|
||||
ogg/vorbis/vorbisproperties.h
|
||||
ogg/flac/oggflacfile.h
|
||||
ogg/speex/speexfile.h
|
||||
ogg/speex/speexproperties.h
|
||||
flac/flacfile.h
|
||||
flac/flacpicture.h
|
||||
flac/flacproperties.h
|
||||
flac/flacmetadatablock.h
|
||||
ape/apefile.h
|
||||
ape/apeproperties.h
|
||||
ape/apetag.h
|
||||
ape/apefooter.h
|
||||
ape/apeitem.h
|
||||
mpc/mpcfile.h
|
||||
mpc/mpcproperties.h
|
||||
wavpack/wavpackfile.h
|
||||
wavpack/wavpackproperties.h
|
||||
trueaudio/trueaudiofile.h
|
||||
trueaudio/trueaudioproperties.h
|
||||
riff/rifffile.h
|
||||
riff/aiff/aifffile.h
|
||||
riff/aiff/aiffproperties.h
|
||||
riff/wav/wavfile.h
|
||||
riff/wav/wavproperties.h
|
||||
asf/asffile.h
|
||||
asf/asfproperties.h
|
||||
asf/asftag.h
|
||||
asf/asfattribute.h
|
||||
asf/asfpicture.h
|
||||
mp4/mp4file.h
|
||||
mp4/mp4atom.h
|
||||
mp4/mp4tag.h
|
||||
mp4/mp4item.h
|
||||
mp4/mp4properties.h
|
||||
mp4/mp4coverart.h
|
||||
mod/modfilebase.h
|
||||
mod/modfile.h
|
||||
mod/modtag.h
|
||||
mod/modproperties.h
|
||||
it/itfile.h
|
||||
it/itproperties.h
|
||||
s3m/s3mfile.h
|
||||
s3m/s3mproperties.h
|
||||
xm/xmfile.h
|
||||
xm/xmproperties.h
|
||||
mpeg/id3v2/frames/chapterframe.h
|
||||
mpeg/id3v2/frames/tableofcontentsframe.h
|
||||
mpeg/id3v2/frames/podcastframe.h
|
||||
)
|
||||
if(WITH_VORBIS)
|
||||
set(tag_HDRS ${tag_HDRS}
|
||||
ogg/oggfile.h
|
||||
ogg/oggpage.h
|
||||
ogg/oggpageheader.h
|
||||
ogg/xiphcomment.h
|
||||
ogg/vorbis/vorbisfile.h
|
||||
ogg/vorbis/vorbisproperties.h
|
||||
ogg/flac/oggflacfile.h
|
||||
ogg/speex/speexfile.h
|
||||
ogg/speex/speexproperties.h
|
||||
ogg/opus/opusfile.h
|
||||
ogg/opus/opusproperties.h
|
||||
flac/flacfile.h
|
||||
flac/flacpicture.h
|
||||
flac/flacproperties.h
|
||||
flac/flacmetadatablock.h
|
||||
)
|
||||
endif()
|
||||
if(WITH_APE)
|
||||
set(tag_HDRS ${tag_HDRS}
|
||||
ape/apefile.h
|
||||
ape/apeproperties.h
|
||||
ape/apetag.h
|
||||
ape/apefooter.h
|
||||
ape/apeitem.h
|
||||
mpc/mpcfile.h
|
||||
mpc/mpcproperties.h
|
||||
wavpack/wavpackfile.h
|
||||
wavpack/wavpackproperties.h
|
||||
)
|
||||
endif()
|
||||
if(WITH_TRUEAUDIO)
|
||||
set(tag_HDRS ${tag_HDRS}
|
||||
trueaudio/trueaudiofile.h
|
||||
trueaudio/trueaudioproperties.h
|
||||
)
|
||||
endif()
|
||||
if(WITH_RIFF)
|
||||
set(tag_HDRS ${tag_HDRS}
|
||||
riff/rifffile.h
|
||||
riff/aiff/aifffile.h
|
||||
riff/aiff/aiffproperties.h
|
||||
riff/wav/wavfile.h
|
||||
riff/wav/wavproperties.h
|
||||
riff/wav/infotag.h
|
||||
)
|
||||
endif()
|
||||
if(WITH_ASF)
|
||||
set(tag_HDRS ${tag_HDRS}
|
||||
asf/asffile.h
|
||||
asf/asfproperties.h
|
||||
asf/asftag.h
|
||||
asf/asfattribute.h
|
||||
asf/asfpicture.h
|
||||
)
|
||||
endif()
|
||||
if(WITH_MP4)
|
||||
set(tag_HDRS ${tag_HDRS}
|
||||
mp4/mp4file.h
|
||||
mp4/mp4atom.h
|
||||
mp4/mp4tag.h
|
||||
mp4/mp4item.h
|
||||
mp4/mp4properties.h
|
||||
mp4/mp4coverart.h
|
||||
mp4/mp4stem.h
|
||||
mp4/mp4itemfactory.h
|
||||
)
|
||||
endif()
|
||||
if(WITH_MOD)
|
||||
set(tag_HDRS ${tag_HDRS}
|
||||
mod/modfilebase.h
|
||||
mod/modfile.h
|
||||
mod/modtag.h
|
||||
mod/modproperties.h
|
||||
it/itfile.h
|
||||
it/itproperties.h
|
||||
s3m/s3mfile.h
|
||||
s3m/s3mproperties.h
|
||||
xm/xmfile.h
|
||||
xm/xmproperties.h
|
||||
)
|
||||
endif()
|
||||
if(WITH_DSF)
|
||||
set(tag_HDRS ${tag_HDRS}
|
||||
dsf/dsffile.h
|
||||
dsf/dsfproperties.h
|
||||
dsdiff/dsdifffile.h
|
||||
dsdiff/dsdiffproperties.h
|
||||
dsdiff/dsdiffdiintag.h
|
||||
)
|
||||
endif()
|
||||
if(WITH_SHORTEN)
|
||||
set(tag_HDRS ${tag_HDRS}
|
||||
shorten/shortenfile.h
|
||||
shorten/shortenproperties.h
|
||||
shorten/shortentag.h
|
||||
)
|
||||
endif()
|
||||
if(WITH_MATROSKA)
|
||||
set(tag_HDRS ${tag_HDRS}
|
||||
matroska/matroskaattachedfile.h
|
||||
matroska/matroskaattachments.h
|
||||
matroska/matroskachapter.h
|
||||
matroska/matroskachapteredition.h
|
||||
matroska/matroskachapters.h
|
||||
matroska/matroskaelement.h
|
||||
matroska/matroskafile.h
|
||||
matroska/matroskaproperties.h
|
||||
matroska/matroskasimpletag.h
|
||||
matroska/matroskatag.h
|
||||
)
|
||||
set(tag_PRIVATE_HDRS ${tag_PRIVATE_HDRS}
|
||||
matroska/ebml/ebmlbinaryelement.h
|
||||
matroska/ebml/ebmlelement.h
|
||||
matroska/ebml/ebmlmasterelement.h
|
||||
matroska/ebml/ebmlmkattachments.h
|
||||
matroska/ebml/ebmlmkchapters.h
|
||||
matroska/ebml/ebmlmkcues.h
|
||||
matroska/ebml/ebmlmkseekhead.h
|
||||
matroska/ebml/ebmlmksegment.h
|
||||
matroska/ebml/ebmlmkinfo.h
|
||||
matroska/ebml/ebmlmktracks.h
|
||||
matroska/ebml/ebmlmktags.h
|
||||
matroska/ebml/ebmlstringelement.h
|
||||
matroska/ebml/ebmluintelement.h
|
||||
matroska/ebml/ebmlfloatelement.h
|
||||
matroska/ebml/ebmlutils.h
|
||||
matroska/ebml/ebmlvoidelement.h
|
||||
matroska/matroskacues.h
|
||||
matroska/matroskaseekhead.h
|
||||
matroska/matroskasegment.h
|
||||
)
|
||||
endif()
|
||||
|
||||
set(mpeg_SRCS
|
||||
mpeg/mpegfile.cpp
|
||||
@@ -150,170 +289,314 @@ set(id3v2_SRCS
|
||||
set(frames_SRCS
|
||||
mpeg/id3v2/frames/attachedpictureframe.cpp
|
||||
mpeg/id3v2/frames/commentsframe.cpp
|
||||
mpeg/id3v2/frames/eventtimingcodesframe.cpp
|
||||
mpeg/id3v2/frames/generalencapsulatedobjectframe.cpp
|
||||
mpeg/id3v2/frames/ownershipframe.cpp
|
||||
mpeg/id3v2/frames/popularimeterframe.cpp
|
||||
mpeg/id3v2/frames/privateframe.cpp
|
||||
mpeg/id3v2/frames/relativevolumeframe.cpp
|
||||
mpeg/id3v2/frames/synchronizedlyricsframe.cpp
|
||||
mpeg/id3v2/frames/textidentificationframe.cpp
|
||||
mpeg/id3v2/frames/uniquefileidentifierframe.cpp
|
||||
mpeg/id3v2/frames/unknownframe.cpp
|
||||
mpeg/id3v2/frames/unsynchronizedlyricsframe.cpp
|
||||
mpeg/id3v2/frames/urllinkframe.cpp
|
||||
mpeg/id3v2/frames/chapterframe.cpp
|
||||
mpeg/id3v2/frames/tableofcontentsframe.cpp
|
||||
mpeg/id3v2/frames/podcastframe.cpp
|
||||
)
|
||||
|
||||
set(ogg_SRCS
|
||||
ogg/oggfile.cpp
|
||||
ogg/oggpage.cpp
|
||||
ogg/oggpageheader.cpp
|
||||
ogg/xiphcomment.cpp
|
||||
)
|
||||
if(WITH_VORBIS)
|
||||
set(ogg_SRCS
|
||||
ogg/oggfile.cpp
|
||||
ogg/oggpage.cpp
|
||||
ogg/oggpageheader.cpp
|
||||
ogg/xiphcomment.cpp
|
||||
)
|
||||
|
||||
set(vorbis_SRCS
|
||||
ogg/vorbis/vorbisfile.cpp
|
||||
ogg/vorbis/vorbisproperties.cpp
|
||||
)
|
||||
set(vorbis_SRCS
|
||||
ogg/vorbis/vorbisfile.cpp
|
||||
ogg/vorbis/vorbisproperties.cpp
|
||||
)
|
||||
|
||||
set(flacs_SRCS
|
||||
flac/flacfile.cpp
|
||||
flac/flacpicture.cpp
|
||||
flac/flacproperties.cpp
|
||||
flac/flacmetadatablock.cpp
|
||||
flac/flacunknownmetadatablock.cpp
|
||||
)
|
||||
set(flacs_SRCS
|
||||
flac/flacfile.cpp
|
||||
flac/flacpicture.cpp
|
||||
flac/flacproperties.cpp
|
||||
flac/flacmetadatablock.cpp
|
||||
flac/flacunknownmetadatablock.cpp
|
||||
)
|
||||
|
||||
set(oggflacs_SRCS
|
||||
ogg/flac/oggflacfile.cpp
|
||||
)
|
||||
set(oggflacs_SRCS
|
||||
ogg/flac/oggflacfile.cpp
|
||||
)
|
||||
|
||||
set(mpc_SRCS
|
||||
mpc/mpcfile.cpp
|
||||
mpc/mpcproperties.cpp
|
||||
)
|
||||
set(speex_SRCS
|
||||
ogg/speex/speexfile.cpp
|
||||
ogg/speex/speexproperties.cpp
|
||||
)
|
||||
|
||||
set(mp4_SRCS
|
||||
mp4/mp4file.cpp
|
||||
mp4/mp4atom.cpp
|
||||
mp4/mp4tag.cpp
|
||||
mp4/mp4item.cpp
|
||||
mp4/mp4properties.cpp
|
||||
mp4/mp4coverart.cpp
|
||||
)
|
||||
set(opus_SRCS
|
||||
ogg/opus/opusfile.cpp
|
||||
ogg/opus/opusproperties.cpp
|
||||
)
|
||||
endif()
|
||||
|
||||
set(ape_SRCS
|
||||
ape/apetag.cpp
|
||||
ape/apefooter.cpp
|
||||
ape/apeitem.cpp
|
||||
ape/apefile.cpp
|
||||
ape/apeproperties.cpp
|
||||
)
|
||||
if(WITH_APE)
|
||||
set(mpc_SRCS
|
||||
mpc/mpcfile.cpp
|
||||
mpc/mpcproperties.cpp
|
||||
)
|
||||
|
||||
set(wavpack_SRCS
|
||||
wavpack/wavpackfile.cpp
|
||||
wavpack/wavpackproperties.cpp
|
||||
)
|
||||
set(ape_SRCS
|
||||
ape/apetag.cpp
|
||||
ape/apefooter.cpp
|
||||
ape/apeitem.cpp
|
||||
ape/apefile.cpp
|
||||
ape/apeproperties.cpp
|
||||
)
|
||||
|
||||
set(speex_SRCS
|
||||
ogg/speex/speexfile.cpp
|
||||
ogg/speex/speexproperties.cpp
|
||||
)
|
||||
set(wavpack_SRCS
|
||||
wavpack/wavpackfile.cpp
|
||||
wavpack/wavpackproperties.cpp
|
||||
)
|
||||
endif()
|
||||
|
||||
set(trueaudio_SRCS
|
||||
trueaudio/trueaudiofile.cpp
|
||||
trueaudio/trueaudioproperties.cpp
|
||||
)
|
||||
if(WITH_MP4)
|
||||
set(mp4_SRCS
|
||||
mp4/mp4file.cpp
|
||||
mp4/mp4atom.cpp
|
||||
mp4/mp4tag.cpp
|
||||
mp4/mp4item.cpp
|
||||
mp4/mp4properties.cpp
|
||||
mp4/mp4coverart.cpp
|
||||
mp4/mp4stem.cpp
|
||||
mp4/mp4itemfactory.cpp
|
||||
)
|
||||
endif()
|
||||
|
||||
set(asf_SRCS
|
||||
asf/asftag.cpp
|
||||
asf/asffile.cpp
|
||||
asf/asfproperties.cpp
|
||||
asf/asfattribute.cpp
|
||||
asf/asfpicture.cpp
|
||||
)
|
||||
if(WITH_TRUEAUDIO)
|
||||
set(trueaudio_SRCS
|
||||
trueaudio/trueaudiofile.cpp
|
||||
trueaudio/trueaudioproperties.cpp
|
||||
)
|
||||
endif()
|
||||
|
||||
set(riff_SRCS
|
||||
riff/rifffile.cpp
|
||||
)
|
||||
if(WITH_ASF)
|
||||
set(asf_SRCS
|
||||
asf/asftag.cpp
|
||||
asf/asffile.cpp
|
||||
asf/asfproperties.cpp
|
||||
asf/asfattribute.cpp
|
||||
asf/asfpicture.cpp
|
||||
)
|
||||
endif()
|
||||
|
||||
set(aiff_SRCS
|
||||
riff/aiff/aifffile.cpp
|
||||
riff/aiff/aiffproperties.cpp
|
||||
)
|
||||
if(WITH_RIFF)
|
||||
set(riff_SRCS
|
||||
riff/rifffile.cpp
|
||||
)
|
||||
|
||||
set(wav_SRCS
|
||||
riff/wav/wavfile.cpp
|
||||
riff/wav/wavproperties.cpp
|
||||
)
|
||||
set(aiff_SRCS
|
||||
riff/aiff/aifffile.cpp
|
||||
riff/aiff/aiffproperties.cpp
|
||||
)
|
||||
|
||||
set(mod_SRCS
|
||||
mod/modfilebase.cpp
|
||||
mod/modfile.cpp
|
||||
mod/modtag.cpp
|
||||
mod/modproperties.cpp
|
||||
)
|
||||
set(wav_SRCS
|
||||
riff/wav/wavfile.cpp
|
||||
riff/wav/wavproperties.cpp
|
||||
riff/wav/infotag.cpp
|
||||
)
|
||||
endif()
|
||||
|
||||
set(s3m_SRCS
|
||||
s3m/s3mfile.cpp
|
||||
s3m/s3mproperties.cpp
|
||||
)
|
||||
if(WITH_MOD)
|
||||
set(mod_SRCS
|
||||
mod/modfilebase.cpp
|
||||
mod/modfile.cpp
|
||||
mod/modtag.cpp
|
||||
mod/modproperties.cpp
|
||||
)
|
||||
|
||||
set(it_SRCS
|
||||
it/itfile.cpp
|
||||
it/itproperties.cpp
|
||||
)
|
||||
set(s3m_SRCS
|
||||
s3m/s3mfile.cpp
|
||||
s3m/s3mproperties.cpp
|
||||
)
|
||||
|
||||
set(xm_SRCS
|
||||
xm/xmfile.cpp
|
||||
xm/xmproperties.cpp
|
||||
)
|
||||
set(it_SRCS
|
||||
it/itfile.cpp
|
||||
it/itproperties.cpp
|
||||
)
|
||||
|
||||
set(xm_SRCS
|
||||
xm/xmfile.cpp
|
||||
xm/xmproperties.cpp
|
||||
)
|
||||
endif()
|
||||
|
||||
if(WITH_DSF)
|
||||
set(dsf_SRCS
|
||||
dsf/dsffile.cpp
|
||||
dsf/dsfproperties.cpp
|
||||
)
|
||||
|
||||
set(dsdiff_SRCS
|
||||
dsdiff/dsdifffile.cpp
|
||||
dsdiff/dsdiffproperties.cpp
|
||||
dsdiff/dsdiffdiintag.cpp
|
||||
)
|
||||
endif()
|
||||
|
||||
if(WITH_SHORTEN)
|
||||
set(shorten_SRCS
|
||||
shorten/shortenfile.cpp
|
||||
shorten/shortenproperties.cpp
|
||||
shorten/shortentag.cpp
|
||||
)
|
||||
endif()
|
||||
|
||||
if(WITH_MATROSKA)
|
||||
set(matroska_SRCS
|
||||
matroska/matroskaattachedfile.cpp
|
||||
matroska/matroskaattachments.cpp
|
||||
matroska/matroskachapter.cpp
|
||||
matroska/matroskachapteredition.cpp
|
||||
matroska/matroskachapters.cpp
|
||||
matroska/matroskacues.cpp
|
||||
matroska/matroskaelement.cpp
|
||||
matroska/matroskafile.cpp
|
||||
matroska/matroskaproperties.cpp
|
||||
matroska/matroskaseekhead.cpp
|
||||
matroska/matroskasegment.cpp
|
||||
matroska/matroskasimpletag.cpp
|
||||
matroska/matroskatag.cpp
|
||||
)
|
||||
|
||||
set(ebml_SRCS
|
||||
matroska/ebml/ebmlbinaryelement.cpp
|
||||
matroska/ebml/ebmlelement.cpp
|
||||
matroska/ebml/ebmlmasterelement.cpp
|
||||
matroska/ebml/ebmlmkattachments.cpp
|
||||
matroska/ebml/ebmlmkchapters.cpp
|
||||
matroska/ebml/ebmlmkcues.cpp
|
||||
matroska/ebml/ebmlmkseekhead.cpp
|
||||
matroska/ebml/ebmlmksegment.cpp
|
||||
matroska/ebml/ebmlmkinfo.cpp
|
||||
matroska/ebml/ebmlmktracks.cpp
|
||||
matroska/ebml/ebmlmktags.cpp
|
||||
matroska/ebml/ebmlstringelement.cpp
|
||||
matroska/ebml/ebmluintelement.cpp
|
||||
matroska/ebml/ebmlfloatelement.cpp
|
||||
matroska/ebml/ebmlutils.cpp
|
||||
matroska/ebml/ebmlvoidelement.cpp
|
||||
)
|
||||
endif()
|
||||
|
||||
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/unicode.cpp
|
||||
toolkit/tdebuglistener.cpp
|
||||
toolkit/tzlib.cpp
|
||||
toolkit/tversionnumber.cpp
|
||||
)
|
||||
|
||||
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}
|
||||
${asf_SRCS} ${mp4_SRCS} ${mod_SRCS} ${s3m_SRCS} ${it_SRCS} ${xm_SRCS} ${opus_SRCS}
|
||||
${dsf_SRCS} ${dsdiff_SRCS} ${shorten_SRCS} ${matroska_SRCS} ${ebml_SRCS}
|
||||
tag.cpp
|
||||
tagunion.cpp
|
||||
fileref.cpp
|
||||
audioproperties.cpp
|
||||
tagutils.cpp
|
||||
)
|
||||
|
||||
add_library(tag ${tag_LIB_SRCS} ${tag_HDRS})
|
||||
add_library(tag ${tag_LIB_SRCS} ${tag_HDRS} ${tag_PRIVATE_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 $<IF:$<TARGET_EXISTS:utf8::cpp>,utf8::cpp,$<$<TARGET_EXISTS:utf8cpp>:utf8cpp>>
|
||||
$<$<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(NOT BUILD_SHARED_LIBS)
|
||||
target_compile_definitions(tag PUBLIC TAGLIB_STATIC)
|
||||
endif()
|
||||
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
|
||||
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}
|
||||
)
|
||||
|
||||
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,15 +31,14 @@
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#include <tbytevector.h>
|
||||
#include <tstring.h>
|
||||
#include <tdebug.h>
|
||||
#include <tagunion.h>
|
||||
#include <id3v1tag.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"
|
||||
|
||||
@@ -47,63 +46,59 @@ using namespace TagLib;
|
||||
|
||||
namespace
|
||||
{
|
||||
enum { ApeAPEIndex, ApeID3v1Index };
|
||||
}
|
||||
enum { ApeAPEIndex = 0, ApeID3v1Index = 1 };
|
||||
} // namespace
|
||||
|
||||
class APE::File::FilePrivate
|
||||
{
|
||||
public:
|
||||
FilePrivate() :
|
||||
APELocation(-1),
|
||||
APESize(0),
|
||||
ID3v1Location(-1),
|
||||
properties(0),
|
||||
hasAPE(false),
|
||||
hasID3v1(false) {}
|
||||
offset_t APELocation { -1 };
|
||||
long APESize { 0 };
|
||||
|
||||
~FilePrivate()
|
||||
{
|
||||
delete properties;
|
||||
}
|
||||
offset_t ID3v1Location { -1 };
|
||||
|
||||
long APELocation;
|
||||
uint APESize;
|
||||
|
||||
long ID3v1Location;
|
||||
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;
|
||||
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 propertiesStyle) : TagLib::File(file)
|
||||
APE::File::File(FileName file, bool readProperties, Properties::ReadStyle) :
|
||||
TagLib::File(file),
|
||||
d(std::make_unique<FilePrivate>())
|
||||
{
|
||||
d = new FilePrivate;
|
||||
read(readProperties, propertiesStyle);
|
||||
if(isOpen())
|
||||
read(readProperties);
|
||||
}
|
||||
|
||||
APE::File::File(IOStream *stream, bool readProperties,
|
||||
Properties::ReadStyle propertiesStyle) : TagLib::File(stream)
|
||||
APE::File::File(IOStream *stream, bool readProperties, Properties::ReadStyle) :
|
||||
TagLib::File(stream),
|
||||
d(std::make_unique<FilePrivate>())
|
||||
{
|
||||
d = new FilePrivate;
|
||||
read(readProperties, propertiesStyle);
|
||||
if(isOpen())
|
||||
read(readProperties);
|
||||
}
|
||||
|
||||
APE::File::~File()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
APE::File::~File() = default;
|
||||
|
||||
TagLib::Tag *APE::File::tag() const
|
||||
{
|
||||
@@ -112,34 +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->hasAPE)
|
||||
return d->tag.access<APE::Tag>(ApeAPEIndex, false)->setProperties(properties);
|
||||
else if(d->hasID3v1)
|
||||
return d->tag.access<ID3v1::Tag>(ApeID3v1Index, false)->setProperties(properties);
|
||||
else
|
||||
return d->tag.access<APE::Tag>(ApeAPEIndex, true)->setProperties(properties);
|
||||
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()
|
||||
@@ -151,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)
|
||||
@@ -223,83 +212,83 @@ 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);
|
||||
bool APE::File::hasAPETag() const
|
||||
{
|
||||
return d->APELocation >= 0;
|
||||
}
|
||||
|
||||
if(!ID3v1Tag())
|
||||
APETag(true);
|
||||
}
|
||||
bool APE::File::hasID3v1Tag() const
|
||||
{
|
||||
return d->ID3v1Location >= 0;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// private members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void APE::File::read(bool readProperties, Properties::ReadStyle /* propertiesStyle */)
|
||||
void APE::File::read(bool readProperties)
|
||||
{
|
||||
// Look for an ID3v2 tag
|
||||
|
||||
d->ID3v2Location = Utils::findID3v2(this);
|
||||
|
||||
if(d->ID3v2Location >= 0) {
|
||||
seek(d->ID3v2Location);
|
||||
d->ID3v2Header = std::make_unique<ID3v2::Header>(readBlock(ID3v2::Header::size()));
|
||||
d->ID3v2Size = d->ID3v2Header->completeTagSize();
|
||||
}
|
||||
|
||||
// Look for an ID3v1 tag
|
||||
|
||||
d->ID3v1Location = findID3v1();
|
||||
d->ID3v1Location = Utils::findID3v1(this);
|
||||
|
||||
if(d->ID3v1Location >= 0) {
|
||||
if(d->ID3v1Location >= 0)
|
||||
d->tag.set(ApeID3v1Index, new ID3v1::Tag(this, d->ID3v1Location));
|
||||
d->hasID3v1 = true;
|
||||
}
|
||||
|
||||
// Look for an APE tag
|
||||
|
||||
d->APELocation = findAPE();
|
||||
d->APELocation = Utils::findAPE(this, d->ID3v1Location);
|
||||
|
||||
if(d->APELocation >= 0) {
|
||||
d->tag.set(ApeAPEIndex, new APE::Tag(this, d->APELocation));
|
||||
d->APESize = APETag()->footer()->completeTagSize();
|
||||
d->APELocation = d->APELocation + APETag()->footer()->size() - d->APESize;
|
||||
d->hasAPE = true;
|
||||
d->APELocation = d->APELocation + APE::Footer::size() - d->APESize;
|
||||
}
|
||||
|
||||
if(!d->hasID3v1)
|
||||
if(d->ID3v1Location < 0)
|
||||
APETag(true);
|
||||
|
||||
// Look for APE audio properties
|
||||
|
||||
if(readProperties) {
|
||||
d->properties = new Properties(this);
|
||||
|
||||
offset_t streamLength;
|
||||
|
||||
if(d->APELocation >= 0)
|
||||
streamLength = d->APELocation;
|
||||
else if(d->ID3v1Location >= 0)
|
||||
streamLength = d->ID3v1Location;
|
||||
else
|
||||
streamLength = length();
|
||||
|
||||
if(d->ID3v2Location >= 0) {
|
||||
seek(d->ID3v2Location + d->ID3v2Size);
|
||||
streamLength -= d->ID3v2Location + d->ID3v2Size;
|
||||
}
|
||||
else {
|
||||
seek(0);
|
||||
}
|
||||
|
||||
d->properties = 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;
|
||||
}
|
||||
|
||||
@@ -48,7 +48,7 @@ namespace TagLib {
|
||||
//! An implementation of APE metadata
|
||||
|
||||
/*!
|
||||
* This is implementation of APE metadata.
|
||||
* This is an implementation of APE metadata.
|
||||
*
|
||||
* This supports ID3v1 and APE (v1 and v2) style comments as well as reading stream
|
||||
* properties from the file.
|
||||
@@ -59,7 +59,7 @@ namespace TagLib {
|
||||
//! An implementation of TagLib::File with APE specific methods
|
||||
|
||||
/*!
|
||||
* This implements and provides an interface APE WavPack files to the
|
||||
* This implements and provides an interface for APE 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 APE files.
|
||||
@@ -84,17 +84,22 @@ namespace TagLib {
|
||||
};
|
||||
|
||||
/*!
|
||||
* Contructs an WavPack file from \a file. If \a readProperties is true the
|
||||
* file's audio properties will also be read using \a propertiesStyle. If
|
||||
* false, \a propertiesStyle is ignored.
|
||||
* Constructs an APE file from \a file. If \a readProperties is \c true the
|
||||
* file's audio properties will also be read.
|
||||
*
|
||||
* \note In the current implementation, \a propertiesStyle is ignored.
|
||||
*/
|
||||
File(FileName file, bool readProperties = true,
|
||||
Properties::ReadStyle propertiesStyle = Properties::Average);
|
||||
|
||||
/*!
|
||||
* Contructs an WavPack file from \a file. If \a readProperties is true the
|
||||
* file's audio properties will also be read using \a propertiesStyle. If
|
||||
* false, \a propertiesStyle is ignored.
|
||||
* Constructs an APE file from \a stream. If \a readProperties is \c true the
|
||||
* file's audio properties will also be read.
|
||||
*
|
||||
* \note TagLib will *not* take ownership of the stream, the caller is
|
||||
* responsible for deleting it after the File object.
|
||||
*
|
||||
* \note In the current implementation, \a propertiesStyle is ignored.
|
||||
*/
|
||||
File(IOStream *stream, bool readProperties = true,
|
||||
Properties::ReadStyle propertiesStyle = Properties::Average);
|
||||
@@ -102,38 +107,42 @@ 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.
|
||||
* As for the export, only one tag is taken into account. If the file
|
||||
* has no tag at all, APE will be created.
|
||||
* 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.
|
||||
@@ -141,32 +150,43 @@ namespace TagLib {
|
||||
* \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.
|
||||
*/
|
||||
virtual bool save();
|
||||
bool save() override;
|
||||
|
||||
/*!
|
||||
* Returns a pointer to the ID3v1 tag of the file.
|
||||
*
|
||||
* If \a create is false (the default) this will return a null pointer
|
||||
* if there is no valid ID3v1 tag. If \a create is true it will create
|
||||
* an ID3v1 tag if one does not exist. If there is already an APE tag, the
|
||||
* new ID3v1 tag will be placed after it.
|
||||
* If \a create is \c false (the default) this may return a null pointer
|
||||
* if there is no valid ID3v1 tag. If \a create is \c true it will create
|
||||
* an ID3v1 tag if one does not exist and returns a valid pointer.
|
||||
*
|
||||
* \note This may return a valid pointer regardless of whether or not the
|
||||
* file on disk has an ID3v1 tag. Use hasID3v1Tag() to check if the file
|
||||
* on disk actually has an ID3v1 tag.
|
||||
*
|
||||
* \note The Tag <b>is still</b> owned by the APE::File and should not be
|
||||
* deleted by the user. It will be deleted when the file (object) is
|
||||
* destroyed.
|
||||
*
|
||||
* \see hasID3v1Tag()
|
||||
*/
|
||||
ID3v1::Tag *ID3v1Tag(bool create = false);
|
||||
|
||||
/*!
|
||||
* Returns a pointer to the APE tag of the file.
|
||||
*
|
||||
* If \a create is false (the default) this will return a null pointer
|
||||
* if there is no valid APE tag. If \a create is true it will create
|
||||
* a APE tag if one does not exist.
|
||||
* If \a create is \c false (the default) this may return a null pointer
|
||||
* if there is no valid APE tag. If \a create is \c true it will create
|
||||
* an APE tag if one does not exist and returns a valid pointer.
|
||||
*
|
||||
* \note This may return a valid pointer regardless of whether or not the
|
||||
* file on disk has an APE tag. Use hasAPETag() to check if the file
|
||||
* on disk actually has an APE tag.
|
||||
*
|
||||
* \note The Tag <b>is still</b> owned by the APE::File and should not be
|
||||
* deleted by the user. It will be deleted when the file (object) is
|
||||
* destroyed.
|
||||
*
|
||||
* \see hasAPETag()
|
||||
*/
|
||||
APE::Tag *APETag(bool create = false);
|
||||
|
||||
@@ -180,19 +200,37 @@ namespace TagLib {
|
||||
*/
|
||||
void strip(int tags = AllTags);
|
||||
|
||||
private:
|
||||
File(const File &);
|
||||
File &operator=(const File &);
|
||||
/*!
|
||||
* Returns whether or not the file on disk actually has an APE tag.
|
||||
*
|
||||
* \see APETag()
|
||||
*/
|
||||
bool hasAPETag() const;
|
||||
|
||||
void read(bool readProperties, Properties::ReadStyle propertiesStyle);
|
||||
void scan();
|
||||
long findID3v1();
|
||||
long findAPE();
|
||||
/*!
|
||||
* Returns whether or not the file on disk actually has an ID3v1 tag.
|
||||
*
|
||||
* \see ID3v1Tag()
|
||||
*/
|
||||
bool hasID3v1Tag() const;
|
||||
|
||||
/*!
|
||||
* Returns whether or not the given \a stream can be opened as an APE
|
||||
* file.
|
||||
*
|
||||
* \note This method is designed to do a quick check. The result may
|
||||
* not necessarily be correct.
|
||||
*/
|
||||
static bool isSupported(IOStream *stream);
|
||||
|
||||
private:
|
||||
void read(bool readProperties);
|
||||
|
||||
class FilePrivate;
|
||||
FilePrivate *d;
|
||||
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
|
||||
std::unique_ptr<FilePrivate> d;
|
||||
};
|
||||
}
|
||||
}
|
||||
} // namespace APE
|
||||
} // namespace TagLib
|
||||
|
||||
#endif
|
||||
|
||||
@@ -24,13 +24,11 @@
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#include <iostream>
|
||||
#include "apefooter.h"
|
||||
|
||||
#include <bitset>
|
||||
|
||||
#include <tstring.h>
|
||||
#include <tdebug.h>
|
||||
|
||||
#include "apefooter.h"
|
||||
#include "taglib.h"
|
||||
|
||||
using namespace TagLib;
|
||||
using namespace APE;
|
||||
@@ -38,63 +36,49 @@ 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 +103,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 +137,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);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -177,19 +160,19 @@ void APE::Footer::parse(const ByteVector &data)
|
||||
|
||||
// Read the version number
|
||||
|
||||
d->version = data.mid(8, 4).toUInt(false);
|
||||
d->version = data.toUInt(8, false);
|
||||
|
||||
// Read the tag size
|
||||
|
||||
d->tagSize = data.mid(12, 4).toUInt(false);
|
||||
d->tagSize = data.toUInt(12, false);
|
||||
|
||||
// Read the item count
|
||||
|
||||
d->itemCount = data.mid(16, 4).toUInt(false);
|
||||
d->itemCount = data.toUInt(16, false);
|
||||
|
||||
// Read the flags
|
||||
|
||||
std::bitset<32> flags(TAGLIB_CONSTRUCT_BITSET(data.mid(20, 4).toUInt(false)));
|
||||
std::bitset<32> flags(TAGLIB_CONSTRUCT_BITSET(data.toUInt(20, false)));
|
||||
|
||||
d->headerPresent = flags[31];
|
||||
d->footerPresent = !flags[30];
|
||||
@@ -226,7 +209,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
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ namespace TagLib {
|
||||
|
||||
/*!
|
||||
* This class implements APE footers (and headers). It attempts to follow, both
|
||||
* semantically and programatically, the structure specified in
|
||||
* semantically and programmatically, the structure specified in
|
||||
* the APE v2.0 standard. The API is based on the properties of APE footer and
|
||||
* headers specified there.
|
||||
*/
|
||||
@@ -59,25 +59,28 @@ namespace TagLib {
|
||||
/*!
|
||||
* Destroys the footer.
|
||||
*/
|
||||
virtual ~Footer();
|
||||
~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.
|
||||
* Returns \c true if a header is present in the tag.
|
||||
*/
|
||||
bool headerPresent() const;
|
||||
|
||||
/*!
|
||||
* Returns true if a footer is present in the tag.
|
||||
* Returns \c true if a footer is present in the tag.
|
||||
*/
|
||||
bool footerPresent() const;
|
||||
|
||||
/*!
|
||||
* Returns true this is actually the header.
|
||||
* Returns \c true if this is actually the header.
|
||||
*/
|
||||
bool isHeader() const;
|
||||
|
||||
@@ -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.
|
||||
@@ -142,8 +145,8 @@ namespace TagLib {
|
||||
ByteVector renderFooter() const;
|
||||
|
||||
/*!
|
||||
* Renders the header corresponding to the footer. If headerPresent is
|
||||
* set to false, it returns an empty ByteVector.
|
||||
* Renders the header corresponding to the footer. If headerPresent() is
|
||||
* \c false, it returns an empty ByteVector.
|
||||
*/
|
||||
ByteVector renderHeader() const;
|
||||
|
||||
@@ -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,74 +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)
|
||||
{
|
||||
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;
|
||||
@@ -125,58 +126,52 @@ void APE::Item::setBinaryData(const ByteVector &value)
|
||||
{
|
||||
d->type = Binary;
|
||||
d->value = value;
|
||||
}
|
||||
|
||||
ByteVector APE::Item::value() const
|
||||
{
|
||||
// This seems incorrect as it won't be actually rendering the value to keep it
|
||||
// up to date.
|
||||
|
||||
return d->value;
|
||||
d->text.clear();
|
||||
}
|
||||
|
||||
void APE::Item::setKey(const String &key)
|
||||
{
|
||||
d->key = key;
|
||||
d->key = key;
|
||||
}
|
||||
|
||||
void APE::Item::setValue(const String &value)
|
||||
{
|
||||
d->type = Text;
|
||||
d->text = value;
|
||||
d->type = Text;
|
||||
d->text = value;
|
||||
d->value.clear();
|
||||
}
|
||||
|
||||
void APE::Item::setValues(const StringList &value)
|
||||
void APE::Item::setValues(const StringList &values)
|
||||
{
|
||||
d->type = Text;
|
||||
d->text = value;
|
||||
d->type = Text;
|
||||
d->text = values;
|
||||
d->value.clear();
|
||||
}
|
||||
|
||||
void APE::Item::appendValue(const String &value)
|
||||
{
|
||||
d->type = Text;
|
||||
d->text.append(value);
|
||||
d->type = Text;
|
||||
d->text.append(value);
|
||||
d->value.clear();
|
||||
}
|
||||
|
||||
void APE::Item::appendValues(const StringList &values)
|
||||
{
|
||||
d->type = Text;
|
||||
d->text.append(values);
|
||||
d->type = Text;
|
||||
d->text.append(values);
|
||||
d->value.clear();
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
@@ -188,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;
|
||||
@@ -200,7 +190,9 @@ StringList APE::Item::values() const
|
||||
|
||||
String APE::Item::toString() const
|
||||
{
|
||||
return isEmpty() ? String::null : d->text.front();
|
||||
if(d->type == Text && !isEmpty())
|
||||
return d->text.front();
|
||||
return String();
|
||||
}
|
||||
|
||||
bool APE::Item::isEmpty() const
|
||||
@@ -209,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();
|
||||
@@ -229,48 +219,52 @@ void APE::Item::parse(const ByteVector &data)
|
||||
return;
|
||||
}
|
||||
|
||||
uint valueLength = data.mid(0, 4).toUInt(false);
|
||||
uint flags = data.mid(4, 4).toUInt(false);
|
||||
const unsigned int valueLength = data.toUInt(0, false);
|
||||
const unsigned int flags = data.toUInt(4, false);
|
||||
|
||||
d->key = String(data.mid(8), String::UTF8);
|
||||
// An item key can contain ASCII characters from 0x20 up to 0x7E, not UTF-8.
|
||||
// We assume that the validity of the given key has been checked.
|
||||
|
||||
d->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(d->value, '\0'), String::UTF8);
|
||||
d->text = StringList(ByteVectorList::split(val, '\0'), String::UTF8);
|
||||
else
|
||||
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.
|
||||
*/
|
||||
@@ -71,7 +63,7 @@ namespace TagLib {
|
||||
|
||||
/*!
|
||||
* Constructs an item with \a key and \a value.
|
||||
* If \a binary is true a Binary item will be created, otherwise \a value will be interpreted as text
|
||||
* If \a binary is \c true a Binary item will be created, otherwise \a value will be interpreted as text
|
||||
*/
|
||||
Item(const String &key, const ByteVector &value, bool binary);
|
||||
|
||||
@@ -83,13 +75,18 @@ namespace TagLib {
|
||||
/*!
|
||||
* Destroys the item.
|
||||
*/
|
||||
virtual ~Item();
|
||||
~Item();
|
||||
|
||||
/*!
|
||||
* Copies the contents of \a item into this item.
|
||||
*/
|
||||
Item &operator=(const Item &item);
|
||||
|
||||
/*!
|
||||
* Exchanges the content of this item with the content of \a item.
|
||||
*/
|
||||
void swap(Item &item) noexcept;
|
||||
|
||||
/*!
|
||||
* Returns the key.
|
||||
*/
|
||||
@@ -97,7 +94,7 @@ namespace TagLib {
|
||||
|
||||
/*!
|
||||
* Returns the binary value.
|
||||
* If the item type is not \a Binary, the returned contents are undefined
|
||||
* If the item type is not \a Binary, always returns an empty ByteVector.
|
||||
*/
|
||||
ByteVector binaryData() const;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -152,18 +144,15 @@ namespace TagLib {
|
||||
int size() const;
|
||||
|
||||
/*!
|
||||
* Returns the value as a single string. In case of multiple strings,
|
||||
* the first is returned.
|
||||
* Returns the value as a single string. In case of multiple strings,
|
||||
* the first is returned. If the data type is not \a Text, always returns
|
||||
* an empty String.
|
||||
*/
|
||||
String toString() const;
|
||||
|
||||
#ifndef DO_NOT_DOCUMENT
|
||||
/* Remove in next binary incompatible release */
|
||||
StringList toStringList() const;
|
||||
#endif
|
||||
|
||||
/*!
|
||||
* Returns the list of text values.
|
||||
* Returns the list of text values. If the data type is not \a Text, always
|
||||
* returns an empty StringList.
|
||||
*/
|
||||
StringList values() const;
|
||||
|
||||
@@ -183,16 +172,16 @@ namespace TagLib {
|
||||
void setReadOnly(bool readOnly);
|
||||
|
||||
/*!
|
||||
* Return true if the item is read-only.
|
||||
* Return \c true if the item is read-only.
|
||||
*/
|
||||
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.
|
||||
@@ -200,18 +189,16 @@ namespace TagLib {
|
||||
ItemTypes type() const;
|
||||
|
||||
/*!
|
||||
* Returns if the item has any real content.
|
||||
* Returns \c false if the item has any real content.
|
||||
*/
|
||||
bool isEmpty() const;
|
||||
|
||||
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,56 +27,41 @@
|
||||
* 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 "apefooter.h"
|
||||
#include "apetag.h"
|
||||
|
||||
using namespace TagLib;
|
||||
|
||||
class APE::Properties::PropertiesPrivate
|
||||
{
|
||||
public:
|
||||
PropertiesPrivate(File *file, long streamLength) :
|
||||
length(0),
|
||||
bitrate(0),
|
||||
sampleRate(0),
|
||||
channels(0),
|
||||
version(0),
|
||||
bitsPerSample(0),
|
||||
sampleFrames(0),
|
||||
file(file),
|
||||
streamLength(streamLength) {}
|
||||
|
||||
int length;
|
||||
int bitrate;
|
||||
int sampleRate;
|
||||
int channels;
|
||||
int version;
|
||||
int bitsPerSample;
|
||||
uint sampleFrames;
|
||||
File *file;
|
||||
long streamLength;
|
||||
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 *file, ReadStyle style) : AudioProperties(style)
|
||||
APE::Properties::Properties(File *file, offset_t streamLength, ReadStyle style) :
|
||||
AudioProperties(style),
|
||||
d(std::make_unique<PropertiesPrivate>())
|
||||
{
|
||||
d = new PropertiesPrivate(file, file->length());
|
||||
read();
|
||||
read(file, streamLength);
|
||||
}
|
||||
|
||||
APE::Properties::~Properties()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
APE::Properties::~Properties() = default;
|
||||
|
||||
int APE::Properties::length() const
|
||||
int APE::Properties::lengthInMilliseconds() const
|
||||
{
|
||||
return d->length;
|
||||
}
|
||||
@@ -106,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;
|
||||
}
|
||||
@@ -115,117 +100,121 @@ TagLib::uint APE::Properties::sampleFrames() const
|
||||
// private members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
void APE::Properties::read()
|
||||
namespace
|
||||
{
|
||||
// First we are searching the descriptor
|
||||
long offset = findDescriptor();
|
||||
if(offset < 0)
|
||||
return;
|
||||
int headerVersion(const ByteVector &header)
|
||||
{
|
||||
if(header.size() < 6 || !header.startsWith("MAC "))
|
||||
return -1;
|
||||
|
||||
// Then we read the header common for all versions of APE
|
||||
d->file->seek(offset);
|
||||
ByteVector commonHeader=d->file->readBlock(6);
|
||||
if(!commonHeader.startsWith("MAC "))
|
||||
return;
|
||||
d->version = commonHeader.mid(4).toUInt(false);
|
||||
|
||||
if(d->version >= 3980) {
|
||||
analyzeCurrent();
|
||||
return header.toUShort(4, false);
|
||||
}
|
||||
else {
|
||||
analyzeOld();
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
long APE::Properties::findDescriptor()
|
||||
void APE::Properties::read(File *file, offset_t streamLength)
|
||||
{
|
||||
long ID3v2Location = findID3v2();
|
||||
long ID3v2OriginalSize = 0;
|
||||
bool hasID3v2 = false;
|
||||
if(ID3v2Location >= 0) {
|
||||
ID3v2::Tag tag(d->file, ID3v2Location);
|
||||
ID3v2OriginalSize = tag.header()->completeTagSize();
|
||||
if(tag.header()->tagSize() > 0)
|
||||
hasID3v2 = true;
|
||||
// First, we assume that the file pointer is set at the first descriptor.
|
||||
offset_t offset = file->tell();
|
||||
int vers = headerVersion(file->readBlock(6));
|
||||
|
||||
// Next, we look for the descriptor.
|
||||
if(vers < 0) {
|
||||
offset = file->find("MAC ", offset);
|
||||
file->seek(offset);
|
||||
vers = headerVersion(file->readBlock(6));
|
||||
}
|
||||
|
||||
long offset = 0;
|
||||
if(hasID3v2)
|
||||
offset = d->file->find("MAC ", ID3v2Location + ID3v2OriginalSize);
|
||||
if(vers < 0) {
|
||||
debug("APE::Properties::read() -- APE descriptor not found");
|
||||
return;
|
||||
}
|
||||
|
||||
d->version = vers;
|
||||
|
||||
if(d->version >= 3980)
|
||||
analyzeCurrent(file);
|
||||
else
|
||||
offset = d->file->find("MAC ");
|
||||
analyzeOld(file);
|
||||
|
||||
if(offset < 0) {
|
||||
debug("APE::Properties::findDescriptor() -- APE descriptor not found");
|
||||
return -1;
|
||||
if(d->sampleFrames > 0 && d->sampleRate > 0) {
|
||||
const auto length = static_cast<double>(d->sampleFrames) * 1000.0 / d->sampleRate;
|
||||
d->length = static_cast<int>(length + 0.5);
|
||||
d->bitrate = static_cast<int>(static_cast<double>(streamLength) * 8.0 / length + 0.5);
|
||||
}
|
||||
|
||||
return offset;
|
||||
}
|
||||
|
||||
long APE::Properties::findID3v2()
|
||||
{
|
||||
if(!d->file->isValid())
|
||||
return -1;
|
||||
|
||||
d->file->seek(0);
|
||||
|
||||
if(d->file->readBlock(3) == ID3v2::Header::fileIdentifier())
|
||||
return 0;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
void APE::Properties::analyzeCurrent()
|
||||
void APE::Properties::analyzeCurrent(File *file)
|
||||
{
|
||||
// Read the descriptor
|
||||
d->file->seek(2, File::Current);
|
||||
ByteVector descriptor = d->file->readBlock(44);
|
||||
uint descriptorBytes = descriptor.mid(0,4).toUInt(false);
|
||||
file->seek(2, File::Current);
|
||||
const ByteVector descriptor = file->readBlock(44);
|
||||
if(descriptor.size() < 44) {
|
||||
debug("APE::Properties::analyzeCurrent() -- descriptor is too short.");
|
||||
return;
|
||||
}
|
||||
|
||||
if ((descriptorBytes - 52) > 0)
|
||||
d->file->seek(descriptorBytes - 52, File::Current);
|
||||
if(const unsigned int descriptorBytes = descriptor.toUInt(0, false);
|
||||
descriptorBytes - 52 > 0)
|
||||
file->seek(descriptorBytes - 52, File::Current);
|
||||
|
||||
// Read the header
|
||||
ByteVector header = d->file->readBlock(24);
|
||||
const ByteVector header = file->readBlock(24);
|
||||
if(header.size() < 24) {
|
||||
debug("APE::Properties::analyzeCurrent() -- MAC header is too short.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the APE info
|
||||
d->channels = header.mid(18, 2).toShort(false);
|
||||
d->sampleRate = header.mid(20, 4).toUInt(false);
|
||||
d->bitsPerSample = header.mid(16, 2).toShort(false);
|
||||
//d->compressionLevel =
|
||||
d->channels = header.toShort(18, false);
|
||||
d->sampleRate = header.toUInt(20, false);
|
||||
d->bitsPerSample = header.toShort(16, false);
|
||||
|
||||
uint totalFrames = header.mid(12, 4).toUInt(false);
|
||||
uint blocksPerFrame = header.mid(4, 4).toUInt(false);
|
||||
uint finalFrameBlocks = header.mid(8, 4).toUInt(false);
|
||||
d->sampleFrames = totalFrames > 0 ? (totalFrames - 1) * blocksPerFrame + finalFrameBlocks : 0;
|
||||
d->length = d->sampleRate > 0 ? d->sampleFrames / d->sampleRate : 0;
|
||||
d->bitrate = d->length > 0 ? ((d->streamLength * 8L) / d->length) / 1000 : 0;
|
||||
const unsigned int totalFrames = header.toUInt(12, false);
|
||||
if(totalFrames == 0)
|
||||
return;
|
||||
|
||||
const unsigned int blocksPerFrame = header.toUInt(4, false);
|
||||
const unsigned int finalFrameBlocks = header.toUInt(8, false);
|
||||
d->sampleFrames = (totalFrames - 1) * blocksPerFrame + finalFrameBlocks;
|
||||
}
|
||||
|
||||
void APE::Properties::analyzeOld()
|
||||
void APE::Properties::analyzeOld(File *file)
|
||||
{
|
||||
ByteVector header = d->file->readBlock(26);
|
||||
uint totalFrames = header.mid(18, 4).toUInt(false);
|
||||
const ByteVector header = file->readBlock(26);
|
||||
if(header.size() < 26) {
|
||||
debug("APE::Properties::analyzeOld() -- MAC header is too short.");
|
||||
return;
|
||||
}
|
||||
|
||||
const unsigned int totalFrames = header.toUInt(18, false);
|
||||
|
||||
// Fail on 0 length APE files (catches non-finalized APE files)
|
||||
if(totalFrames == 0)
|
||||
return;
|
||||
|
||||
short compressionLevel = header.mid(0, 2).toShort(false);
|
||||
uint blocksPerFrame;
|
||||
const short compressionLevel = header.toShort(0, false);
|
||||
unsigned int blocksPerFrame;
|
||||
if(d->version >= 3950)
|
||||
blocksPerFrame = 73728 * 4;
|
||||
else if(d->version >= 3900 || (d->version >= 3800 && compressionLevel == 4000))
|
||||
blocksPerFrame = 73728;
|
||||
else
|
||||
blocksPerFrame = 9216;
|
||||
d->channels = header.mid(4, 2).toShort(false);
|
||||
d->sampleRate = header.mid(6, 4).toUInt(false);
|
||||
uint finalFrameBlocks = header.mid(22, 4).toUInt(false);
|
||||
uint totalBlocks = totalFrames > 0 ? (totalFrames - 1) * blocksPerFrame + finalFrameBlocks : 0;
|
||||
d->length = totalBlocks / d->sampleRate;
|
||||
d->bitrate = d->length > 0 ? ((d->streamLength * 8L) / d->length) / 1000 : 0;
|
||||
}
|
||||
|
||||
// Get the APE info
|
||||
d->channels = header.toShort(4, false);
|
||||
d->sampleRate = header.toUInt(6, false);
|
||||
|
||||
const unsigned int finalFrameBlocks = header.toUInt(22, false);
|
||||
d->sampleFrames = (totalFrames - 1) * blocksPerFrame + finalFrameBlocks;
|
||||
|
||||
// Get the bit depth from the RIFF-fmt chunk.
|
||||
file->seek(16, File::Current);
|
||||
const ByteVector fmt = file->readBlock(28);
|
||||
if(fmt.size() < 28 || !fmt.startsWith("WAVEfmt ")) {
|
||||
debug("APE::Properties::analyzeOld() -- fmt header is too short.");
|
||||
return;
|
||||
}
|
||||
|
||||
d->bitsPerSample = fmt.toShort(26, false);
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
#ifndef TAGLIB_APEPROPERTIES_H
|
||||
#define TAGLIB_APEPROPERTIES_H
|
||||
|
||||
#include "taglib.h"
|
||||
#include "taglib_export.h"
|
||||
#include "audioproperties.h"
|
||||
|
||||
@@ -51,49 +52,66 @@ namespace TagLib {
|
||||
public:
|
||||
/*!
|
||||
* Create an instance of APE::Properties with the data read from the
|
||||
* ByteVector \a data.
|
||||
* APE::File \a file.
|
||||
*/
|
||||
Properties(File *f, ReadStyle style = Average);
|
||||
Properties(File *file, offset_t streamLength, ReadStyle style = Average);
|
||||
|
||||
/*!
|
||||
* Destroys this APE::Properties instance.
|
||||
*/
|
||||
virtual ~Properties();
|
||||
~Properties() override;
|
||||
|
||||
// Reimplementations.
|
||||
|
||||
virtual int length() const;
|
||||
virtual int bitrate() const;
|
||||
virtual int sampleRate() const;
|
||||
virtual int channels() const;
|
||||
Properties(const Properties &) = delete;
|
||||
Properties &operator=(const Properties &) = delete;
|
||||
|
||||
/*!
|
||||
* Returns number of bits per sample.
|
||||
* Returns the length of the file in milliseconds.
|
||||
*
|
||||
* \see lengthInSeconds()
|
||||
*/
|
||||
int lengthInMilliseconds() const override;
|
||||
|
||||
/*!
|
||||
* Returns the average bit rate of the file in kb/s.
|
||||
*/
|
||||
int bitrate() const override;
|
||||
|
||||
/*!
|
||||
* Returns the sample rate in Hz.
|
||||
*/
|
||||
int sampleRate() const override;
|
||||
|
||||
/*!
|
||||
* Returns the number of audio channels.
|
||||
*/
|
||||
int channels() const override;
|
||||
|
||||
/*!
|
||||
* Returns the number of bits per audio sample.
|
||||
*/
|
||||
int bitsPerSample() const;
|
||||
uint sampleFrames() const;
|
||||
|
||||
/*!
|
||||
* Returns APE version.
|
||||
* Returns the total number of audio samples in file.
|
||||
*/
|
||||
unsigned int sampleFrames() const;
|
||||
|
||||
/*!
|
||||
* Returns the APE version.
|
||||
*/
|
||||
int version() const;
|
||||
|
||||
private:
|
||||
Properties(const Properties &);
|
||||
Properties &operator=(const Properties &);
|
||||
void read(File *file, offset_t streamLength);
|
||||
|
||||
void read();
|
||||
|
||||
long findDescriptor();
|
||||
long findID3v2();
|
||||
|
||||
void analyzeCurrent();
|
||||
void analyzeOld();
|
||||
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,29 +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
|
||||
{
|
||||
constexpr unsigned int MinKeyLength = 2;
|
||||
constexpr 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), tagLength(0) {}
|
||||
|
||||
TagLib::File *file;
|
||||
long footerLocation;
|
||||
long tagLength;
|
||||
File *file { nullptr };
|
||||
offset_t footerLocation { 0 };
|
||||
|
||||
Footer footer;
|
||||
|
||||
ItemListMap itemListMap;
|
||||
};
|
||||
|
||||
@@ -61,24 +79,21 @@ public:
|
||||
// public methods
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
APE::Tag::Tag() : TagLib::Tag()
|
||||
APE::Tag::Tag() :
|
||||
d(std::make_unique<TagPrivate>())
|
||||
{
|
||||
d = new TagPrivate;
|
||||
}
|
||||
|
||||
APE::Tag::Tag(TagLib::File *file, long footerLocation) : TagLib::Tag()
|
||||
APE::Tag::Tag(TagLib::File *file, offset_t footerLocation) :
|
||||
d(std::make_unique<TagPrivate>())
|
||||
{
|
||||
d = new TagPrivate;
|
||||
d->file = file;
|
||||
d->footerLocation = footerLocation;
|
||||
|
||||
read();
|
||||
}
|
||||
|
||||
APE::Tag::~Tag()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
APE::Tag::~Tag() = default;
|
||||
|
||||
ByteVector APE::Tag::fileIdentifier()
|
||||
{
|
||||
@@ -87,51 +102,44 @@ ByteVector APE::Tag::fileIdentifier()
|
||||
|
||||
String APE::Tag::title() const
|
||||
{
|
||||
if(d->itemListMap["TITLE"].isEmpty())
|
||||
return String::null;
|
||||
return d->itemListMap["TITLE"].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"].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"].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"].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"].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)
|
||||
@@ -159,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 = PropertyMap::prepareKey(it->first);
|
||||
for(const auto &[tag, item] : std::as_const(itemListMap())) {
|
||||
// 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(String tagName = tag.upper();
|
||||
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;
|
||||
@@ -207,52 +223,145 @@ 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 = PropertyMap::prepareKey(remIt->first);
|
||||
for(const auto &[k, t] : std::as_const(itemListMap())) {
|
||||
// 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(String key = k.upper();
|
||||
!key.isEmpty() && t.type() == APE::Item::Text && !props.contains(key))
|
||||
toRemove.append(k);
|
||||
}
|
||||
|
||||
for (StringList::Iterator 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();
|
||||
for(; it != properties.end(); ++it) {
|
||||
const String &tagName = it->first;
|
||||
if(!(itemListMap().contains(tagName)) || !(itemListMap()[tagName].values() == it->second)) {
|
||||
if(it->second.size() == 0)
|
||||
PropertyMap invalid;
|
||||
for(const auto &[tagName, val] : std::as_const(props)) {
|
||||
if(!checkKey(tagName))
|
||||
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 PropertyMap();
|
||||
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;
|
||||
if(const String uppercaseKey = key.upper(); uppercaseKey == "PICTURE") {
|
||||
const StringList itemNames = StringList(FRONT_COVER).append(BACK_COVER);
|
||||
for(const auto &itemName: itemNames) {
|
||||
if(d->itemListMap.contains(itemName)) {
|
||||
if(Item picture = d->itemListMap.value(itemName);
|
||||
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)
|
||||
{
|
||||
if(const String uppercaseKey = key.upper(); 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() < MinKeyLength || key.size() > MaxKeyLength)
|
||||
return false;
|
||||
|
||||
return isKeyValid(key.data(String::UTF8));
|
||||
}
|
||||
|
||||
APE::Footer *APE::Tag::footer() const
|
||||
@@ -267,40 +376,46 @@ const APE::ItemListMap& APE::Tag::itemListMap() const
|
||||
|
||||
void APE::Tag::removeItem(const String &key)
|
||||
{
|
||||
Map<const String, Item>::Iterator it = d->itemListMap.find(key.upper());
|
||||
if(it != d->itemListMap.end())
|
||||
d->itemListMap.erase(it);
|
||||
d->itemListMap.erase(key.upper());
|
||||
}
|
||||
|
||||
void APE::Tag::addValue(const String &key, const String &value, bool replace)
|
||||
{
|
||||
if(replace)
|
||||
removeItem(key);
|
||||
if(!key.isEmpty() && !value.isEmpty()) {
|
||||
if(!replace && d->itemListMap.contains(key)) {
|
||||
// Text items may contain more than one value
|
||||
if(APE::Item::Text == d->itemListMap.begin()->second.type())
|
||||
d->itemListMap[key.upper()].appendValue(value);
|
||||
// Binary or locator items may have only one value
|
||||
else
|
||||
setItem(key, Item(key, value));
|
||||
}
|
||||
else
|
||||
setItem(key, Item(key, value));
|
||||
}
|
||||
|
||||
if(value.isEmpty())
|
||||
return;
|
||||
|
||||
// Text items may contain more than one value.
|
||||
// Binary or locator items may have only one value, hence always replaced.
|
||||
|
||||
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
|
||||
@@ -320,7 +435,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());
|
||||
@@ -331,15 +446,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);
|
||||
@@ -351,16 +462,42 @@ ByteVector APE::Tag::render() const
|
||||
|
||||
void APE::Tag::parse(const ByteVector &data)
|
||||
{
|
||||
uint pos = 0;
|
||||
|
||||
// 11 bytes is the minimum size for an APE item
|
||||
|
||||
for(uint i = 0; i < d->footer.itemCount() && pos <= data.size() - 11; i++) {
|
||||
APE::Item item;
|
||||
item.parse(data.mid(pos));
|
||||
if(data.size() < 11)
|
||||
return;
|
||||
|
||||
d->itemListMap.insert(item.key().upper(), item);
|
||||
unsigned int pos = 0;
|
||||
|
||||
pos += item.size();
|
||||
for(unsigned int i = 0; i < d->footer.itemCount() && pos <= data.size() - 11; i++) {
|
||||
|
||||
const int nullPos = data.find('\0', pos + 8);
|
||||
if(nullPos < 0) {
|
||||
debug("APE::Tag::parse() - Couldn't find a key/value separator. Stopped parsing.");
|
||||
return;
|
||||
}
|
||||
|
||||
const unsigned int keyLength = nullPos - pos - 8;
|
||||
const unsigned int 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,12 @@
|
||||
#ifndef TAGLIB_APETAG_H
|
||||
#define TAGLIB_APETAG_H
|
||||
|
||||
#include "tag.h"
|
||||
#include "tbytevector.h"
|
||||
#include "tmap.h"
|
||||
#include "tstring.h"
|
||||
#include "taglib.h"
|
||||
#include "taglib_export.h"
|
||||
|
||||
#include "tag.h"
|
||||
#include "apeitem.h"
|
||||
|
||||
namespace TagLib {
|
||||
@@ -49,8 +49,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 +65,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 +89,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.
|
||||
@@ -115,17 +117,29 @@ namespace TagLib {
|
||||
*
|
||||
* The only conversion done by this export function is to rename the APE tags
|
||||
* TRACK to TRACKNUMBER, YEAR to DATE, and ALBUM ARTIST to ALBUMARTIST, respectively,
|
||||
* (and a few other keys, see \ref p_propertymapping)
|
||||
* 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
|
||||
* comments as for the export function apply.
|
||||
* comments as for the export function apply; additionally note that the APE tag
|
||||
* specification requires keys to have between 2 and 16 printable ASCII characters
|
||||
* with the exception of the fixed strings "ID3", "TAG", "OGGS", and "MP+".
|
||||
*/
|
||||
PropertyMap setProperties(const PropertyMap &);
|
||||
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.
|
||||
*/
|
||||
static bool checkKey(const String&);
|
||||
|
||||
/*!
|
||||
* Returns a pointer to the tag's footer.
|
||||
@@ -136,7 +150,7 @@ namespace TagLib {
|
||||
* Returns a reference to the item list map. This is an ItemListMap of
|
||||
* all of the items in the tag.
|
||||
*
|
||||
* This is the most powerfull structure for accessing the items of the tag.
|
||||
* This is the most powerful structure for accessing the items of the tag.
|
||||
*
|
||||
* APE tags are case-insensitive, all keys in this map have been converted
|
||||
* to upper case.
|
||||
@@ -153,7 +167,7 @@ namespace TagLib {
|
||||
|
||||
/*!
|
||||
* Adds to the text item specified by \a key the data \a value. If \a replace
|
||||
* is true, then all of the other values on the same key will be removed
|
||||
* is \c true, then all of the other values on the same key will be removed
|
||||
* first. If a binary item exists for \a key it will be removed first.
|
||||
*/
|
||||
void addValue(const String &key, const String &value, bool replace = true);
|
||||
@@ -172,9 +186,9 @@ namespace TagLib {
|
||||
void setItem(const String &key, const Item &item);
|
||||
|
||||
/*!
|
||||
* Returns true if the tag does not contain any data.
|
||||
* Returns \c true if the tag does not contain any data.
|
||||
*/
|
||||
bool isEmpty() const;
|
||||
bool isEmpty() const override;
|
||||
|
||||
protected:
|
||||
|
||||
@@ -189,13 +203,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,118 +23,103 @@
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <taglib.h>
|
||||
#include <tdebug.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) {}
|
||||
AttributeTypes type;
|
||||
AttributePrivate() :
|
||||
pictureValue(ASF::Picture::fromInvalid())
|
||||
{
|
||||
}
|
||||
AttributeTypes type { UnicodeType };
|
||||
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(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;
|
||||
@@ -154,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 d->numericValue;
|
||||
}
|
||||
|
||||
ASF::Picture ASF::Attribute::toPicture() const
|
||||
@@ -177,30 +162,30 @@ ASF::Picture ASF::Attribute::toPicture() const
|
||||
return d->pictureValue;
|
||||
}
|
||||
|
||||
String ASF::Attribute::parse(ASF::File &f, int kind)
|
||||
String ASF::Attribute::parse(ASF::File &file, int kind)
|
||||
{
|
||||
uint size, nameLength;
|
||||
unsigned int size, nameLength;
|
||||
String name;
|
||||
d->pictureValue = Picture::fromInvalid();
|
||||
// extended content descriptor
|
||||
if(kind == 0) {
|
||||
nameLength = f.readWORD();
|
||||
name = f.readString(nameLength);
|
||||
d->type = ASF::Attribute::AttributeTypes(f.readWORD());
|
||||
size = f.readWORD();
|
||||
nameLength = readWORD(&file);
|
||||
name = readString(&file, nameLength);
|
||||
d->type = static_cast<ASF::Attribute::AttributeTypes>(readWORD(&file));
|
||||
size = readWORD(&file);
|
||||
}
|
||||
// metadata & metadata library
|
||||
else {
|
||||
int temp = f.readWORD();
|
||||
int temp = readWORD(&file);
|
||||
// metadata library
|
||||
if(kind == 2) {
|
||||
d->language = temp;
|
||||
}
|
||||
d->stream = f.readWORD();
|
||||
nameLength = f.readWORD();
|
||||
d->type = ASF::Attribute::AttributeTypes(f.readWORD());
|
||||
size = f.readDWORD();
|
||||
name = f.readString(nameLength);
|
||||
d->stream = readWORD(&file);
|
||||
nameLength = readWORD(&file);
|
||||
d->type = static_cast<ASF::Attribute::AttributeTypes>(readWORD(&file));
|
||||
size = readDWORD(&file);
|
||||
name = readString(&file, nameLength);
|
||||
}
|
||||
|
||||
if(kind != 2 && size > 65535) {
|
||||
@@ -209,33 +194,33 @@ String ASF::Attribute::parse(ASF::File &f, int kind)
|
||||
|
||||
switch(d->type) {
|
||||
case WordType:
|
||||
d->shortValue = f.readWORD();
|
||||
d->numericValue = readWORD(&file);
|
||||
break;
|
||||
|
||||
case BoolType:
|
||||
if(kind == 0) {
|
||||
d->boolValue = f.readDWORD() == 1;
|
||||
d->numericValue = readDWORD(&file) != 0;
|
||||
}
|
||||
else {
|
||||
d->boolValue = f.readWORD() == 1;
|
||||
d->numericValue = readWORD(&file) != 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case DWordType:
|
||||
d->intValue = f.readDWORD();
|
||||
d->numericValue = readDWORD(&file);
|
||||
break;
|
||||
|
||||
case QWordType:
|
||||
d->longLongValue = f.readQWORD();
|
||||
d->numericValue = readQWORD(&file);
|
||||
break;
|
||||
|
||||
case UnicodeType:
|
||||
d->stringValue = f.readString(size);
|
||||
d->stringValue = readString(&file, size);
|
||||
break;
|
||||
|
||||
case BytesType:
|
||||
case GuidType:
|
||||
d->byteVectorValue = f.readBlock(size);
|
||||
d->byteVectorValue = file.readBlock(size);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -255,7 +240,6 @@ int ASF::Attribute::dataSize() const
|
||||
case WordType:
|
||||
return 2;
|
||||
case BoolType:
|
||||
return 4;
|
||||
case DWordType:
|
||||
return 4;
|
||||
case QWordType:
|
||||
@@ -263,8 +247,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();
|
||||
}
|
||||
@@ -277,52 +263,55 @@ 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:
|
||||
data.append(File::renderString(d->stringValue));
|
||||
data.append(renderString(d->stringValue));
|
||||
break;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
if(kind == 0) {
|
||||
data = File::renderString(name, true) +
|
||||
ByteVector::fromShort((int)d->type, false) +
|
||||
ByteVector::fromShort(data.size(), false) +
|
||||
data = renderString(name, true) +
|
||||
ByteVector::fromShort(static_cast<short>(d->type), false) +
|
||||
ByteVector::fromShort(static_cast<short>(data.size()), false) +
|
||||
data;
|
||||
}
|
||||
else {
|
||||
ByteVector nameData = File::renderString(name);
|
||||
data = ByteVector::fromShort(kind == 2 ? d->language : 0, false) +
|
||||
ByteVector::fromShort(d->stream, false) +
|
||||
ByteVector::fromShort(nameData.size(), false) +
|
||||
ByteVector::fromShort((int)d->type, false) +
|
||||
ByteVector nameData = renderString(name);
|
||||
data = ByteVector::fromShort(static_cast<short>(kind == 2 ? d->language : 0), false) +
|
||||
ByteVector::fromShort(static_cast<short>(d->stream), false) +
|
||||
ByteVector::fromShort(static_cast<short>(nameData.size()), false) +
|
||||
ByteVector::fromShort(static_cast<short>(d->type), false) +
|
||||
ByteVector::fromUInt(data.size(), false) +
|
||||
nameData +
|
||||
data;
|
||||
@@ -350,4 +339,3 @@ void ASF::Attribute::setStream(int value)
|
||||
{
|
||||
d->stream = value;
|
||||
}
|
||||
|
||||
|
||||
@@ -33,13 +33,13 @@
|
||||
|
||||
namespace TagLib
|
||||
{
|
||||
|
||||
namespace ASF
|
||||
{
|
||||
|
||||
class File;
|
||||
class Picture;
|
||||
|
||||
//! Attribute of ASF (WMA) metadata
|
||||
|
||||
class TAGLIB_EXPORT Attribute
|
||||
{
|
||||
public:
|
||||
@@ -108,20 +108,25 @@ 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 with the content of \a other.
|
||||
*/
|
||||
void swap(Attribute &other) noexcept;
|
||||
|
||||
/*!
|
||||
* Destroys the attribute.
|
||||
*/
|
||||
virtual ~Attribute();
|
||||
~Attribute();
|
||||
|
||||
/*!
|
||||
* Returns type of the value.
|
||||
* Returns the type of the value.
|
||||
*/
|
||||
AttributeTypes type() const;
|
||||
|
||||
@@ -194,10 +199,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,218 +23,260 @@
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <tdebug.h>
|
||||
#include <tbytevectorlist.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"
|
||||
|
||||
using namespace TagLib;
|
||||
|
||||
class ASF::File::FilePrivate
|
||||
{
|
||||
public:
|
||||
FilePrivate():
|
||||
size(0),
|
||||
tag(0),
|
||||
properties(0),
|
||||
contentDescriptionObject(0),
|
||||
extendedContentDescriptionObject(0),
|
||||
headerExtensionObject(0),
|
||||
metadataObject(0),
|
||||
metadataLibraryObject(0) {}
|
||||
unsigned long long size;
|
||||
ASF::Tag *tag;
|
||||
ASF::Properties *properties;
|
||||
List<ASF::File::BaseObject *> objects;
|
||||
ASF::File::ContentDescriptionObject *contentDescriptionObject;
|
||||
ASF::File::ExtendedContentDescriptionObject *extendedContentDescriptionObject;
|
||||
ASF::File::HeaderExtensionObject *headerExtensionObject;
|
||||
ASF::File::MetadataObject *metadataObject;
|
||||
ASF::File::MetadataLibraryObject *metadataLibraryObject;
|
||||
class BaseObject;
|
||||
class UnknownObject;
|
||||
class FilePropertiesObject;
|
||||
class StreamPropertiesObject;
|
||||
class ContentDescriptionObject;
|
||||
class ExtendedContentDescriptionObject;
|
||||
class HeaderExtensionObject;
|
||||
class CodecListObject;
|
||||
class MetadataObject;
|
||||
class MetadataLibraryObject;
|
||||
|
||||
FilePrivate()
|
||||
{
|
||||
objects.setAutoDelete(true);
|
||||
}
|
||||
|
||||
~FilePrivate() = default;
|
||||
|
||||
FilePrivate(const FilePrivate &) = delete;
|
||||
FilePrivate &operator=(const FilePrivate &) = delete;
|
||||
|
||||
unsigned long long headerSize { 0 };
|
||||
|
||||
std::unique_ptr<ASF::Tag> tag;
|
||||
std::unique_ptr<ASF::Properties> properties;
|
||||
|
||||
List<BaseObject *> objects;
|
||||
|
||||
ContentDescriptionObject *contentDescriptionObject { nullptr };
|
||||
ExtendedContentDescriptionObject *extendedContentDescriptionObject { nullptr };
|
||||
HeaderExtensionObject *headerExtensionObject { nullptr };
|
||||
MetadataObject *metadataObject { nullptr };
|
||||
MetadataLibraryObject *metadataLibraryObject { nullptr };
|
||||
};
|
||||
|
||||
static ByteVector headerGuid("\x30\x26\xB2\x75\x8E\x66\xCF\x11\xA6\xD9\x00\xAA\x00\x62\xCE\x6C", 16);
|
||||
static ByteVector filePropertiesGuid("\xA1\xDC\xAB\x8C\x47\xA9\xCF\x11\x8E\xE4\x00\xC0\x0C\x20\x53\x65", 16);
|
||||
static ByteVector streamPropertiesGuid("\x91\x07\xDC\xB7\xB7\xA9\xCF\x11\x8E\xE6\x00\xC0\x0C\x20\x53\x65", 16);
|
||||
static ByteVector contentDescriptionGuid("\x33\x26\xB2\x75\x8E\x66\xCF\x11\xA6\xD9\x00\xAA\x00\x62\xCE\x6C", 16);
|
||||
static ByteVector extendedContentDescriptionGuid("\x40\xA4\xD0\xD2\x07\xE3\xD2\x11\x97\xF0\x00\xA0\xC9\x5E\xA8\x50", 16);
|
||||
static ByteVector headerExtensionGuid("\xb5\x03\xbf_.\xa9\xcf\x11\x8e\xe3\x00\xc0\x0c Se", 16);
|
||||
static ByteVector metadataGuid("\xEA\xCB\xF8\xC5\xAF[wH\204g\xAA\214D\xFAL\xCA", 16);
|
||||
static ByteVector metadataLibraryGuid("\224\034#D\230\224\321I\241A\x1d\x13NEpT", 16);
|
||||
static ByteVector contentEncryptionGuid("\xFB\xB3\x11\x22\x23\xBD\xD2\x11\xB4\xB7\x00\xA0\xC9\x55\xFC\x6E", 16);
|
||||
static ByteVector extendedContentEncryptionGuid("\x14\xE6\x8A\x29\x22\x26 \x17\x4C\xB9\x35\xDA\xE0\x7E\xE9\x28\x9C", 16);
|
||||
static ByteVector advancedContentEncryptionGuid("\xB6\x9B\x07\x7A\xA4\xDA\x12\x4E\xA5\xCA\x91\xD3\x8D\xC1\x1A\x8D", 16);
|
||||
namespace
|
||||
{
|
||||
const ByteVector headerGuid("\x30\x26\xB2\x75\x8E\x66\xCF\x11\xA6\xD9\x00\xAA\x00\x62\xCE\x6C", 16);
|
||||
const ByteVector filePropertiesGuid("\xA1\xDC\xAB\x8C\x47\xA9\xCF\x11\x8E\xE4\x00\xC0\x0C\x20\x53\x65", 16);
|
||||
const ByteVector streamPropertiesGuid("\x91\x07\xDC\xB7\xB7\xA9\xCF\x11\x8E\xE6\x00\xC0\x0C\x20\x53\x65", 16);
|
||||
const ByteVector contentDescriptionGuid("\x33\x26\xB2\x75\x8E\x66\xCF\x11\xA6\xD9\x00\xAA\x00\x62\xCE\x6C", 16);
|
||||
const ByteVector extendedContentDescriptionGuid("\x40\xA4\xD0\xD2\x07\xE3\xD2\x11\x97\xF0\x00\xA0\xC9\x5E\xA8\x50", 16);
|
||||
const ByteVector headerExtensionGuid("\xb5\x03\xbf_.\xa9\xcf\x11\x8e\xe3\x00\xc0\x0c Se", 16);
|
||||
const ByteVector metadataGuid("\xEA\xCB\xF8\xC5\xAF[wH\204g\xAA\214D\xFAL\xCA", 16);
|
||||
const ByteVector metadataLibraryGuid("\224\034#D\230\224\321I\241A\x1d\x13NEpT", 16);
|
||||
const ByteVector codecListGuid("\x40\x52\xd1\x86\x1d\x31\xd0\x11\xa3\xa4\x00\xa0\xc9\x03\x48\xf6", 16);
|
||||
const ByteVector contentEncryptionGuid("\xFB\xB3\x11\x22\x23\xBD\xD2\x11\xB4\xB7\x00\xA0\xC9\x55\xFC\x6E", 16);
|
||||
const ByteVector extendedContentEncryptionGuid("\x14\xE6\x8A\x29\x22\x26 \x17\x4C\xB9\x35\xDA\xE0\x7E\xE9\x28\x9C", 16);
|
||||
const ByteVector advancedContentEncryptionGuid("\xB6\x9B\x07\x7A\xA4\xDA\x12\x4E\xA5\xCA\x91\xD3\x8D\xC1\x1A\x8D", 16);
|
||||
} // namespace
|
||||
|
||||
class ASF::File::BaseObject
|
||||
class ASF::File::FilePrivate::BaseObject
|
||||
{
|
||||
public:
|
||||
ByteVector data;
|
||||
virtual ~BaseObject() {}
|
||||
virtual ByteVector guid() = 0;
|
||||
virtual void parse(ASF::File *file, unsigned int size);
|
||||
virtual ~BaseObject() = default;
|
||||
virtual ByteVector guid() const = 0;
|
||||
virtual void parse(ASF::File *file, long long size);
|
||||
virtual ByteVector render(ASF::File *file);
|
||||
};
|
||||
|
||||
class ASF::File::UnknownObject : public ASF::File::BaseObject
|
||||
class ASF::File::FilePrivate::UnknownObject : public ASF::File::FilePrivate::BaseObject
|
||||
{
|
||||
ByteVector myGuid;
|
||||
public:
|
||||
UnknownObject(const ByteVector &guid);
|
||||
ByteVector guid();
|
||||
ByteVector guid() const override;
|
||||
};
|
||||
|
||||
class ASF::File::FilePropertiesObject : public ASF::File::BaseObject
|
||||
class ASF::File::FilePrivate::FilePropertiesObject : public ASF::File::FilePrivate::BaseObject
|
||||
{
|
||||
public:
|
||||
ByteVector guid();
|
||||
void parse(ASF::File *file, uint size);
|
||||
ByteVector guid() const override;
|
||||
void parse(ASF::File *file, long long size) override;
|
||||
};
|
||||
|
||||
class ASF::File::StreamPropertiesObject : public ASF::File::BaseObject
|
||||
class ASF::File::FilePrivate::StreamPropertiesObject : public ASF::File::FilePrivate::BaseObject
|
||||
{
|
||||
public:
|
||||
ByteVector guid();
|
||||
void parse(ASF::File *file, uint size);
|
||||
ByteVector guid() const override;
|
||||
void parse(ASF::File *file, long long size) override;
|
||||
};
|
||||
|
||||
class ASF::File::ContentDescriptionObject : public ASF::File::BaseObject
|
||||
class ASF::File::FilePrivate::ContentDescriptionObject : public ASF::File::FilePrivate::BaseObject
|
||||
{
|
||||
public:
|
||||
ByteVector guid();
|
||||
void parse(ASF::File *file, uint size);
|
||||
ByteVector 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::ExtendedContentDescriptionObject : public ASF::File::BaseObject
|
||||
class ASF::File::FilePrivate::ExtendedContentDescriptionObject : public ASF::File::FilePrivate::BaseObject
|
||||
{
|
||||
public:
|
||||
ByteVectorList attributeData;
|
||||
ByteVector guid();
|
||||
void parse(ASF::File *file, uint size);
|
||||
ByteVector 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::MetadataObject : public ASF::File::BaseObject
|
||||
class ASF::File::FilePrivate::MetadataObject : public ASF::File::FilePrivate::BaseObject
|
||||
{
|
||||
public:
|
||||
ByteVectorList attributeData;
|
||||
ByteVector guid();
|
||||
void parse(ASF::File *file, uint size);
|
||||
ByteVector 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::MetadataLibraryObject : public ASF::File::BaseObject
|
||||
class ASF::File::FilePrivate::MetadataLibraryObject : public ASF::File::FilePrivate::BaseObject
|
||||
{
|
||||
public:
|
||||
ByteVectorList attributeData;
|
||||
ByteVector guid();
|
||||
void parse(ASF::File *file, uint size);
|
||||
ByteVector 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::HeaderExtensionObject : public ASF::File::BaseObject
|
||||
class ASF::File::FilePrivate::HeaderExtensionObject : public ASF::File::FilePrivate::BaseObject
|
||||
{
|
||||
public:
|
||||
List<ASF::File::BaseObject *> objects;
|
||||
~HeaderExtensionObject();
|
||||
ByteVector guid();
|
||||
void parse(ASF::File *file, uint size);
|
||||
ByteVector render(ASF::File *file);
|
||||
List<ASF::File::FilePrivate::BaseObject *> objects;
|
||||
HeaderExtensionObject();
|
||||
ByteVector guid() const override;
|
||||
void parse(ASF::File *file, long long size) override;
|
||||
ByteVector render(ASF::File *file) override;
|
||||
};
|
||||
|
||||
ASF::File::HeaderExtensionObject::~HeaderExtensionObject()
|
||||
class ASF::File::FilePrivate::CodecListObject : public ASF::File::FilePrivate::BaseObject
|
||||
{
|
||||
for(unsigned int i = 0; i < objects.size(); i++) {
|
||||
delete objects[i];
|
||||
}
|
||||
}
|
||||
public:
|
||||
ByteVector guid() const override;
|
||||
void parse(ASF::File *file, long long size) override;
|
||||
|
||||
void ASF::File::BaseObject::parse(ASF::File *file, unsigned int size)
|
||||
private:
|
||||
enum CodecType
|
||||
{
|
||||
Video = 0x0001,
|
||||
Audio = 0x0002,
|
||||
Unknown = 0xFFFF
|
||||
};
|
||||
};
|
||||
|
||||
void ASF::File::FilePrivate::BaseObject::parse(ASF::File *file, 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::BaseObject::render(ASF::File * /*file*/)
|
||||
ByteVector ASF::File::FilePrivate::BaseObject::render(ASF::File * /*file*/)
|
||||
{
|
||||
return guid() + ByteVector::fromLongLong(data.size() + 24, false) + data;
|
||||
}
|
||||
|
||||
ASF::File::UnknownObject::UnknownObject(const ByteVector &guid) : myGuid(guid)
|
||||
ASF::File::FilePrivate::UnknownObject::UnknownObject(const ByteVector &guid) : myGuid(guid)
|
||||
{
|
||||
}
|
||||
|
||||
ByteVector ASF::File::UnknownObject::guid()
|
||||
ByteVector ASF::File::FilePrivate::UnknownObject::guid() const
|
||||
{
|
||||
return myGuid;
|
||||
}
|
||||
|
||||
ByteVector ASF::File::FilePropertiesObject::guid()
|
||||
ByteVector ASF::File::FilePrivate::FilePropertiesObject::guid() const
|
||||
{
|
||||
return filePropertiesGuid;
|
||||
}
|
||||
|
||||
void ASF::File::FilePropertiesObject::parse(ASF::File *file, uint size)
|
||||
void ASF::File::FilePrivate::FilePropertiesObject::parse(ASF::File *file, long long size)
|
||||
{
|
||||
BaseObject::parse(file, size);
|
||||
file->d->properties->setLength((int)(data.mid(40, 8).toLongLong(false) / 10000000L - data.mid(56, 8).toLongLong(false) / 1000L));
|
||||
if(data.size() < 64) {
|
||||
debug("ASF::File::FilePrivate::FilePropertiesObject::parse() -- data is too short.");
|
||||
return;
|
||||
}
|
||||
|
||||
const long long duration = data.toLongLong(40, false);
|
||||
const long long preroll = data.toLongLong(56, false);
|
||||
file->d->properties->setLengthInMilliseconds(
|
||||
static_cast<int>(static_cast<double>(duration) / 10000.0 - static_cast<double>(preroll) + 0.5));
|
||||
}
|
||||
|
||||
ByteVector ASF::File::StreamPropertiesObject::guid()
|
||||
ByteVector ASF::File::FilePrivate::StreamPropertiesObject::guid() const
|
||||
{
|
||||
return streamPropertiesGuid;
|
||||
}
|
||||
|
||||
void ASF::File::StreamPropertiesObject::parse(ASF::File *file, uint size)
|
||||
void ASF::File::FilePrivate::StreamPropertiesObject::parse(ASF::File *file, long long size)
|
||||
{
|
||||
BaseObject::parse(file, size);
|
||||
file->d->properties->setChannels(data.mid(56, 2).toShort(false));
|
||||
file->d->properties->setSampleRate(data.mid(58, 4).toUInt(false));
|
||||
file->d->properties->setBitrate(data.mid(62, 4).toUInt(false) * 8 / 1000);
|
||||
if(data.size() < 70) {
|
||||
debug("ASF::File::FilePrivate::StreamPropertiesObject::parse() -- data is too short.");
|
||||
return;
|
||||
}
|
||||
|
||||
file->d->properties->setCodec(data.toUShort(54, false));
|
||||
file->d->properties->setChannels(data.toUShort(56, false));
|
||||
file->d->properties->setSampleRate(data.toUInt(58, false));
|
||||
file->d->properties->setBitrate(static_cast<int>(data.toUInt(62, false) * 8.0 / 1000.0 + 0.5));
|
||||
file->d->properties->setBitsPerSample(data.toUShort(68, false));
|
||||
}
|
||||
|
||||
ByteVector ASF::File::ContentDescriptionObject::guid()
|
||||
ByteVector ASF::File::FilePrivate::ContentDescriptionObject::guid() const
|
||||
{
|
||||
return contentDescriptionGuid;
|
||||
}
|
||||
|
||||
void ASF::File::ContentDescriptionObject::parse(ASF::File *file, uint /*size*/)
|
||||
void ASF::File::FilePrivate::ContentDescriptionObject::parse(ASF::File *file, long long /*size*/)
|
||||
{
|
||||
file->d->contentDescriptionObject = this;
|
||||
int titleLength = file->readWORD();
|
||||
int artistLength = file->readWORD();
|
||||
int copyrightLength = file->readWORD();
|
||||
int commentLength = file->readWORD();
|
||||
int ratingLength = file->readWORD();
|
||||
file->d->tag->setTitle(file->readString(titleLength));
|
||||
file->d->tag->setArtist(file->readString(artistLength));
|
||||
file->d->tag->setCopyright(file->readString(copyrightLength));
|
||||
file->d->tag->setComment(file->readString(commentLength));
|
||||
file->d->tag->setRating(file->readString(ratingLength));
|
||||
const int titleLength = readWORD(file);
|
||||
const int artistLength = readWORD(file);
|
||||
const int copyrightLength = readWORD(file);
|
||||
const int commentLength = readWORD(file);
|
||||
const int ratingLength = readWORD(file);
|
||||
file->d->tag->setTitle(readString(file,titleLength));
|
||||
file->d->tag->setArtist(readString(file,artistLength));
|
||||
file->d->tag->setCopyright(readString(file,copyrightLength));
|
||||
file->d->tag->setComment(readString(file,commentLength));
|
||||
file->d->tag->setRating(readString(file,ratingLength));
|
||||
}
|
||||
|
||||
ByteVector ASF::File::ContentDescriptionObject::render(ASF::File *file)
|
||||
ByteVector ASF::File::FilePrivate::ContentDescriptionObject::render(ASF::File *file)
|
||||
{
|
||||
ByteVector v1 = file->renderString(file->d->tag->title());
|
||||
ByteVector v2 = file->renderString(file->d->tag->artist());
|
||||
ByteVector v3 = file->renderString(file->d->tag->copyright());
|
||||
ByteVector v4 = file->renderString(file->d->tag->comment());
|
||||
ByteVector v5 = file->renderString(file->d->tag->rating());
|
||||
const ByteVector v1 = renderString(file->d->tag->title());
|
||||
const ByteVector v2 = renderString(file->d->tag->artist());
|
||||
const ByteVector v3 = renderString(file->d->tag->copyright());
|
||||
const ByteVector v4 = renderString(file->d->tag->comment());
|
||||
const ByteVector v5 = renderString(file->d->tag->rating());
|
||||
data.clear();
|
||||
data.append(ByteVector::fromShort(v1.size(), false));
|
||||
data.append(ByteVector::fromShort(v2.size(), false));
|
||||
data.append(ByteVector::fromShort(v3.size(), false));
|
||||
data.append(ByteVector::fromShort(v4.size(), false));
|
||||
data.append(ByteVector::fromShort(v5.size(), false));
|
||||
data.append(ByteVector::fromShort(static_cast<short>(v1.size()), false));
|
||||
data.append(ByteVector::fromShort(static_cast<short>(v2.size()), false));
|
||||
data.append(ByteVector::fromShort(static_cast<short>(v3.size()), false));
|
||||
data.append(ByteVector::fromShort(static_cast<short>(v4.size()), false));
|
||||
data.append(ByteVector::fromShort(static_cast<short>(v5.size()), false));
|
||||
data.append(v1);
|
||||
data.append(v2);
|
||||
data.append(v3);
|
||||
@@ -243,15 +285,14 @@ ByteVector ASF::File::ContentDescriptionObject::render(ASF::File *file)
|
||||
return BaseObject::render(file);
|
||||
}
|
||||
|
||||
ByteVector ASF::File::ExtendedContentDescriptionObject::guid()
|
||||
ByteVector ASF::File::FilePrivate::ExtendedContentDescriptionObject::guid() const
|
||||
{
|
||||
return extendedContentDescriptionGuid;
|
||||
}
|
||||
|
||||
void ASF::File::ExtendedContentDescriptionObject::parse(ASF::File *file, uint /*size*/)
|
||||
void ASF::File::FilePrivate::ExtendedContentDescriptionObject::parse(ASF::File *file, long long /*size*/)
|
||||
{
|
||||
file->d->extendedContentDescriptionObject = this;
|
||||
int count = file->readWORD();
|
||||
int count = readWORD(file);
|
||||
while(count--) {
|
||||
ASF::Attribute attribute;
|
||||
String name = attribute.parse(*file);
|
||||
@@ -259,23 +300,22 @@ void ASF::File::ExtendedContentDescriptionObject::parse(ASF::File *file, uint /*
|
||||
}
|
||||
}
|
||||
|
||||
ByteVector ASF::File::ExtendedContentDescriptionObject::render(ASF::File *file)
|
||||
ByteVector ASF::File::FilePrivate::ExtendedContentDescriptionObject::render(ASF::File *file)
|
||||
{
|
||||
data.clear();
|
||||
data.append(ByteVector::fromShort(attributeData.size(), false));
|
||||
data.append(attributeData.toByteVector(ByteVector::null));
|
||||
data.append(ByteVector::fromShort(static_cast<short>(attributeData.size()), false));
|
||||
data.append(attributeData.toByteVector(""));
|
||||
return BaseObject::render(file);
|
||||
}
|
||||
|
||||
ByteVector ASF::File::MetadataObject::guid()
|
||||
ByteVector ASF::File::FilePrivate::MetadataObject::guid() const
|
||||
{
|
||||
return metadataGuid;
|
||||
}
|
||||
|
||||
void ASF::File::MetadataObject::parse(ASF::File *file, uint /*size*/)
|
||||
void ASF::File::FilePrivate::MetadataObject::parse(ASF::File *file, long long /*size*/)
|
||||
{
|
||||
file->d->metadataObject = this;
|
||||
int count = file->readWORD();
|
||||
int count = readWORD(file);
|
||||
while(count--) {
|
||||
ASF::Attribute attribute;
|
||||
String name = attribute.parse(*file, 1);
|
||||
@@ -283,23 +323,22 @@ void ASF::File::MetadataObject::parse(ASF::File *file, uint /*size*/)
|
||||
}
|
||||
}
|
||||
|
||||
ByteVector ASF::File::MetadataObject::render(ASF::File *file)
|
||||
ByteVector ASF::File::FilePrivate::MetadataObject::render(ASF::File *file)
|
||||
{
|
||||
data.clear();
|
||||
data.append(ByteVector::fromShort(attributeData.size(), false));
|
||||
data.append(attributeData.toByteVector(ByteVector::null));
|
||||
data.append(ByteVector::fromShort(static_cast<short>(attributeData.size()), false));
|
||||
data.append(attributeData.toByteVector(""));
|
||||
return BaseObject::render(file);
|
||||
}
|
||||
|
||||
ByteVector ASF::File::MetadataLibraryObject::guid()
|
||||
ByteVector ASF::File::FilePrivate::MetadataLibraryObject::guid() const
|
||||
{
|
||||
return metadataLibraryGuid;
|
||||
}
|
||||
|
||||
void ASF::File::MetadataLibraryObject::parse(ASF::File *file, uint /*size*/)
|
||||
void ASF::File::FilePrivate::MetadataLibraryObject::parse(ASF::File *file, long long /*size*/)
|
||||
{
|
||||
file->d->metadataLibraryObject = this;
|
||||
int count = file->readWORD();
|
||||
int count = readWORD(file);
|
||||
while(count--) {
|
||||
ASF::Attribute attribute;
|
||||
String name = attribute.parse(*file, 2);
|
||||
@@ -307,46 +346,52 @@ void ASF::File::MetadataLibraryObject::parse(ASF::File *file, uint /*size*/)
|
||||
}
|
||||
}
|
||||
|
||||
ByteVector ASF::File::MetadataLibraryObject::render(ASF::File *file)
|
||||
ByteVector ASF::File::FilePrivate::MetadataLibraryObject::render(ASF::File *file)
|
||||
{
|
||||
data.clear();
|
||||
data.append(ByteVector::fromShort(attributeData.size(), false));
|
||||
data.append(attributeData.toByteVector(ByteVector::null));
|
||||
data.append(ByteVector::fromShort(static_cast<short>(attributeData.size()), false));
|
||||
data.append(attributeData.toByteVector(""));
|
||||
return BaseObject::render(file);
|
||||
}
|
||||
|
||||
ByteVector ASF::File::HeaderExtensionObject::guid()
|
||||
ASF::File::FilePrivate::HeaderExtensionObject::HeaderExtensionObject()
|
||||
{
|
||||
objects.setAutoDelete(true);
|
||||
}
|
||||
|
||||
ByteVector ASF::File::FilePrivate::HeaderExtensionObject::guid() const
|
||||
{
|
||||
return headerExtensionGuid;
|
||||
}
|
||||
|
||||
void ASF::File::HeaderExtensionObject::parse(ASF::File *file, uint /*size*/)
|
||||
void ASF::File::FilePrivate::HeaderExtensionObject::parse(ASF::File *file, long long /*size*/)
|
||||
{
|
||||
file->d->headerExtensionObject = this;
|
||||
file->seek(18, File::Current);
|
||||
long long dataSize = file->readDWORD();
|
||||
long long dataSize = readDWORD(file);
|
||||
long long dataPos = 0;
|
||||
while(dataPos < dataSize) {
|
||||
ByteVector guid = file->readBlock(16);
|
||||
if(guid.size() != 16) {
|
||||
ByteVector uid = file->readBlock(16);
|
||||
if(uid.size() != 16) {
|
||||
file->setValid(false);
|
||||
break;
|
||||
}
|
||||
bool ok;
|
||||
long long size = file->readQWORD(&ok);
|
||||
if(!ok) {
|
||||
long long size = readQWORD(file, &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, size);
|
||||
objects.append(obj);
|
||||
@@ -354,124 +399,128 @@ void ASF::File::HeaderExtensionObject::parse(ASF::File *file, uint /*size*/)
|
||||
}
|
||||
}
|
||||
|
||||
ByteVector ASF::File::HeaderExtensionObject::render(ASF::File *file)
|
||||
ByteVector ASF::File::FilePrivate::HeaderExtensionObject::render(ASF::File *file)
|
||||
{
|
||||
data.clear();
|
||||
for(unsigned int i = 0; i < objects.size(); i++) {
|
||||
data.append(objects[i]->render(file));
|
||||
for(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);
|
||||
}
|
||||
|
||||
ByteVector ASF::File::FilePrivate::CodecListObject::guid() const
|
||||
{
|
||||
return codecListGuid;
|
||||
}
|
||||
|
||||
void ASF::File::FilePrivate::CodecListObject::parse(ASF::File *file, long long size)
|
||||
{
|
||||
BaseObject::parse(file, size);
|
||||
if(data.size() <= 20) {
|
||||
debug("ASF::File::FilePrivate::CodecListObject::parse() -- data is too short.");
|
||||
return;
|
||||
}
|
||||
|
||||
unsigned int pos = 16;
|
||||
|
||||
const int count = data.toUInt(pos, false);
|
||||
pos += 4;
|
||||
|
||||
for(int i = 0; i < count; ++i) {
|
||||
|
||||
if(pos >= data.size())
|
||||
break;
|
||||
|
||||
const auto type = static_cast<CodecType>(data.toUShort(pos, false));
|
||||
pos += 2;
|
||||
|
||||
int nameLength = data.toUShort(pos, false);
|
||||
pos += 2;
|
||||
|
||||
const unsigned int namePos = pos;
|
||||
pos += nameLength * 2;
|
||||
|
||||
const int descLength = data.toUShort(pos, false);
|
||||
pos += 2;
|
||||
|
||||
const unsigned int descPos = pos;
|
||||
pos += descLength * 2;
|
||||
|
||||
const int infoLength = data.toUShort(pos, false);
|
||||
pos += 2 + infoLength * 2;
|
||||
|
||||
if(type == CodecListObject::Audio) {
|
||||
// First audio codec found.
|
||||
|
||||
const String name(data.mid(namePos, nameLength * 2), String::UTF16LE);
|
||||
file->d->properties->setCodecName(name.stripWhiteSpace());
|
||||
|
||||
const String desc(data.mid(descPos, descLength * 2), String::UTF16LE);
|
||||
file->d->properties->setCodecDescription(desc.stripWhiteSpace());
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// 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 readProperties, Properties::ReadStyle propertiesStyle)
|
||||
: TagLib::File(file)
|
||||
ASF::File::File(FileName file, bool, Properties::ReadStyle) :
|
||||
TagLib::File(file),
|
||||
d(std::make_unique<FilePrivate>())
|
||||
{
|
||||
d = new FilePrivate;
|
||||
read(readProperties, propertiesStyle);
|
||||
if(isOpen())
|
||||
read();
|
||||
}
|
||||
|
||||
ASF::File::File(IOStream *stream, bool readProperties, Properties::ReadStyle propertiesStyle)
|
||||
: TagLib::File(stream)
|
||||
ASF::File::File(IOStream *stream, bool, Properties::ReadStyle) :
|
||||
TagLib::File(stream),
|
||||
d(std::make_unique<FilePrivate>())
|
||||
{
|
||||
d = new FilePrivate;
|
||||
read(readProperties, propertiesStyle);
|
||||
if(isOpen())
|
||||
read();
|
||||
}
|
||||
|
||||
ASF::File::~File()
|
||||
{
|
||||
for(unsigned int i = 0; i < d->objects.size(); i++) {
|
||||
delete d->objects[i];
|
||||
}
|
||||
if(d->tag) {
|
||||
delete d->tag;
|
||||
}
|
||||
if(d->properties) {
|
||||
delete d->properties;
|
||||
}
|
||||
delete d;
|
||||
}
|
||||
ASF::File::~File() = default;
|
||||
|
||||
ASF::Tag *ASF::File::tag() const
|
||||
{
|
||||
return d->tag;
|
||||
return d->tag.get();
|
||||
}
|
||||
|
||||
PropertyMap ASF::File::properties() const
|
||||
{
|
||||
return d->tag->properties();
|
||||
}
|
||||
|
||||
void ASF::File::removeUnsupportedProperties(const StringList &properties)
|
||||
{
|
||||
d->tag->removeUnsupportedProperties(properties);
|
||||
}
|
||||
|
||||
PropertyMap ASF::File::setProperties(const PropertyMap &properties)
|
||||
{
|
||||
return d->tag->setProperties(properties);
|
||||
}
|
||||
|
||||
ASF::Properties *ASF::File::audioProperties() const
|
||||
{
|
||||
return d->properties;
|
||||
}
|
||||
|
||||
void ASF::File::read(bool /*readProperties*/, Properties::ReadStyle /*propertiesStyle*/)
|
||||
{
|
||||
if(!isValid())
|
||||
return;
|
||||
|
||||
ByteVector guid = readBlock(16);
|
||||
if(guid != headerGuid) {
|
||||
debug("ASF: Not an ASF file.");
|
||||
setValid(false);
|
||||
return;
|
||||
}
|
||||
|
||||
d->tag = new ASF::Tag();
|
||||
d->properties = new ASF::Properties();
|
||||
|
||||
bool ok;
|
||||
d->size = readQWORD(&ok);
|
||||
if(!ok) {
|
||||
setValid(false);
|
||||
return;
|
||||
}
|
||||
int numObjects = readDWORD(&ok);
|
||||
if(!ok) {
|
||||
setValid(false);
|
||||
return;
|
||||
}
|
||||
seek(2, Current);
|
||||
|
||||
for(int i = 0; i < numObjects; i++) {
|
||||
ByteVector guid = readBlock(16);
|
||||
if(guid.size() != 16) {
|
||||
setValid(false);
|
||||
break;
|
||||
}
|
||||
long size = (long)readQWORD(&ok);
|
||||
if(!ok) {
|
||||
setValid(false);
|
||||
break;
|
||||
}
|
||||
BaseObject *obj;
|
||||
if(guid == filePropertiesGuid) {
|
||||
obj = new FilePropertiesObject();
|
||||
}
|
||||
else if(guid == streamPropertiesGuid) {
|
||||
obj = new StreamPropertiesObject();
|
||||
}
|
||||
else if(guid == contentDescriptionGuid) {
|
||||
obj = new ContentDescriptionObject();
|
||||
}
|
||||
else if(guid == extendedContentDescriptionGuid) {
|
||||
obj = new ExtendedContentDescriptionObject();
|
||||
}
|
||||
else if(guid == headerExtensionGuid) {
|
||||
obj = new HeaderExtensionObject();
|
||||
}
|
||||
else {
|
||||
if(guid == contentEncryptionGuid ||
|
||||
guid == extendedContentEncryptionGuid ||
|
||||
guid == advancedContentEncryptionGuid) {
|
||||
d->properties->setEncrypted(true);
|
||||
}
|
||||
obj = new UnknownObject(guid);
|
||||
}
|
||||
obj->parse(this, size);
|
||||
d->objects.append(obj);
|
||||
}
|
||||
return d->properties.get();
|
||||
}
|
||||
|
||||
bool ASF::File::save()
|
||||
@@ -487,40 +536,43 @@ bool ASF::File::save()
|
||||
}
|
||||
|
||||
if(!d->contentDescriptionObject) {
|
||||
d->contentDescriptionObject = new ContentDescriptionObject();
|
||||
d->contentDescriptionObject = new FilePrivate::ContentDescriptionObject();
|
||||
d->objects.append(d->contentDescriptionObject);
|
||||
}
|
||||
if(!d->extendedContentDescriptionObject) {
|
||||
d->extendedContentDescriptionObject = new ExtendedContentDescriptionObject();
|
||||
d->extendedContentDescriptionObject = new FilePrivate::ExtendedContentDescriptionObject();
|
||||
d->objects.append(d->extendedContentDescriptionObject);
|
||||
}
|
||||
if(!d->headerExtensionObject) {
|
||||
d->headerExtensionObject = new HeaderExtensionObject();
|
||||
d->headerExtensionObject = new FilePrivate::HeaderExtensionObject();
|
||||
d->objects.append(d->headerExtensionObject);
|
||||
}
|
||||
if(!d->metadataObject) {
|
||||
d->metadataObject = new MetadataObject();
|
||||
d->metadataObject = new FilePrivate::MetadataObject();
|
||||
d->headerExtensionObject->objects.append(d->metadataObject);
|
||||
}
|
||||
if(!d->metadataLibraryObject) {
|
||||
d->metadataLibraryObject = new MetadataLibraryObject();
|
||||
d->metadataLibraryObject = new FilePrivate::MetadataLibraryObject();
|
||||
d->headerExtensionObject->objects.append(d->metadataLibraryObject);
|
||||
}
|
||||
|
||||
ASF::AttributeListMap::ConstIterator it = d->tag->attributeListMap().begin();
|
||||
for(; it != d->tag->attributeListMap().end(); it++) {
|
||||
const String &name = it->first;
|
||||
const AttributeList &attributes = it->second;
|
||||
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(unsigned int j = 0; j < attributes.size(); j++) {
|
||||
const Attribute &attribute = attributes[j];
|
||||
bool largeValue = attribute.dataSize() > 65535;
|
||||
if(!inExtendedContentDescriptionObject && !largeValue && attribute.language() == 0 && attribute.stream() == 0) {
|
||||
|
||||
for(const auto &attribute : attributes) {
|
||||
const bool largeValue = attribute.dataSize() > 65535;
|
||||
const bool guid = attribute.type() == Attribute::GuidType;
|
||||
|
||||
if(!inExtendedContentDescriptionObject && !guid && !largeValue && attribute.language() == 0 && attribute.stream() == 0) {
|
||||
d->extendedContentDescriptionObject->attributeData.append(attribute.render(name));
|
||||
inExtendedContentDescriptionObject = true;
|
||||
}
|
||||
else if(!inMetadataObject && !largeValue && attribute.language() == 0 && attribute.stream() != 0) {
|
||||
else if(!inMetadataObject && !guid && !largeValue && attribute.language() == 0 && attribute.stream() != 0) {
|
||||
d->metadataObject->attributeData.append(attribute.render(name, 1));
|
||||
inMetadataObject = true;
|
||||
}
|
||||
@@ -531,85 +583,104 @@ bool ASF::File::save()
|
||||
}
|
||||
|
||||
ByteVector data;
|
||||
for(unsigned int i = 0; i < d->objects.size(); i++) {
|
||||
data.append(d->objects[i]->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, 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;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// protected members
|
||||
// private members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
int ASF::File::readBYTE(bool *ok)
|
||||
void ASF::File::read()
|
||||
{
|
||||
ByteVector v = readBlock(1);
|
||||
if(v.size() != 1) {
|
||||
if(ok) *ok = false;
|
||||
return 0;
|
||||
}
|
||||
if(ok) *ok = true;
|
||||
return v[0];
|
||||
}
|
||||
if(!isValid())
|
||||
return;
|
||||
|
||||
int ASF::File::readWORD(bool *ok)
|
||||
{
|
||||
ByteVector v = readBlock(2);
|
||||
if(v.size() != 2) {
|
||||
if(ok) *ok = false;
|
||||
return 0;
|
||||
if(readBlock(16) != headerGuid) {
|
||||
debug("ASF::File::read(): Not an ASF file.");
|
||||
setValid(false);
|
||||
return;
|
||||
}
|
||||
if(ok) *ok = true;
|
||||
return v.toUShort(false);
|
||||
}
|
||||
|
||||
unsigned int ASF::File::readDWORD(bool *ok)
|
||||
{
|
||||
ByteVector v = readBlock(4);
|
||||
if(v.size() != 4) {
|
||||
if(ok) *ok = false;
|
||||
return 0;
|
||||
d->tag = std::make_unique<ASF::Tag>();
|
||||
d->properties = std::make_unique<ASF::Properties>();
|
||||
|
||||
bool ok;
|
||||
d->headerSize = readQWORD(this, &ok);
|
||||
if(!ok) {
|
||||
setValid(false);
|
||||
return;
|
||||
}
|
||||
if(ok) *ok = true;
|
||||
return v.toUInt(false);
|
||||
}
|
||||
|
||||
long long ASF::File::readQWORD(bool *ok)
|
||||
{
|
||||
ByteVector v = readBlock(8);
|
||||
if(v.size() != 8) {
|
||||
if(ok) *ok = false;
|
||||
return 0;
|
||||
int numObjects = readDWORD(this, &ok);
|
||||
if(!ok) {
|
||||
setValid(false);
|
||||
return;
|
||||
}
|
||||
if(ok) *ok = true;
|
||||
return v.toLongLong(false);
|
||||
}
|
||||
seek(2, Current);
|
||||
|
||||
String ASF::File::readString(int length)
|
||||
{
|
||||
ByteVector data = readBlock(length);
|
||||
unsigned int size = data.size();
|
||||
while (size >= 2) {
|
||||
if(data[size - 1] != '\0' || data[size - 2] != '\0') {
|
||||
FilePrivate::FilePropertiesObject *filePropertiesObject = nullptr;
|
||||
FilePrivate::StreamPropertiesObject *streamPropertiesObject = nullptr;
|
||||
for(int i = 0; i < numObjects; i++) {
|
||||
const ByteVector guid = readBlock(16);
|
||||
if(guid.size() != 16) {
|
||||
setValid(false);
|
||||
break;
|
||||
}
|
||||
size -= 2;
|
||||
auto size = readQWORD(this, &ok);
|
||||
if(!ok) {
|
||||
setValid(false);
|
||||
break;
|
||||
}
|
||||
FilePrivate::BaseObject *obj;
|
||||
if(guid == filePropertiesGuid) {
|
||||
filePropertiesObject = new FilePrivate::FilePropertiesObject();
|
||||
obj = filePropertiesObject;
|
||||
}
|
||||
else if(guid == streamPropertiesGuid) {
|
||||
streamPropertiesObject = new FilePrivate::StreamPropertiesObject();
|
||||
obj = streamPropertiesObject;
|
||||
}
|
||||
else if(guid == contentDescriptionGuid) {
|
||||
d->contentDescriptionObject = new FilePrivate::ContentDescriptionObject();
|
||||
obj = d->contentDescriptionObject;
|
||||
}
|
||||
else if(guid == extendedContentDescriptionGuid) {
|
||||
d->extendedContentDescriptionObject = new FilePrivate::ExtendedContentDescriptionObject();
|
||||
obj = d->extendedContentDescriptionObject;
|
||||
}
|
||||
else if(guid == headerExtensionGuid) {
|
||||
d->headerExtensionObject = new FilePrivate::HeaderExtensionObject();
|
||||
obj = d->headerExtensionObject;
|
||||
}
|
||||
else if(guid == codecListGuid) {
|
||||
obj = new FilePrivate::CodecListObject();
|
||||
}
|
||||
else {
|
||||
if(guid == contentEncryptionGuid ||
|
||||
guid == extendedContentEncryptionGuid ||
|
||||
guid == advancedContentEncryptionGuid) {
|
||||
d->properties->setEncrypted(true);
|
||||
}
|
||||
obj = new FilePrivate::UnknownObject(guid);
|
||||
}
|
||||
obj->parse(this, size);
|
||||
d->objects.append(obj);
|
||||
}
|
||||
if(size != data.size()) {
|
||||
data.resize(size);
|
||||
}
|
||||
return String(data, String::UTF16LE);
|
||||
}
|
||||
|
||||
ByteVector ASF::File::renderString(const String &str, bool includeLength)
|
||||
{
|
||||
ByteVector data = str.data(String::UTF16LE) + ByteVector::fromShort(0, false);
|
||||
if(includeLength) {
|
||||
data = ByteVector::fromShort(data.size(), false) + data;
|
||||
if(!filePropertiesObject || !streamPropertiesObject) {
|
||||
debug("ASF::File::read(): Missing mandatory header objects.");
|
||||
setValid(false);
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
|
||||
@@ -26,16 +26,16 @@
|
||||
#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 {
|
||||
//! An implementation of TagLib::File with ASF specific methods
|
||||
|
||||
/*!
|
||||
* This implements and provides an interface for ASF files to the
|
||||
@@ -48,29 +48,35 @@ namespace TagLib {
|
||||
public:
|
||||
|
||||
/*!
|
||||
* Contructs an ASF file from \a file. If \a readProperties is true the
|
||||
* file's audio properties will also be read using \a propertiesStyle. If
|
||||
* false, \a propertiesStyle is ignored.
|
||||
* Constructs an ASF file from \a file.
|
||||
*
|
||||
* \note In the current implementation, both \a readProperties and
|
||||
* \a propertiesStyle are ignored.
|
||||
* \a propertiesStyle are ignored. The audio properties are always
|
||||
* read.
|
||||
*/
|
||||
File(FileName file, bool readProperties = true, Properties::ReadStyle propertiesStyle = Properties::Average);
|
||||
File(FileName file, bool readProperties = true,
|
||||
Properties::ReadStyle propertiesStyle = Properties::Average);
|
||||
|
||||
/*!
|
||||
* Contructs an ASF file from \a file. If \a readProperties is true the
|
||||
* file's audio properties will also be read using \a propertiesStyle. If
|
||||
* false, \a propertiesStyle is ignored.
|
||||
* Constructs an ASF file from \a stream.
|
||||
*
|
||||
* \note In the current implementation, both \a readProperties and
|
||||
* \a propertiesStyle are ignored.
|
||||
* \a propertiesStyle are ignored. The audio properties are always
|
||||
* read.
|
||||
*
|
||||
* \note TagLib will *not* take ownership of the stream, the caller is
|
||||
* responsible for deleting it after the File object.
|
||||
*/
|
||||
File(IOStream *stream, bool readProperties = true, Properties::ReadStyle propertiesStyle = Properties::Average);
|
||||
File(IOStream *stream, bool readProperties = true,
|
||||
Properties::ReadStyle propertiesStyle = Properties::Average);
|
||||
|
||||
/*!
|
||||
* 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.
|
||||
@@ -82,49 +88,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 override;
|
||||
|
||||
/*!
|
||||
* Removes unsupported properties. Forwards to the actual Tag's
|
||||
* removeUnsupportedProperties() function.
|
||||
*/
|
||||
void removeUnsupportedProperties(const StringList &properties) override;
|
||||
|
||||
/*!
|
||||
* Implements the unified property interface -- import function.
|
||||
*/
|
||||
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.
|
||||
* This returns \c true if the save was successful.
|
||||
*/
|
||||
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:
|
||||
|
||||
int readBYTE(bool *ok = 0);
|
||||
int readWORD(bool *ok = 0);
|
||||
unsigned int readDWORD(bool *ok = 0);
|
||||
long long readQWORD(bool *ok = 0);
|
||||
static ByteVector renderString(const String &str, bool includeLength = false);
|
||||
String readString(int len);
|
||||
void read(bool readProperties, Properties::ReadStyle propertiesStyle);
|
||||
|
||||
friend class Attribute;
|
||||
friend class Picture;
|
||||
|
||||
class BaseObject;
|
||||
class UnknownObject;
|
||||
class FilePropertiesObject;
|
||||
class StreamPropertiesObject;
|
||||
class ContentDescriptionObject;
|
||||
class ExtendedContentDescriptionObject;
|
||||
class HeaderExtensionObject;
|
||||
class MetadataObject;
|
||||
class MetadataLibraryObject;
|
||||
void read();
|
||||
|
||||
class FilePrivate;
|
||||
FilePrivate *d;
|
||||
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
|
||||
std::unique_ptr<FilePrivate> d;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace ASF
|
||||
} // namespace TagLib
|
||||
|
||||
#endif
|
||||
|
||||
@@ -23,19 +23,14 @@
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <taglib.h>
|
||||
#include <tdebug.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;
|
||||
@@ -49,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
|
||||
{
|
||||
@@ -119,26 +105,25 @@ 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) +
|
||||
ASF::File::renderString(d->mimeType) +
|
||||
ASF::File::renderString(d->description) +
|
||||
renderString(d->mimeType) +
|
||||
renderString(d->description) +
|
||||
d->picture;
|
||||
}
|
||||
|
||||
@@ -148,8 +133,8 @@ void ASF::Picture::parse(const ByteVector& bytes)
|
||||
if(bytes.size() < 9)
|
||||
return;
|
||||
int pos = 0;
|
||||
d->type = (Type)bytes[0]; ++pos;
|
||||
uint dataLen = bytes.mid(pos, 4).toUInt(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);
|
||||
|
||||
@@ -170,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()
|
||||
@@ -179,4 +163,3 @@ ASF::Picture ASF::Picture::fromInvalid()
|
||||
ret.d->valid = false;
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
@@ -28,8 +28,8 @@
|
||||
|
||||
#include "tstring.h"
|
||||
#include "tbytevector.h"
|
||||
#include "tpicturetype.h"
|
||||
#include "taglib_export.h"
|
||||
#include "attachedpictureframe.h"
|
||||
|
||||
namespace TagLib
|
||||
{
|
||||
@@ -39,9 +39,9 @@ namespace TagLib
|
||||
//! An ASF attached picture interface implementation
|
||||
|
||||
/*!
|
||||
* This is an implementation of ASF attached pictures interface. Pictures may be
|
||||
* This is an implementation of ASF attached pictures. Pictures may be
|
||||
* included in attributes, one per WM/Picture attribute (but there may be multiple WM/Picture
|
||||
* attribute in a single tag). These pictures are usually in either JPEG or
|
||||
* attributes in a single tag). These pictures are usually in either JPEG or
|
||||
* PNG format.
|
||||
* \see Attribute::toPicture()
|
||||
* \see Attribute::Attribute(const Picture& picture)
|
||||
@@ -49,53 +49,10 @@ namespace TagLib
|
||||
class TAGLIB_EXPORT Picture {
|
||||
public:
|
||||
|
||||
/*!
|
||||
/*
|
||||
* 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.
|
||||
@@ -103,14 +60,14 @@ namespace TagLib
|
||||
Picture();
|
||||
|
||||
/*!
|
||||
* Construct an picture as a copy of \a other.
|
||||
* Construct a picture as a copy of \a other.
|
||||
*/
|
||||
Picture(const Picture& other);
|
||||
|
||||
/*!
|
||||
* Destroys the picture.
|
||||
*/
|
||||
virtual ~Picture();
|
||||
~Picture();
|
||||
|
||||
/*!
|
||||
* Copies the contents of \a other into this picture.
|
||||
@@ -118,7 +75,12 @@ namespace TagLib
|
||||
Picture& operator=(const Picture& other);
|
||||
|
||||
/*!
|
||||
* Returns true if Picture stores valid picture
|
||||
* Exchanges the content of the Picture with the content of \a other.
|
||||
*/
|
||||
void swap(Picture &other) noexcept;
|
||||
|
||||
/*!
|
||||
* Returns \c true if Picture stores valid picture
|
||||
*/
|
||||
bool isValid() const;
|
||||
|
||||
@@ -173,7 +135,7 @@ namespace TagLib
|
||||
/*!
|
||||
* Returns the image data as a ByteVector.
|
||||
*
|
||||
* \note ByteVector has a data() method that returns a const char * which
|
||||
* \note ByteVector has a data() method that returns a <tt>const char *</tt> which
|
||||
* should make it easy to export this data to external programs.
|
||||
*
|
||||
* \see setPicture()
|
||||
@@ -205,13 +167,14 @@ namespace TagLib
|
||||
/* THIS IS PRIVATE, DON'T TOUCH IT! */
|
||||
void parse(const ByteVector& );
|
||||
static Picture fromInvalid();
|
||||
friend class Attribute;
|
||||
#endif
|
||||
|
||||
private:
|
||||
class PicturePrivate;
|
||||
PicturePrivate *d;
|
||||
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
|
||||
std::shared_ptr<PicturePrivate> d;
|
||||
};
|
||||
}
|
||||
}
|
||||
} // namespace ASF
|
||||
} // namespace TagLib
|
||||
|
||||
#endif // ASFPICTURE_H
|
||||
|
||||
@@ -23,12 +23,6 @@
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <tdebug.h>
|
||||
#include <tstring.h>
|
||||
#include "asfproperties.h"
|
||||
|
||||
using namespace TagLib;
|
||||
@@ -36,30 +30,30 @@ using namespace TagLib;
|
||||
class ASF::Properties::PropertiesPrivate
|
||||
{
|
||||
public:
|
||||
PropertiesPrivate(): length(0), bitrate(0), sampleRate(0), channels(0), encrypted(false) {}
|
||||
int length;
|
||||
int bitrate;
|
||||
int sampleRate;
|
||||
int channels;
|
||||
bool encrypted;
|
||||
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 { false };
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// public members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
ASF::Properties::Properties() : AudioProperties(AudioProperties::Average)
|
||||
ASF::Properties::Properties() :
|
||||
AudioProperties(AudioProperties::Average),
|
||||
d(std::make_unique<PropertiesPrivate>())
|
||||
{
|
||||
d = new PropertiesPrivate;
|
||||
}
|
||||
|
||||
ASF::Properties::~Properties()
|
||||
{
|
||||
if(d)
|
||||
delete d;
|
||||
}
|
||||
ASF::Properties::~Properties() = default;
|
||||
|
||||
int ASF::Properties::length() const
|
||||
int ASF::Properties::lengthInMilliseconds() const
|
||||
{
|
||||
return d->length;
|
||||
}
|
||||
@@ -79,6 +73,26 @@ int ASF::Properties::channels() const
|
||||
return d->channels;
|
||||
}
|
||||
|
||||
int ASF::Properties::bitsPerSample() const
|
||||
{
|
||||
return d->bitsPerSample;
|
||||
}
|
||||
|
||||
ASF::Properties::Codec ASF::Properties::codec() const
|
||||
{
|
||||
return d->codec;
|
||||
}
|
||||
|
||||
String ASF::Properties::codecName() const
|
||||
{
|
||||
return d->codecName;
|
||||
}
|
||||
|
||||
String ASF::Properties::codecDescription() const
|
||||
{
|
||||
return d->codecDescription;
|
||||
}
|
||||
|
||||
bool ASF::Properties::isEncrypted() const
|
||||
{
|
||||
return d->encrypted;
|
||||
@@ -88,28 +102,64 @@ bool ASF::Properties::isEncrypted() const
|
||||
// private members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void ASF::Properties::setLength(int length)
|
||||
void ASF::Properties::setLengthInMilliseconds(int value)
|
||||
{
|
||||
d->length = length;
|
||||
d->length = value;
|
||||
}
|
||||
|
||||
void ASF::Properties::setBitrate(int length)
|
||||
void ASF::Properties::setBitrate(int value)
|
||||
{
|
||||
d->bitrate = length;
|
||||
d->bitrate = value;
|
||||
}
|
||||
|
||||
void ASF::Properties::setSampleRate(int length)
|
||||
void ASF::Properties::setSampleRate(int value)
|
||||
{
|
||||
d->sampleRate = length;
|
||||
d->sampleRate = value;
|
||||
}
|
||||
|
||||
void ASF::Properties::setChannels(int length)
|
||||
void ASF::Properties::setChannels(int value)
|
||||
{
|
||||
d->channels = length;
|
||||
d->channels = value;
|
||||
}
|
||||
|
||||
void ASF::Properties::setEncrypted(bool encrypted)
|
||||
void ASF::Properties::setBitsPerSample(int value)
|
||||
{
|
||||
d->encrypted = encrypted;
|
||||
d->bitsPerSample = value;
|
||||
}
|
||||
|
||||
void ASF::Properties::setCodec(int value)
|
||||
{
|
||||
switch(value)
|
||||
{
|
||||
case 0x0160:
|
||||
d->codec = WMA1;
|
||||
break;
|
||||
case 0x0161:
|
||||
d->codec = WMA2;
|
||||
break;
|
||||
case 0x0162:
|
||||
d->codec = WMA9Pro;
|
||||
break;
|
||||
case 0x0163:
|
||||
d->codec = WMA9Lossless;
|
||||
break;
|
||||
default:
|
||||
d->codec = Unknown;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void ASF::Properties::setCodecName(const String &value)
|
||||
{
|
||||
d->codecName = value;
|
||||
}
|
||||
|
||||
void ASF::Properties::setCodecDescription(const String &value)
|
||||
{
|
||||
d->codecDescription = value;
|
||||
}
|
||||
|
||||
void ASF::Properties::setEncrypted(bool value)
|
||||
{
|
||||
d->encrypted = value;
|
||||
}
|
||||
|
||||
@@ -26,51 +26,137 @@
|
||||
#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
|
||||
{
|
||||
public:
|
||||
|
||||
/*!
|
||||
* Create an instance of ASF::Properties.
|
||||
* Audio codec types which can be used in ASF files.
|
||||
*/
|
||||
enum Codec
|
||||
{
|
||||
/*!
|
||||
* Couldn't detect the codec.
|
||||
*/
|
||||
Unknown = 0,
|
||||
|
||||
/*!
|
||||
* Windows Media Audio 1
|
||||
*/
|
||||
WMA1,
|
||||
|
||||
/*!
|
||||
* Windows Media Audio 2 or above
|
||||
*/
|
||||
WMA2,
|
||||
|
||||
/*!
|
||||
* Windows Media Audio 9 Professional
|
||||
*/
|
||||
WMA9Pro,
|
||||
|
||||
/*!
|
||||
* Windows Media Audio 9 Lossless
|
||||
*/
|
||||
WMA9Lossless,
|
||||
};
|
||||
|
||||
/*!
|
||||
* Creates an instance of ASF::Properties.
|
||||
*/
|
||||
Properties();
|
||||
|
||||
/*!
|
||||
* Destroys this ASF::Properties instance.
|
||||
*/
|
||||
virtual ~Properties();
|
||||
~Properties() override;
|
||||
|
||||
// Reimplementations.
|
||||
virtual int length() const;
|
||||
virtual int bitrate() const;
|
||||
virtual int sampleRate() const;
|
||||
virtual int channels() const;
|
||||
Properties(const Properties &) = delete;
|
||||
Properties &operator=(const Properties &) = delete;
|
||||
|
||||
/*!
|
||||
* Returns the length of the file in milliseconds.
|
||||
*
|
||||
* \see lengthInSeconds()
|
||||
*/
|
||||
int lengthInMilliseconds() const override;
|
||||
|
||||
/*!
|
||||
* Returns the average bit rate of the file in kb/s.
|
||||
*/
|
||||
int bitrate() const override;
|
||||
|
||||
/*!
|
||||
* Returns the sample rate in Hz.
|
||||
*/
|
||||
int sampleRate() const override;
|
||||
|
||||
/*!
|
||||
* Returns the number of audio channels.
|
||||
*/
|
||||
int channels() const override;
|
||||
|
||||
/*!
|
||||
* Returns the number of bits per audio sample.
|
||||
*/
|
||||
int bitsPerSample() const;
|
||||
|
||||
/*!
|
||||
* Returns the codec used in the file.
|
||||
*
|
||||
* \see codecName()
|
||||
* \see codecDescription()
|
||||
*/
|
||||
Codec codec() const;
|
||||
|
||||
/*!
|
||||
* Returns the concrete codec name, for example "Windows Media Audio 9.1"
|
||||
* used in the file if available, otherwise an empty string.
|
||||
*
|
||||
* \see codec()
|
||||
* \see codecDescription()
|
||||
*/
|
||||
String codecName() const;
|
||||
|
||||
/*!
|
||||
* Returns the codec description, typically contains the encoder settings,
|
||||
* for example "VBR Quality 50, 44kHz, stereo 1-pass VBR" if available,
|
||||
* otherwise an empty string.
|
||||
*
|
||||
* \see codec()
|
||||
* \see codecName()
|
||||
*/
|
||||
String codecDescription() const;
|
||||
|
||||
/*!
|
||||
* Returns whether or not the file is encrypted.
|
||||
*/
|
||||
bool isEncrypted() const;
|
||||
|
||||
#ifndef DO_NOT_DOCUMENT
|
||||
void setLength(int value);
|
||||
void setLengthInMilliseconds(int value);
|
||||
void setBitrate(int value);
|
||||
void setSampleRate(int value);
|
||||
void setChannels(int value);
|
||||
void setBitsPerSample(int value);
|
||||
void setCodec(int value);
|
||||
void setCodecName(const String &value);
|
||||
void setCodecDescription(const String &value);
|
||||
void setEncrypted(bool value);
|
||||
#endif
|
||||
|
||||
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,14 +23,29 @@
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#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:
|
||||
@@ -42,17 +57,12 @@ public:
|
||||
AttributeListMap attributeListMap;
|
||||
};
|
||||
|
||||
ASF::Tag::Tag()
|
||||
: TagLib::Tag()
|
||||
ASF::Tag::Tag() :
|
||||
d(std::make_unique<TagPrivate>())
|
||||
{
|
||||
d = new TagPrivate;
|
||||
}
|
||||
|
||||
ASF::Tag::~Tag()
|
||||
{
|
||||
if(d)
|
||||
delete d;
|
||||
}
|
||||
ASF::Tag::~Tag() = default;
|
||||
|
||||
String ASF::Tag::title() const
|
||||
{
|
||||
@@ -67,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
|
||||
@@ -99,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();
|
||||
@@ -110,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)
|
||||
@@ -149,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));
|
||||
}
|
||||
@@ -164,18 +175,36 @@ ASF::AttributeListMap& ASF::Tag::attributeListMap()
|
||||
return d->attributeListMap;
|
||||
}
|
||||
|
||||
const ASF::AttributeListMap &ASF::Tag::attributeListMap() const
|
||||
{
|
||||
return d->attributeListMap;
|
||||
}
|
||||
|
||||
bool ASF::Tag::contains(const String &key) const
|
||||
{
|
||||
return d->attributeListMap.contains(key);
|
||||
}
|
||||
|
||||
void ASF::Tag::removeItem(const String &key)
|
||||
{
|
||||
AttributeListMap::Iterator it = d->attributeListMap.find(key);
|
||||
if(it != d->attributeListMap.end())
|
||||
d->attributeListMap.erase(it);
|
||||
d->attributeListMap.erase(key);
|
||||
}
|
||||
|
||||
ASF::AttributeList ASF::Tag::attribute(const String &name) const
|
||||
{
|
||||
return d->attributeListMap[name];
|
||||
}
|
||||
|
||||
void ASF::Tag::setAttribute(const String &name, const Attribute &attribute)
|
||||
{
|
||||
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)
|
||||
{
|
||||
d->attributeListMap.insert(name, values);
|
||||
}
|
||||
|
||||
void ASF::Tag::addAttribute(const String &name, const Attribute &attribute)
|
||||
@@ -196,3 +225,224 @@ bool ASF::Tag::isEmpty() const
|
||||
d->attributeListMap.isEmpty();
|
||||
}
|
||||
|
||||
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
|
||||
{
|
||||
PropertyMap props;
|
||||
|
||||
if(!d->title.isEmpty()) {
|
||||
props["TITLE"] = d->title;
|
||||
}
|
||||
if(!d->artist.isEmpty()) {
|
||||
props["ARTIST"] = d->artist;
|
||||
}
|
||||
if(!d->copyright.isEmpty()) {
|
||||
props["COPYRIGHT"] = d->copyright;
|
||||
}
|
||||
if(!d->comment.isEmpty()) {
|
||||
props["COMMENT"] = d->comment;
|
||||
}
|
||||
|
||||
for(const auto &[k, attributes] : std::as_const(d->attributeListMap)) {
|
||||
if(const String key = translateKey(k); !key.isEmpty()) {
|
||||
for(const auto &attr : attributes) {
|
||||
if(key == "TRACKNUMBER") {
|
||||
if(attr.type() == ASF::Attribute::DWordType)
|
||||
props.insert(key, String::number(attr.toUInt()));
|
||||
else
|
||||
props.insert(key, attr.toString());
|
||||
}
|
||||
else {
|
||||
props.insert(key, attr.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
props.addUnsupportedData(k);
|
||||
}
|
||||
}
|
||||
return props;
|
||||
}
|
||||
|
||||
void ASF::Tag::removeUnsupportedProperties(const StringList &props)
|
||||
{
|
||||
for(const auto &prop : props)
|
||||
d->attributeListMap.erase(prop);
|
||||
}
|
||||
|
||||
PropertyMap ASF::Tag::setProperties(const PropertyMap &props)
|
||||
{
|
||||
static Map<String, String> reverseKeyMap;
|
||||
if(reverseKeyMap.isEmpty()) {
|
||||
for(const auto &[k, t] : keyTranslation) {
|
||||
reverseKeyMap[t] = k;
|
||||
}
|
||||
}
|
||||
|
||||
const PropertyMap origProps = properties();
|
||||
for(const auto &[prop, _] : origProps) {
|
||||
if(!props.contains(prop) || props[prop].isEmpty()) {
|
||||
if(prop == "TITLE") {
|
||||
d->title.clear();
|
||||
}
|
||||
else if(prop == "ARTIST") {
|
||||
d->artist.clear();
|
||||
}
|
||||
else if(prop == "COMMENT") {
|
||||
d->comment.clear();
|
||||
}
|
||||
else if(prop == "COPYRIGHT") {
|
||||
d->copyright.clear();
|
||||
}
|
||||
else {
|
||||
d->attributeListMap.erase(reverseKeyMap[prop]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PropertyMap ignoredProps;
|
||||
for(const auto &[prop, attributes] : props) {
|
||||
if(reverseKeyMap.contains(prop)) {
|
||||
String name = reverseKeyMap[prop];
|
||||
removeItem(name);
|
||||
for(const auto &attr : attributes) {
|
||||
addAttribute(name, attr);
|
||||
}
|
||||
}
|
||||
else if(prop == "TITLE") {
|
||||
d->title = attributes.toString();
|
||||
}
|
||||
else if(prop == "ARTIST") {
|
||||
d->artist = attributes.toString();
|
||||
}
|
||||
else if(prop == "COMMENT") {
|
||||
d->comment = attributes.toString();
|
||||
}
|
||||
else if(prop == "COPYRIGHT") {
|
||||
d->copyright = attributes.toString();
|
||||
}
|
||||
else {
|
||||
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;
|
||||
if(const String uppercaseKey = key.upper(); 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)
|
||||
{
|
||||
if(const String uppercaseKey = key.upper(); 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,20 @@
|
||||
#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>;
|
||||
|
||||
//! An implementation of ASF (WMA) tags
|
||||
|
||||
class TAGLIB_EXPORT Tag : public TagLib::Tag {
|
||||
|
||||
@@ -47,34 +49,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.
|
||||
* an empty string 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
|
||||
* Returns the genre name; if no genre is present in the tag an empty string
|
||||
* will be returned.
|
||||
*/
|
||||
virtual String genre() const;
|
||||
String genre() const override;
|
||||
|
||||
/*!
|
||||
* Returns the rating.
|
||||
@@ -82,105 +87,135 @@ namespace TagLib {
|
||||
virtual String rating() const;
|
||||
|
||||
/*!
|
||||
* Returns the genre name; if no genre is present in the tag String::null
|
||||
* will be returned.
|
||||
* Returns the copyright information; if no copyright information is
|
||||
* present in the tag an empty string will be returned.
|
||||
*/
|
||||
virtual String copyright() const;
|
||||
|
||||
/*!
|
||||
* 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 an empty string 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
|
||||
* Returns \c 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;
|
||||
|
||||
/*!
|
||||
* Returns a reference to the item list map. This is an AttributeListMap of
|
||||
* all of the items in the tag.
|
||||
*
|
||||
* This is the most powerfull structure for accessing the items of the tag.
|
||||
* \warning You should not modify this data structure directly, instead
|
||||
* use attributeListMap() const, contains(), removeItem(),
|
||||
* attribute(), setAttribute(), addAttribute().
|
||||
*/
|
||||
AttributeListMap &attributeListMap();
|
||||
|
||||
/*!
|
||||
* Removes the \a key attribute from the tag
|
||||
* Returns a reference to the item list map. This is an AttributeListMap of
|
||||
* all of the items in the tag.
|
||||
*/
|
||||
void removeItem(const String &name);
|
||||
const AttributeListMap &attributeListMap() 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.
|
||||
* \return \c true if a value for \a key is currently set.
|
||||
*/
|
||||
bool contains(const String &key) const;
|
||||
|
||||
/*!
|
||||
* Removes the \a key attribute from the tag
|
||||
*/
|
||||
void removeItem(const String &key);
|
||||
|
||||
/*!
|
||||
* \return The list of values for the key \a name, or an empty list if no
|
||||
* values have been set.
|
||||
*/
|
||||
AttributeList attribute(const String &name) const;
|
||||
|
||||
/*!
|
||||
* Sets the \a 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);
|
||||
|
||||
/*!
|
||||
* 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 multiple \a values to the key \a name.
|
||||
*/
|
||||
void setAttribute(const String &name, const AttributeList &values);
|
||||
|
||||
/*!
|
||||
* 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 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
|
||||
|
||||
104
taglib/asf/asfutils.h
Normal file
104
taglib/asf/asfutils.h
Normal file
@@ -0,0 +1,104 @@
|
||||
/***************************************************************************
|
||||
copyright : (C) 2015 by Tsuda Kageyu
|
||||
email : tsuda.kageyu@gmail.com
|
||||
***************************************************************************/
|
||||
|
||||
/***************************************************************************
|
||||
* This library is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU Lesser General Public License version *
|
||||
* 2.1 as published by the Free Software Foundation. *
|
||||
* *
|
||||
* This library is distributed in the hope that it will be useful, but *
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
||||
* Lesser General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Lesser General Public *
|
||||
* License along with this library; if not, write to the Free Software *
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
|
||||
* 02110-1301 USA *
|
||||
* *
|
||||
* Alternatively, this file is available under the Mozilla Public *
|
||||
* License Version 1.1. You may obtain a copy of the License at *
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef TAGLIB_ASFUTILS_H
|
||||
#define TAGLIB_ASFUTILS_H
|
||||
|
||||
// THIS FILE IS NOT A PART OF THE TAGLIB API
|
||||
|
||||
#ifndef DO_NOT_DOCUMENT // tell Doxygen not to document this header
|
||||
|
||||
namespace TagLib
|
||||
{
|
||||
namespace ASF
|
||||
{
|
||||
namespace
|
||||
{
|
||||
|
||||
inline unsigned short readWORD(File *file, bool *ok = nullptr)
|
||||
{
|
||||
const ByteVector v = file->readBlock(2);
|
||||
if(v.size() != 2) {
|
||||
if(ok) *ok = false;
|
||||
return 0;
|
||||
}
|
||||
if(ok) *ok = true;
|
||||
return v.toUShort(false);
|
||||
}
|
||||
|
||||
inline unsigned int readDWORD(File *file, bool *ok = 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);
|
||||
}
|
||||
|
||||
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(static_cast<short>(data.size()), false) + data;
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace ASF
|
||||
} // namespace TagLib
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -29,16 +29,37 @@ using namespace TagLib;
|
||||
|
||||
class AudioProperties::AudioPropertiesPrivate
|
||||
{
|
||||
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// public methods
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
AudioProperties::~AudioProperties()
|
||||
{
|
||||
AudioProperties::~AudioProperties() = default;
|
||||
|
||||
int AudioProperties::length() const
|
||||
{
|
||||
return lengthInSeconds();
|
||||
}
|
||||
|
||||
int AudioProperties::lengthInSeconds() const
|
||||
{
|
||||
return lengthInMilliseconds() / 1000;
|
||||
}
|
||||
|
||||
int AudioProperties::lengthInMilliseconds() const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int AudioProperties::bitrate() const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int AudioProperties::sampleRate() const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -47,5 +68,4 @@ AudioProperties::~AudioProperties()
|
||||
|
||||
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 {
|
||||
@@ -34,7 +37,7 @@ namespace TagLib {
|
||||
|
||||
/*!
|
||||
* The values here are common to most audio formats. For more specific, codec
|
||||
* dependant values, please see see the subclasses APIs. This is meant to
|
||||
* dependent values, please see the subclasses APIs. This is meant to
|
||||
* compliment the TagLib::File and TagLib::Tag APIs in providing a simple
|
||||
* interface that is sufficient for most applications.
|
||||
*/
|
||||
@@ -64,22 +67,46 @@ namespace TagLib {
|
||||
*/
|
||||
virtual ~AudioProperties();
|
||||
|
||||
AudioProperties(const AudioProperties &) = delete;
|
||||
AudioProperties &operator=(const AudioProperties &) = delete;
|
||||
|
||||
/*!
|
||||
* Returns the length of the file in seconds.
|
||||
* 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
|
||||
* the nearest whole second.
|
||||
*
|
||||
* \see lengthInMilliseconds()
|
||||
*/
|
||||
virtual int length() const = 0;
|
||||
virtual int lengthInSeconds() const;
|
||||
|
||||
/*!
|
||||
* Returns the length of the file in milliseconds.
|
||||
*
|
||||
* \see lengthInSeconds()
|
||||
*/
|
||||
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.
|
||||
@@ -98,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
|
||||
922
taglib/dsdiff/dsdifffile.cpp
Normal file
922
taglib/dsdiff/dsdifffile.cpp
Normal file
@@ -0,0 +1,922 @@
|
||||
/***************************************************************************
|
||||
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;
|
||||
};
|
||||
|
||||
using ChunkList = std::vector<Chunk64>;
|
||||
|
||||
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;
|
||||
|
||||
return std::none_of(name.cbegin(), name.cend(),
|
||||
[](unsigned char c) { return c < 32 || c > 126; });
|
||||
}
|
||||
|
||||
enum {
|
||||
ID3v2Index = 0,
|
||||
DIINIndex = 1
|
||||
};
|
||||
enum {
|
||||
PROPChunk = 0,
|
||||
DIINChunk = 1
|
||||
};
|
||||
} // namespace
|
||||
|
||||
class DSDIFF::File::FilePrivate
|
||||
{
|
||||
public:
|
||||
FilePrivate(const 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 &properties)
|
||||
{
|
||||
d->tag.removeUnsupportedProperties(properties);
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
if(const ID3v2::Tag *id3v2Tag = ID3v2Tag(); (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
|
||||
|
||||
if(const DSDIFF::DIIN::Tag *diinTag = DIINTag(); (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());
|
||||
if(const int lastChunkIndex = static_cast<int>(d->chunks.size()) - 1;
|
||||
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)) {
|
||||
if(ChunkList &childChunksToUpdate = d->childChunks[PROPChunk];
|
||||
!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)) {
|
||||
if(ChunkList &childChunksToUpdate = d->childChunks[DIINChunk];
|
||||
!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;
|
||||
if(offset_t uPosNotPadded = tell(); (uPosNotPadded & 0x01) != 0) {
|
||||
if(ByteVector iByte = readBlock(1);
|
||||
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
|
||||
if(offset_t uPosNotPadded = tell(); (uPosNotPadded & 0x01) != 0) {
|
||||
if(ByteVector iByte = readBlock(1);
|
||||
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;
|
||||
if(offset_t uPosNotPadded = tell(); (uPosNotPadded & 0x01) != 0) {
|
||||
if(ByteVector iByte = readBlock(1);
|
||||
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;
|
||||
|
||||
if(offset_t uPosNotPadded = tell(); (uPosNotPadded & 0x01) != 0) {
|
||||
if(ByteVector iByte = readBlock(1);
|
||||
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);
|
||||
if(unsigned int titleStrLength = readBlock(4).toUInt(0, 4, bigEndian);
|
||||
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);
|
||||
if(unsigned int artistStrLength = readBlock(4).toUInt(0, 4, bigEndian);
|
||||
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);
|
||||
}
|
||||
290
taglib/dsdiff/dsdifffile.h
Normal file
290
taglib/dsdiff/dsdifffile.h
Normal file
@@ -0,0 +1,290 @@
|
||||
/***************************************************************************
|
||||
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 "tfile.h"
|
||||
#include "id3v2tag.h"
|
||||
#include "dsdiffproperties.h"
|
||||
#include "dsdiffdiintag.h"
|
||||
|
||||
namespace TagLib {
|
||||
|
||||
//! An implementation of DSDIFF metadata
|
||||
|
||||
/*!
|
||||
* This is an 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
|
||||
* <a href="https://dsd-guide.com/sites/default/files/white-papers/DSDIFF_1.5_Spec.pdf">
|
||||
* DSDIFF_1.5_Spec.pdf</a>.
|
||||
* The DSDIFF standard does not explicitly specify the ID3 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 \c 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 a DSDIFF file from \a stream. If \a readProperties is \c 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 \c 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 parameterized 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 \c 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 };
|
||||
|
||||
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 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 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 the 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
|
||||
100
taglib/dsdiff/dsdiffproperties.cpp
Normal file
100
taglib/dsdiff/dsdiffproperties.cpp
Normal file
@@ -0,0 +1,100 @@
|
||||
/***************************************************************************
|
||||
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"
|
||||
|
||||
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(unsigned int sampleRate,
|
||||
unsigned short channels,
|
||||
unsigned long long samplesCount,
|
||||
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>(static_cast<double>(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 a 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(unsigned int sampleRate, unsigned short channels,
|
||||
unsigned long long samplesCount, 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
|
||||
230
taglib/dsf/dsffile.cpp
Normal file
230
taglib/dsf/dsffile.cpp
Normal file
@@ -0,0 +1,230 @@
|
||||
/***************************************************************************
|
||||
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;
|
||||
|
||||
class DSF::File::FilePrivate
|
||||
{
|
||||
public:
|
||||
FilePrivate(const 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,
|
||||
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,
|
||||
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);
|
||||
}
|
||||
156
taglib/dsf/dsffile.h
Normal file
156
taglib/dsf/dsffile.h
Normal file
@@ -0,0 +1,156 @@
|
||||
/***************************************************************************
|
||||
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 {
|
||||
|
||||
//! An implementation of DSF metadata
|
||||
|
||||
/*!
|
||||
* This is an implementation of DSF metadata using an ID3v2 tag inside the
|
||||
* metadata chunk.
|
||||
* The DSF specification is located at
|
||||
* http://dsd-guide.com/sites/default/files/white-papers/DSFFileFormatSpec_E.pdf
|
||||
*/
|
||||
|
||||
namespace DSF {
|
||||
|
||||
//! An implementation of TagLib::File with DSF specific methods
|
||||
|
||||
/*!
|
||||
* This implements and provides an interface for DSF files to the
|
||||
* TagLib::Tag and TagLib::AudioProperties interfaces by way of implementing
|
||||
* the abstract TagLib::File API as well as providing some additional
|
||||
* information specific to DSF files.
|
||||
*/
|
||||
|
||||
class TAGLIB_EXPORT File : public TagLib::File {
|
||||
public:
|
||||
/*!
|
||||
* 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 \c 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>(static_cast<double>(d->sampleCount) * 1000.0 / d->samplingFrequency + 0.5)
|
||||
: 0;
|
||||
}
|
||||
73
taglib/dsf/dsfproperties.h
Normal file
73
taglib/dsf/dsfproperties.h
Normal file
@@ -0,0 +1,73 @@
|
||||
/***************************************************************************
|
||||
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 {
|
||||
//! An implementation of audio properties for 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,94 +27,416 @@
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <tfile.h>
|
||||
#include <tstring.h>
|
||||
#include <tdebug.h>
|
||||
|
||||
#include "fileref.h"
|
||||
#include "asffile.h"
|
||||
|
||||
#include <cstring>
|
||||
#include <utility>
|
||||
|
||||
#include "taglib_config.h"
|
||||
#include "tfilestream.h"
|
||||
#include "tpropertymap.h"
|
||||
#include "tstringlist.h"
|
||||
#include "tvariant.h"
|
||||
#include "tdebug.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 "trueaudiofile.h"
|
||||
#ifdef TAGLIB_WITH_RIFF
|
||||
#include "aifffile.h"
|
||||
#include "wavfile.h"
|
||||
#endif
|
||||
#ifdef TAGLIB_WITH_APE
|
||||
#include "apefile.h"
|
||||
#include "modfile.h"
|
||||
#include "s3mfile.h"
|
||||
#include "mpcfile.h"
|
||||
#include "wavpackfile.h"
|
||||
#endif
|
||||
#ifdef TAGLIB_WITH_ASF
|
||||
#include "asffile.h"
|
||||
#endif
|
||||
#ifdef TAGLIB_WITH_VORBIS
|
||||
#include "flacfile.h"
|
||||
#include "speexfile.h"
|
||||
#include "vorbisfile.h"
|
||||
#include "oggflacfile.h"
|
||||
#include "opusfile.h"
|
||||
#endif
|
||||
#ifdef TAGLIB_WITH_MOD
|
||||
#include "itfile.h"
|
||||
#include "modfile.h"
|
||||
#include "xmfile.h"
|
||||
#include "s3mfile.h"
|
||||
#endif
|
||||
#ifdef TAGLIB_WITH_MP4
|
||||
#include "mp4file.h"
|
||||
#endif
|
||||
#ifdef TAGLIB_WITH_TRUEAUDIO
|
||||
#include "trueaudiofile.h"
|
||||
#endif
|
||||
#ifdef TAGLIB_WITH_DSF
|
||||
#include "dsffile.h"
|
||||
#include "dsdifffile.h"
|
||||
#endif
|
||||
#ifdef TAGLIB_WITH_SHORTEN
|
||||
#include "shortenfile.h"
|
||||
#endif
|
||||
#ifdef TAGLIB_WITH_MATROSKA
|
||||
#include "matroskafile.h"
|
||||
#endif
|
||||
|
||||
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;
|
||||
if(const int pos = s.rfind("."); 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);
|
||||
#ifdef TAGLIB_WITH_VORBIS
|
||||
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 == "SPX")
|
||||
file = new Ogg::Speex::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
else if(ext == "OPUS")
|
||||
file = new Ogg::Opus::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
#endif
|
||||
#ifdef TAGLIB_WITH_APE
|
||||
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 == "APE")
|
||||
file = new APE::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
#endif
|
||||
#ifdef TAGLIB_WITH_TRUEAUDIO
|
||||
else if(ext == "TTA")
|
||||
file = new TrueAudio::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
#endif
|
||||
#ifdef TAGLIB_WITH_MP4
|
||||
else if(ext == "M4A" || ext == "M4R" || ext == "M4B" || ext == "M4P" || ext == "MP4" || ext == "3G2" || ext == "M4V")
|
||||
file = new MP4::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
#endif
|
||||
#ifdef TAGLIB_WITH_ASF
|
||||
else if(ext == "WMA" || ext == "ASF")
|
||||
file = new ASF::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
#endif
|
||||
#ifdef TAGLIB_WITH_RIFF
|
||||
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);
|
||||
#endif
|
||||
#ifdef TAGLIB_WITH_MOD
|
||||
// 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);
|
||||
#endif
|
||||
#ifdef TAGLIB_WITH_DSF
|
||||
else if(ext == "DSF")
|
||||
file = new DSF::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
else if(ext == "DFF" || ext == "DSDIFF")
|
||||
file = new DSDIFF::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
#endif
|
||||
#ifdef TAGLIB_WITH_SHORTEN
|
||||
else if(ext == "SHN")
|
||||
file = new Shorten::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
#endif
|
||||
#ifdef TAGLIB_WITH_MATROSKA
|
||||
else if(ext == "MKA" || ext == "MKV" || ext == "WEBM")
|
||||
file = new Matroska::File(stream, readAudioProperties);
|
||||
#endif
|
||||
|
||||
// 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);
|
||||
#ifdef TAGLIB_WITH_VORBIS
|
||||
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(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);
|
||||
#endif
|
||||
#ifdef TAGLIB_WITH_APE
|
||||
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(APE::File::isSupported(stream))
|
||||
file = new APE::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
#endif
|
||||
#ifdef TAGLIB_WITH_TRUEAUDIO
|
||||
else if(TrueAudio::File::isSupported(stream))
|
||||
file = new TrueAudio::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
#endif
|
||||
#ifdef TAGLIB_WITH_MP4
|
||||
else if(MP4::File::isSupported(stream))
|
||||
file = new MP4::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
#endif
|
||||
#ifdef TAGLIB_WITH_ASF
|
||||
else if(ASF::File::isSupported(stream))
|
||||
file = new ASF::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
#endif
|
||||
#ifdef TAGLIB_WITH_RIFF
|
||||
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);
|
||||
#endif
|
||||
#ifdef TAGLIB_WITH_DSF
|
||||
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);
|
||||
#endif
|
||||
#ifdef TAGLIB_WITH_SHORTEN
|
||||
else if(Shorten::File::isSupported(stream))
|
||||
file = new Shorten::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
#endif
|
||||
#ifdef TAGLIB_WITH_MATROSKA
|
||||
else if(Matroska::File::isSupported(stream))
|
||||
file = new Matroska::File(stream, readAudioProperties, audioPropertiesStyle);
|
||||
#endif
|
||||
|
||||
// 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([[maybe_unused]] 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();
|
||||
}
|
||||
@@ -126,8 +448,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();
|
||||
@@ -135,34 +456,58 @@ 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;
|
||||
|
||||
l.append("mp3");
|
||||
l.append("mp2");
|
||||
l.append("aac");
|
||||
#ifdef TAGLIB_WITH_VORBIS
|
||||
l.append("ogg");
|
||||
l.append("flac");
|
||||
l.append("oga");
|
||||
l.append("mp3");
|
||||
l.append("opus");
|
||||
l.append("spx");
|
||||
#endif
|
||||
#ifdef TAGLIB_WITH_APE
|
||||
l.append("mpc");
|
||||
l.append("wv");
|
||||
l.append("spx");
|
||||
l.append("ape");
|
||||
#endif
|
||||
#ifdef TAGLIB_WITH_TRUEAUDIO
|
||||
l.append("tta");
|
||||
#endif
|
||||
#ifdef TAGLIB_WITH_MP4
|
||||
l.append("m4a");
|
||||
l.append("m4r");
|
||||
l.append("m4b");
|
||||
l.append("m4p");
|
||||
l.append("3g2");
|
||||
l.append("mp4");
|
||||
l.append("m4v");
|
||||
#endif
|
||||
#ifdef TAGLIB_WITH_ASF
|
||||
l.append("wma");
|
||||
l.append("asf");
|
||||
#endif
|
||||
#ifdef TAGLIB_WITH_RIFF
|
||||
l.append("aif");
|
||||
l.append("aiff");
|
||||
l.append("afc");
|
||||
l.append("aifc");
|
||||
l.append("wav");
|
||||
l.append("ape");
|
||||
#endif
|
||||
#ifdef TAGLIB_WITH_MOD
|
||||
l.append("mod");
|
||||
l.append("module"); // alias for "mod"
|
||||
l.append("nst"); // alias for "mod"
|
||||
@@ -170,27 +515,36 @@ StringList FileRef::defaultFileExtensions()
|
||||
l.append("s3m");
|
||||
l.append("it");
|
||||
l.append("xm");
|
||||
#endif
|
||||
#ifdef TAGLIB_WITH_DSF
|
||||
l.append("dsf");
|
||||
l.append("dff");
|
||||
l.append("dsdiff"); // alias for "dff"
|
||||
#endif
|
||||
#ifdef TAGLIB_WITH_SHORTEN
|
||||
l.append("shn");
|
||||
#endif
|
||||
#ifdef TAGLIB_WITH_MATROSKA
|
||||
l.append("mkv");
|
||||
l.append("mka");
|
||||
l.append("webm");
|
||||
#endif
|
||||
|
||||
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
|
||||
@@ -203,77 +557,66 @@ bool FileRef::operator!=(const FileRef &ref) const
|
||||
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 s;
|
||||
// At last, try to resolve file types based on the actual content.
|
||||
|
||||
#ifdef _WIN32
|
||||
s = (wcslen((const wchar_t *) fileName) > 0) ? String((const wchar_t *) fileName) : String((const char *) fileName);
|
||||
#else
|
||||
s = fileName;
|
||||
#endif
|
||||
d->file = detectByContent(d->stream, readAudioProperties, audioPropertiesStyle);
|
||||
if(d->file)
|
||||
return;
|
||||
|
||||
// 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.
|
||||
// Stream have to be closed here if failed to resolve file types.
|
||||
|
||||
int pos = s.rfind(".");
|
||||
if(pos != -1) {
|
||||
String ext = s.substr(pos + 1).upper();
|
||||
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 == "TTA")
|
||||
return new TrueAudio::File(fileName, readAudioProperties, audioPropertiesStyle);
|
||||
if(ext == "M4A" || ext == "M4R" || ext == "M4B" || ext == "M4P" || ext == "MP4" || ext == "3G2")
|
||||
return new MP4::File(fileName, readAudioProperties, audioPropertiesStyle);
|
||||
if(ext == "WMA" || ext == "ASF")
|
||||
return new ASF::File(fileName, readAudioProperties, audioPropertiesStyle);
|
||||
if(ext == "AIF" || ext == "AIFF")
|
||||
return new RIFF::AIFF::File(fileName, readAudioProperties, audioPropertiesStyle);
|
||||
if(ext == "WAV")
|
||||
return new RIFF::WAV::File(fileName, readAudioProperties, audioPropertiesStyle);
|
||||
if(ext == "APE")
|
||||
return new APE::File(fileName, readAudioProperties, audioPropertiesStyle);
|
||||
// module, nst and wow are possible but uncommon extensions
|
||||
if(ext == "MOD" || ext == "MODULE" || ext == "NST" || ext == "WOW")
|
||||
return new Mod::File(fileName, readAudioProperties, audioPropertiesStyle);
|
||||
if(ext == "S3M")
|
||||
return new S3M::File(fileName, readAudioProperties, audioPropertiesStyle);
|
||||
if(ext == "IT")
|
||||
return new IT::File(fileName, readAudioProperties, audioPropertiesStyle);
|
||||
if(ext == "XM")
|
||||
return new XM::File(fileName, readAudioProperties, audioPropertiesStyle);
|
||||
}
|
||||
|
||||
return 0;
|
||||
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;
|
||||
|
||||
274
taglib/fileref.h
274
taglib/fileref.h
@@ -60,43 +60,29 @@ namespace TagLib {
|
||||
{
|
||||
public:
|
||||
|
||||
//! A class for pluggable file type resolution.
|
||||
//! A class for pluggable file type resolution.
|
||||
|
||||
/*!
|
||||
* This class is used to add extend TagLib's very basic file name based file
|
||||
* type resolution.
|
||||
*
|
||||
* This can be accomplished with:
|
||||
*
|
||||
* \code
|
||||
*
|
||||
* class MyFileTypeResolver : FileTypeResolver
|
||||
* {
|
||||
* TagLib::File *createFile(TagLib::FileName *fileName, bool, AudioProperties::ReadStyle)
|
||||
* {
|
||||
* if(someCheckForAnMP3File(fileName))
|
||||
* return new TagLib::MPEG::File(fileName);
|
||||
* return 0;
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* FileRef::addFileTypeResolver(new MyFileTypeResolver);
|
||||
*
|
||||
* \endcode
|
||||
*
|
||||
* Naturally a less contrived example would be slightly more complex. This
|
||||
* can be used to plug in mime-type detection systems or to add new file types
|
||||
* to TagLib.
|
||||
*/
|
||||
/*!
|
||||
* %File type resolver, better implement StreamTypeResolver in order to
|
||||
* support both file and stream resolution.
|
||||
*/
|
||||
|
||||
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 +92,81 @@ 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;
|
||||
};
|
||||
|
||||
//! A class for pluggable stream type resolution.
|
||||
|
||||
/*!
|
||||
* This class is used to extend TagLib's very basic file name based file
|
||||
* type resolution.
|
||||
*
|
||||
* This can be accomplished with:
|
||||
*
|
||||
* \code
|
||||
*
|
||||
* class MyStreamTypeResolver : StreamTypeResolver
|
||||
* {
|
||||
* TagLib::File *createFile(TagLib::FileName *fileName, bool readProps,
|
||||
* AudioProperties::ReadStyle readStyle) const override
|
||||
* {
|
||||
* if(someCheckForAnMP3File(fileName))
|
||||
* return new TagLib::MPEG::File(fileName, readProps, readStyle);
|
||||
* return nullptr;
|
||||
* }
|
||||
*
|
||||
* TagLib::File *createFileFromStream(TagLib::IOStream *s, bool readProps,
|
||||
* AudioProperties::ReadStyle readStyle) const override
|
||||
* {
|
||||
* if(someCheckForAnMP3Stream(s))
|
||||
* return new TagLib::MPEG::File(s, readProps, readStyle);
|
||||
* return nullptr;
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* FileRef::addFileTypeResolver(new MyStreamTypeResolver);
|
||||
*
|
||||
* \endcode
|
||||
*
|
||||
* Naturally a less contrived example would be slightly more complex. This
|
||||
* can be used to plug in mime-type detection systems or to add new file types
|
||||
* to TagLib.
|
||||
*/
|
||||
|
||||
class TAGLIB_EXPORT StreamTypeResolver : public FileTypeResolver
|
||||
{
|
||||
public:
|
||||
StreamTypeResolver();
|
||||
/*!
|
||||
* Destroys this StreamTypeResolver instance.
|
||||
*/
|
||||
virtual ~StreamTypeResolver() override = 0; // virtual is needed by SWIG
|
||||
|
||||
StreamTypeResolver(const StreamTypeResolver &) = delete;
|
||||
StreamTypeResolver &operator=(const StreamTypeResolver &) = delete;
|
||||
|
||||
/*!
|
||||
* This method must be overridden to provide an additional stream type
|
||||
* resolver. If the resolver is able to determine the file type it should
|
||||
* 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
|
||||
* out of scope.
|
||||
*
|
||||
* \see createFile()
|
||||
*/
|
||||
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;
|
||||
};
|
||||
|
||||
/*!
|
||||
@@ -114,9 +175,9 @@ namespace TagLib {
|
||||
FileRef();
|
||||
|
||||
/*!
|
||||
* Create a FileRef from \a fileName. If \a readAudioProperties is true then
|
||||
* Create a FileRef from \a fileName. If \a readAudioProperties is \c true then
|
||||
* the audio properties will be read using \a audioPropertiesStyle. If
|
||||
* \a readAudioProperties is false then \a audioPropertiesStyle will be
|
||||
* \a readAudioProperties is \c false then \a audioPropertiesStyle will be
|
||||
* ignored.
|
||||
*
|
||||
* Also see the note in the class documentation about why you may not want to
|
||||
@@ -128,7 +189,24 @@ namespace TagLib {
|
||||
audioPropertiesStyle = AudioProperties::Average);
|
||||
|
||||
/*!
|
||||
* Contruct a FileRef using \a file. The FileRef now takes ownership of the
|
||||
* Construct a FileRef from an opened \a IOStream. If \a readAudioProperties
|
||||
* is \c true then the audio properties will be read using \a audioPropertiesStyle.
|
||||
* If \a readAudioProperties is \c false then \a audioPropertiesStyle will be
|
||||
* ignored.
|
||||
*
|
||||
* Also see the note in the class documentation about why you may not want to
|
||||
* use this method in your application.
|
||||
*
|
||||
* \note TagLib will *not* take ownership of the stream, the caller is
|
||||
* responsible for deleting it after the File object.
|
||||
*/
|
||||
explicit FileRef(IOStream* stream,
|
||||
bool readAudioProperties = true,
|
||||
AudioProperties::ReadStyle
|
||||
audioPropertiesStyle = AudioProperties::Average);
|
||||
|
||||
/*!
|
||||
* Construct a FileRef using \a file. The FileRef now takes ownership of the
|
||||
* pointer and will delete the File when it passes out of scope.
|
||||
*/
|
||||
explicit FileRef(File *file);
|
||||
@@ -141,24 +219,102 @@ namespace TagLib {
|
||||
/*!
|
||||
* Destroys this FileRef instance.
|
||||
*/
|
||||
virtual ~FileRef();
|
||||
~FileRef();
|
||||
|
||||
/*!
|
||||
* Returns a pointer to represented file's tag.
|
||||
* Returns a pointer to the represented file's tag.
|
||||
*
|
||||
* \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.
|
||||
* were read then this will return a null pointer.
|
||||
*/
|
||||
AudioProperties *audioProperties() const;
|
||||
|
||||
@@ -180,7 +336,7 @@ namespace TagLib {
|
||||
File *file() const;
|
||||
|
||||
/*!
|
||||
* Saves the file. Returns true on success.
|
||||
* Saves the file. Returns \c true on success.
|
||||
*/
|
||||
bool save();
|
||||
|
||||
@@ -191,13 +347,18 @@ namespace TagLib {
|
||||
* is tried.
|
||||
*
|
||||
* Returns a pointer to the added resolver (the same one that's passed in --
|
||||
* this is mostly so that static inialializers have something to use for
|
||||
* this is mostly so that static initializers have something to use for
|
||||
* assignment).
|
||||
*
|
||||
* \see FileTypeResolver
|
||||
*/
|
||||
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
|
||||
@@ -209,7 +370,7 @@ namespace TagLib {
|
||||
* by TagLib for resolution is case-insensitive.
|
||||
*
|
||||
* \note This does not account for any additional file type resolvers that
|
||||
* are plugged in. Also note that this is not intended to replace a propper
|
||||
* are plugged in. Also note that this is not intended to replace a proper
|
||||
* mime-type resolution system, but is just here for reference.
|
||||
*
|
||||
* \see FileTypeResolver
|
||||
@@ -217,7 +378,7 @@ namespace TagLib {
|
||||
static StringList defaultFileExtensions();
|
||||
|
||||
/*!
|
||||
* Returns true if the file (and as such other pointers) are null.
|
||||
* Returns \c true if the file (and as such other pointers) are null.
|
||||
*/
|
||||
bool isNull() const;
|
||||
|
||||
@@ -227,35 +388,28 @@ namespace TagLib {
|
||||
FileRef &operator=(const FileRef &ref);
|
||||
|
||||
/*!
|
||||
* Returns true if this FileRef and \a ref point to the same File object.
|
||||
* Exchanges the content of the FileRef with the content of \a ref.
|
||||
*/
|
||||
void swap(FileRef &ref) noexcept;
|
||||
|
||||
/*!
|
||||
* Returns \c true if this FileRef and \a ref point to the same File object.
|
||||
*/
|
||||
bool operator==(const FileRef &ref) const;
|
||||
|
||||
/*!
|
||||
* Returns true if this FileRef and \a ref do not point to the same File
|
||||
* Returns \c true if this FileRef and \a ref do not point to the same File
|
||||
* object.
|
||||
*/
|
||||
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,94 +44,96 @@ using namespace TagLib;
|
||||
namespace
|
||||
{
|
||||
enum { FlacXiphIndex = 0, FlacID3v2Index = 1, FlacID3v1Index = 2 };
|
||||
enum { MinPaddingLength = 4096 };
|
||||
enum { LastBlockFlag = 0x80 };
|
||||
}
|
||||
|
||||
constexpr long MinPaddingLength = 4096;
|
||||
constexpr long MaxPaddingLegnth = 1024 * 1024;
|
||||
|
||||
constexpr 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),
|
||||
streamLength(0),
|
||||
scanned(false),
|
||||
hasXiphComment(false),
|
||||
hasID3v2(false),
|
||||
hasID3v1(false)
|
||||
FilePrivate(const ID3v2::FrameFactory *frameFactory = ID3v2::FrameFactory::instance()) :
|
||||
ID3v2FrameFactory(frameFactory)
|
||||
{
|
||||
}
|
||||
|
||||
~FilePrivate()
|
||||
{
|
||||
for(uint i = 0; i < blocks.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;
|
||||
ByteVector streamInfoData;
|
||||
std::unique_ptr<Properties> properties;
|
||||
ByteVector xiphCommentData;
|
||||
List<MetadataBlock *> blocks;
|
||||
List<FLAC::MetadataBlock *> blocks;
|
||||
|
||||
long flacStart;
|
||||
long streamStart;
|
||||
long streamLength;
|
||||
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 propertiesStyle) :
|
||||
TagLib::File(file)
|
||||
Properties::ReadStyle,
|
||||
ID3v2::FrameFactory *frameFactory) :
|
||||
TagLib::File(file),
|
||||
d(std::make_unique<FilePrivate>(
|
||||
frameFactory ? frameFactory : ID3v2::FrameFactory::instance()))
|
||||
{
|
||||
d = new FilePrivate;
|
||||
read(readProperties, propertiesStyle);
|
||||
if(isOpen())
|
||||
read(readProperties);
|
||||
}
|
||||
|
||||
FLAC::File::File(FileName file, ID3v2::FrameFactory *frameFactory,
|
||||
bool readProperties, Properties::ReadStyle propertiesStyle) :
|
||||
TagLib::File(file)
|
||||
bool readProperties, Properties::ReadStyle) :
|
||||
TagLib::File(file),
|
||||
d(std::make_unique<FilePrivate>(frameFactory))
|
||||
{
|
||||
d = new FilePrivate;
|
||||
d->ID3v2FrameFactory = frameFactory;
|
||||
read(readProperties, propertiesStyle);
|
||||
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()))
|
||||
{
|
||||
if(isOpen())
|
||||
read(readProperties);
|
||||
}
|
||||
|
||||
FLAC::File::File(IOStream *stream, ID3v2::FrameFactory *frameFactory,
|
||||
bool readProperties, Properties::ReadStyle propertiesStyle) :
|
||||
TagLib::File(stream)
|
||||
bool readProperties, Properties::ReadStyle) :
|
||||
TagLib::File(stream),
|
||||
d(std::make_unique<FilePrivate>(frameFactory))
|
||||
{
|
||||
d = new FilePrivate;
|
||||
d->ID3v2FrameFactory = frameFactory;
|
||||
read(readProperties, propertiesStyle);
|
||||
if(isOpen())
|
||||
read(readProperties);
|
||||
}
|
||||
|
||||
FLAC::File::~File()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
FLAC::File::~File() = default;
|
||||
|
||||
TagLib::Tag *FLAC::File::tag() const
|
||||
{
|
||||
@@ -141,45 +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)
|
||||
{
|
||||
if(d->hasXiphComment)
|
||||
return d->tag.access<Ogg::XiphComment>(FlacXiphIndex, false)->setProperties(properties);
|
||||
else if(d->hasID3v2)
|
||||
return d->tag.access<ID3v2::Tag>(FlacID3v2Index, false)->setProperties(properties);
|
||||
else if(d->hasID3v1)
|
||||
return d->tag.access<ID3v1::Tag>(FlacID3v1Index, false)->setProperties(properties);
|
||||
else
|
||||
return d->tag.access<Ogg::XiphComment>(FlacXiphIndex, true)->setProperties(properties);
|
||||
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
|
||||
{
|
||||
if(const String uppercaseKey = key.upper(); 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)
|
||||
{
|
||||
if(const String uppercaseKey = key.upper(); 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()
|
||||
{
|
||||
if(readOnly()) {
|
||||
@@ -193,93 +236,152 @@ 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];
|
||||
ByteVector blockData = block->render();
|
||||
for(auto it = d->blocks.begin(); it != d->blocks.end();) {
|
||||
ByteVector blockData = (*it)->render();
|
||||
ByteVector blockHeader = ByteVector::fromUInt(blockData.size());
|
||||
blockHeader[0] = block->code();
|
||||
if(blockHeader[0] != 0) {
|
||||
debug("FLAC::File::save() -- Removing too large block.");
|
||||
delete *it;
|
||||
it = d->blocks.erase(it);
|
||||
continue;
|
||||
}
|
||||
blockHeader[0] = static_cast<char>((*it)->code());
|
||||
data.append(blockHeader);
|
||||
data.append(blockData);
|
||||
++it;
|
||||
}
|
||||
|
||||
// Adjust the padding block(s)
|
||||
// Compute the amount of padding, and append that to data.
|
||||
|
||||
long originalLength = d->streamStart - d->flacStart;
|
||||
int paddingLength = originalLength - data.size() - 4;
|
||||
if (paddingLength < 0) {
|
||||
offset_t originalLength = d->streamStart - d->flacStart;
|
||||
offset_t paddingLength = originalLength - data.size() - 4;
|
||||
|
||||
if(paddingLength <= 0) {
|
||||
paddingLength = MinPaddingLength;
|
||||
}
|
||||
ByteVector padding = ByteVector::fromUInt(paddingLength);
|
||||
padding.resize(paddingLength + 4);
|
||||
padding[0] = 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()) {
|
||||
seek(-128, End);
|
||||
if(ID3v1Tag() && !ID3v1Tag()->isEmpty()) {
|
||||
|
||||
// ID3v1 tag is not empty. Update the old one or create a new one.
|
||||
|
||||
if(d->ID3v1Location >= 0) {
|
||||
seek(d->ID3v1Location);
|
||||
}
|
||||
else {
|
||||
seek(0, End);
|
||||
d->ID3v1Location = tell();
|
||||
}
|
||||
|
||||
writeBlock(ID3v1Tag()->render());
|
||||
}
|
||||
else {
|
||||
|
||||
// ID3v1 tag is empty. Remove the old one.
|
||||
|
||||
if(d->ID3v1Location >= 0) {
|
||||
truncate(d->ID3v1Location);
|
||||
d->ID3v1Location = -1;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
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,232 +394,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;
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// private members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void FLAC::File::read(bool readProperties, Properties::ReadStyle propertiesStyle)
|
||||
{
|
||||
// Look for an ID3v2 tag
|
||||
|
||||
d->ID3v2Location = findID3v2();
|
||||
|
||||
if(d->ID3v2Location >= 0) {
|
||||
|
||||
d->tag.set(FlacID3v2Index, new ID3v2::Tag(this, d->ID3v2Location, d->ID3v2FrameFactory));
|
||||
|
||||
d->ID3v2OriginalSize = ID3v2Tag()->header()->completeTagSize();
|
||||
|
||||
if(ID3v2Tag()->header()->tagSize() <= 0)
|
||||
d->tag.set(FlacID3v2Index, 0);
|
||||
else
|
||||
d->hasID3v2 = true;
|
||||
}
|
||||
|
||||
// Look for an ID3v1 tag
|
||||
|
||||
d->ID3v1Location = findID3v1();
|
||||
|
||||
if(d->ID3v1Location >= 0) {
|
||||
d->tag.set(FlacID3v1Index, new ID3v1::Tag(this, d->ID3v1Location));
|
||||
d->hasID3v1 = true;
|
||||
}
|
||||
|
||||
// Look for FLAC metadata, including vorbis comments
|
||||
|
||||
scan();
|
||||
|
||||
if(!isValid())
|
||||
return;
|
||||
|
||||
if(d->hasXiphComment)
|
||||
d->tag.set(FlacXiphIndex, new Ogg::XiphComment(xiphCommentData()));
|
||||
else
|
||||
d->tag.set(FlacXiphIndex, new Ogg::XiphComment);
|
||||
|
||||
if(readProperties)
|
||||
d->properties = new Properties(streamInfoData(), streamLength(), propertiesStyle);
|
||||
}
|
||||
|
||||
ByteVector FLAC::File::streamInfoData()
|
||||
{
|
||||
return isValid() ? d->streamInfoData : ByteVector();
|
||||
}
|
||||
|
||||
ByteVector FLAC::File::xiphCommentData() const
|
||||
{
|
||||
return (isValid() && d->hasXiphComment) ? d->xiphCommentData : ByteVector();
|
||||
}
|
||||
|
||||
long FLAC::File::streamLength()
|
||||
{
|
||||
return d->streamLength;
|
||||
}
|
||||
|
||||
void FLAC::File::scan()
|
||||
{
|
||||
// Scan the metadata pages
|
||||
|
||||
if(d->scanned)
|
||||
return;
|
||||
|
||||
if(!isValid())
|
||||
return;
|
||||
|
||||
long nextBlockOffset;
|
||||
|
||||
if(d->hasID3v2)
|
||||
nextBlockOffset = find("fLaC", d->ID3v2Location + d->ID3v2OriginalSize);
|
||||
else
|
||||
nextBlockOffset = find("fLaC");
|
||||
|
||||
if(nextBlockOffset < 0) {
|
||||
debug("FLAC::File::scan() -- FLAC stream not found");
|
||||
setValid(false);
|
||||
return;
|
||||
}
|
||||
|
||||
nextBlockOffset += 4;
|
||||
d->flacStart = nextBlockOffset;
|
||||
|
||||
seek(nextBlockOffset);
|
||||
|
||||
ByteVector header = readBlock(4);
|
||||
|
||||
// Header format (from spec):
|
||||
// <1> Last-metadata-block flag
|
||||
// <7> BLOCK_TYPE
|
||||
// 0 : STREAMINFO
|
||||
// 1 : PADDING
|
||||
// ..
|
||||
// 4 : VORBIS_COMMENT
|
||||
// ..
|
||||
// <24> Length of metadata to follow
|
||||
|
||||
char blockType = header[0] & 0x7f;
|
||||
bool isLastBlock = (header[0] & 0x80) != 0;
|
||||
uint length = header.mid(1, 3).toUInt();
|
||||
|
||||
// First block should be the stream_info metadata
|
||||
|
||||
if(blockType != MetadataBlock::StreamInfo) {
|
||||
debug("FLAC::File::scan() -- invalid FLAC stream");
|
||||
setValid(false);
|
||||
return;
|
||||
}
|
||||
|
||||
d->streamInfoData = readBlock(length);
|
||||
d->blocks.append(new UnknownMetadataBlock(blockType, d->streamInfoData));
|
||||
nextBlockOffset += length + 4;
|
||||
|
||||
// Search through the remaining metadata
|
||||
while(!isLastBlock) {
|
||||
|
||||
header = readBlock(4);
|
||||
blockType = header[0] & 0x7f;
|
||||
isLastBlock = (header[0] & 0x80) != 0;
|
||||
length = header.mid(1, 3).toUInt();
|
||||
|
||||
ByteVector data = readBlock(length);
|
||||
if(data.size() != length) {
|
||||
debug("FLAC::File::scan() -- FLAC stream corrupted");
|
||||
setValid(false);
|
||||
return;
|
||||
}
|
||||
|
||||
MetadataBlock *block = 0;
|
||||
|
||||
// Found the vorbis-comment
|
||||
if(blockType == MetadataBlock::VorbisComment) {
|
||||
if(!d->hasXiphComment) {
|
||||
d->xiphCommentData = data;
|
||||
d->hasXiphComment = true;
|
||||
}
|
||||
else {
|
||||
debug("FLAC::File::scan() -- multiple Vorbis Comment blocks found, using the first one");
|
||||
}
|
||||
}
|
||||
else if(blockType == MetadataBlock::Picture) {
|
||||
FLAC::Picture *picture = new FLAC::Picture();
|
||||
if(picture->parse(data)) {
|
||||
block = picture;
|
||||
}
|
||||
else {
|
||||
debug("FLAC::File::scan() -- invalid picture found, discarting");
|
||||
delete picture;
|
||||
}
|
||||
}
|
||||
|
||||
if(!block) {
|
||||
block = new UnknownMetadataBlock(blockType, data);
|
||||
}
|
||||
if(block->code() != MetadataBlock::Padding) {
|
||||
d->blocks.append(block);
|
||||
}
|
||||
else {
|
||||
delete block;
|
||||
}
|
||||
|
||||
nextBlockOffset += length + 4;
|
||||
|
||||
if(nextBlockOffset >= File::length()) {
|
||||
debug("FLAC::File::scan() -- FLAC stream corrupted");
|
||||
setValid(false);
|
||||
return;
|
||||
}
|
||||
seek(nextBlockOffset);
|
||||
}
|
||||
|
||||
// End of metadata, now comes the datastream
|
||||
|
||||
d->streamStart = nextBlockOffset;
|
||||
d->streamLength = File::length() - d->streamStart;
|
||||
|
||||
if(d->hasID3v1)
|
||||
d->streamLength -= 128;
|
||||
|
||||
d->scanned = true;
|
||||
}
|
||||
|
||||
long FLAC::File::findID3v1()
|
||||
{
|
||||
if(!isValid())
|
||||
return -1;
|
||||
|
||||
seek(-128, End);
|
||||
long p = tell();
|
||||
|
||||
if(readBlock(3) == ID3v1::Tag::fileIdentifier())
|
||||
return p;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
long FLAC::File::findID3v2()
|
||||
{
|
||||
if(!isValid())
|
||||
return -1;
|
||||
|
||||
seek(0);
|
||||
|
||||
if(readBlock(3) == ID3v2::Header::fileIdentifier())
|
||||
return 0;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -531,8 +412,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);
|
||||
|
||||
@@ -542,16 +422,213 @@ 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->xiphCommentData.isEmpty();
|
||||
}
|
||||
|
||||
bool FLAC::File::hasID3v1Tag() const
|
||||
{
|
||||
return d->ID3v1Location >= 0;
|
||||
}
|
||||
|
||||
bool FLAC::File::hasID3v2Tag() const
|
||||
{
|
||||
return d->ID3v2Location >= 0;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// private members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void FLAC::File::read(bool readProperties)
|
||||
{
|
||||
// Look for an ID3v2 tag
|
||||
|
||||
d->ID3v2Location = Utils::findID3v2(this);
|
||||
|
||||
if(d->ID3v2Location >= 0) {
|
||||
d->tag.set(FlacID3v2Index, new ID3v2::Tag(this, d->ID3v2Location, d->ID3v2FrameFactory));
|
||||
d->ID3v2OriginalSize = ID3v2Tag()->header()->completeTagSize();
|
||||
}
|
||||
|
||||
// Look for an ID3v1 tag
|
||||
|
||||
d->ID3v1Location = Utils::findID3v1(this);
|
||||
|
||||
if(d->ID3v1Location >= 0)
|
||||
d->tag.set(FlacID3v1Index, new ID3v1::Tag(this, d->ID3v1Location));
|
||||
|
||||
// Look for FLAC metadata, including vorbis comments
|
||||
|
||||
scan();
|
||||
|
||||
if(!isValid())
|
||||
return;
|
||||
|
||||
if(!d->xiphCommentData.isEmpty())
|
||||
d->tag.set(FlacXiphIndex, new Ogg::XiphComment(d->xiphCommentData));
|
||||
else
|
||||
d->tag.set(FlacXiphIndex, new Ogg::XiphComment());
|
||||
|
||||
if(readProperties) {
|
||||
|
||||
// First block should be the stream_info metadata
|
||||
|
||||
const ByteVector infoData = d->blocks.front()->render();
|
||||
|
||||
offset_t streamLength;
|
||||
|
||||
if(d->ID3v1Location >= 0)
|
||||
streamLength = d->ID3v1Location - d->streamStart;
|
||||
else
|
||||
streamLength = length() - d->streamStart;
|
||||
|
||||
d->properties = std::make_unique<Properties>(infoData, streamLength);
|
||||
}
|
||||
}
|
||||
|
||||
void FLAC::File::scan()
|
||||
{
|
||||
// Scan the metadata pages
|
||||
|
||||
if(d->scanned)
|
||||
return;
|
||||
|
||||
if(!isValid())
|
||||
return;
|
||||
|
||||
offset_t nextBlockOffset;
|
||||
|
||||
if(d->ID3v2Location >= 0)
|
||||
nextBlockOffset = find("fLaC", d->ID3v2Location + d->ID3v2OriginalSize);
|
||||
else
|
||||
nextBlockOffset = find("fLaC");
|
||||
|
||||
if(nextBlockOffset < 0) {
|
||||
debug("FLAC::File::scan() -- FLAC stream not found");
|
||||
setValid(false);
|
||||
return;
|
||||
}
|
||||
|
||||
nextBlockOffset += 4;
|
||||
d->flacStart = nextBlockOffset;
|
||||
|
||||
while(true) {
|
||||
|
||||
seek(nextBlockOffset);
|
||||
const ByteVector header = readBlock(4);
|
||||
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
|
||||
// ..
|
||||
// 6 : PICTURE
|
||||
// ..
|
||||
// <24> Length of metadata to follow
|
||||
|
||||
const char blockType = header[0] & ~LastBlockFlag;
|
||||
const bool isLastBlock = (header[0] & LastBlockFlag) != 0;
|
||||
const unsigned int blockLength = header.toUInt(1U, 3U);
|
||||
|
||||
// First block should be the stream_info metadata
|
||||
|
||||
if(d->blocks.isEmpty() && blockType != MetadataBlock::StreamInfo) {
|
||||
debug("FLAC::File::scan() -- First block should be the stream_info metadata");
|
||||
setValid(false);
|
||||
return;
|
||||
}
|
||||
|
||||
if(blockLength == 0
|
||||
&& blockType != MetadataBlock::Padding && blockType != MetadataBlock::SeekTable)
|
||||
{
|
||||
debug("FLAC::File::scan() -- Zero-sized metadata block found");
|
||||
setValid(false);
|
||||
return;
|
||||
}
|
||||
|
||||
const ByteVector data = readBlock(blockLength);
|
||||
if(data.size() != blockLength) {
|
||||
debug("FLAC::File::scan() -- Failed to read a metadata block");
|
||||
setValid(false);
|
||||
return;
|
||||
}
|
||||
|
||||
MetadataBlock *block = nullptr;
|
||||
|
||||
// Found the vorbis-comment
|
||||
if(blockType == MetadataBlock::VorbisComment) {
|
||||
if(d->xiphCommentData.isEmpty()) {
|
||||
d->xiphCommentData = data;
|
||||
block = new UnknownMetadataBlock(MetadataBlock::VorbisComment, data);
|
||||
}
|
||||
else {
|
||||
debug("FLAC::File::scan() -- multiple Vorbis Comment blocks found, discarding");
|
||||
}
|
||||
}
|
||||
else if(blockType == MetadataBlock::Picture) {
|
||||
auto picture = new FLAC::Picture();
|
||||
if(picture->parse(data)) {
|
||||
block = picture;
|
||||
}
|
||||
else {
|
||||
debug("FLAC::File::scan() -- invalid picture found, discarding");
|
||||
delete picture;
|
||||
}
|
||||
}
|
||||
else if(blockType == MetadataBlock::Padding) {
|
||||
// Skip all padding blocks.
|
||||
}
|
||||
else {
|
||||
block = new UnknownMetadataBlock(blockType, data);
|
||||
}
|
||||
|
||||
if(block)
|
||||
d->blocks.append(block);
|
||||
|
||||
nextBlockOffset += blockLength + 4;
|
||||
|
||||
if(isLastBlock)
|
||||
break;
|
||||
}
|
||||
|
||||
// End of metadata, now comes the datastream
|
||||
|
||||
d->streamStart = nextBlockOffset;
|
||||
|
||||
d->scanned = true;
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -44,9 +43,9 @@ namespace TagLib {
|
||||
//! An implementation of FLAC metadata
|
||||
|
||||
/*!
|
||||
* This is implementation of FLAC metadata for non-Ogg FLAC files. At some
|
||||
* This is an implementation of FLAC metadata for non-Ogg FLAC files. At some
|
||||
* point when Ogg / FLAC is more common there will be a similar implementation
|
||||
* under the Ogg hiearchy.
|
||||
* under the Ogg hierarchy.
|
||||
*
|
||||
* This supports ID3v1, ID3v2 and Xiph style comments as well as reading stream
|
||||
* properties from the file.
|
||||
@@ -67,38 +66,82 @@ namespace TagLib {
|
||||
{
|
||||
public:
|
||||
/*!
|
||||
* Contructs a FLAC file from \a file. If \a readProperties is true the
|
||||
* file's audio properties will also be read using \a propertiesStyle. If
|
||||
* false, \a propertiesStyle is ignored.
|
||||
*
|
||||
* \deprecated This constructor will be dropped in favor of the one below
|
||||
* in a future version.
|
||||
* This set of flags is used for various operations and is suitable for
|
||||
* being OR-ed together.
|
||||
*/
|
||||
File(FileName file, bool readProperties = true,
|
||||
Properties::ReadStyle propertiesStyle = Properties::Average);
|
||||
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
|
||||
};
|
||||
|
||||
/*!
|
||||
* Contructs a FLAC file from \a file. If \a readProperties is true the
|
||||
* file's audio properties will also be read using \a propertiesStyle. If
|
||||
* false, \a propertiesStyle is ignored.
|
||||
* Constructs a FLAC file from \a file. If \a readProperties is \c true the
|
||||
* file's audio properties will also be read.
|
||||
*
|
||||
* If this file contains and ID3v2 tag the frames will be created using
|
||||
* \a frameFactory.
|
||||
* \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).
|
||||
*/
|
||||
// BIC: merge with the above constructor
|
||||
File(FileName file, bool readProperties = true,
|
||||
Properties::ReadStyle propertiesStyle = Properties::Average,
|
||||
ID3v2::FrameFactory *frameFactory = nullptr);
|
||||
|
||||
/*!
|
||||
* Constructs a FLAC file from \a file. If \a readProperties is \c 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.
|
||||
*
|
||||
* \note In the current implementation, \a propertiesStyle is ignored.
|
||||
*
|
||||
* \deprecated Use the constructor above.
|
||||
*/
|
||||
TAGLIB_DEPRECATED
|
||||
File(FileName file, ID3v2::FrameFactory *frameFactory,
|
||||
bool readProperties = true,
|
||||
Properties::ReadStyle propertiesStyle = Properties::Average);
|
||||
|
||||
/*!
|
||||
* Contructs a FLAC file from \a file. If \a readProperties is true the
|
||||
* file's audio properties will also be read using \a propertiesStyle. If
|
||||
* false, \a propertiesStyle is ignored.
|
||||
* Constructs a FLAC file from \a stream. If \a readProperties is \c true the
|
||||
* file's audio properties will also be read.
|
||||
*
|
||||
* If this file contains and ID3v2 tag the frames will be created using
|
||||
* \a frameFactory.
|
||||
* \note TagLib will *not* take ownership of the stream, the caller is
|
||||
* responsible for deleting it after the File object.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
// BIC: merge with the above constructor
|
||||
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 \c 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.
|
||||
*
|
||||
* \deprecated Use the constructor above.
|
||||
*/
|
||||
TAGLIB_DEPRECATED
|
||||
File(IOStream *stream, ID3v2::FrameFactory *frameFactory,
|
||||
bool readProperties = true,
|
||||
Properties::ReadStyle propertiesStyle = Properties::Average);
|
||||
@@ -106,7 +149,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,
|
||||
@@ -116,7 +162,7 @@ namespace TagLib {
|
||||
* \see ID3v1Tag()
|
||||
* \see XiphComment()
|
||||
*/
|
||||
virtual TagLib::Tag *tag() const;
|
||||
TagLib::Tag *tag() const override;
|
||||
|
||||
/*!
|
||||
* Implements the unified property interface -- export function.
|
||||
@@ -124,104 +170,116 @@ 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.
|
||||
* As with the export, only one tag is taken into account. If the file
|
||||
* has no tag at all, a XiphComment will be created.
|
||||
* This always creates a Xiph comment, if none exists. The return value
|
||||
* relates to the Xiph comment only.
|
||||
* 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
|
||||
* will also keep any old ID3-tags up to date. If the file
|
||||
* has no XiphComment, one will be constructed from the ID3-tags.
|
||||
*
|
||||
* This returns true if the save was successful.
|
||||
* This returns \c true if the save was successful.
|
||||
*/
|
||||
virtual bool save();
|
||||
bool save() override;
|
||||
|
||||
/*!
|
||||
* Returns a pointer to the ID3v2 tag of the file.
|
||||
*
|
||||
* If \a create is false (the default) this will return a null pointer
|
||||
* if there is no valid ID3v2 tag. If \a create is true it will create
|
||||
* an ID3v2 tag if one does not exist.
|
||||
* If \a create is \c false (the default) this returns a null pointer
|
||||
* if there is no valid ID3v2 tag. If \a create is \c true it will create
|
||||
* an ID3v2 tag if one does not exist and returns a valid pointer.
|
||||
*
|
||||
* \note This may return a valid pointer regardless of whether or not the
|
||||
* file on disk has an ID3v2 tag. Use hasID3v2Tag() to check if the file
|
||||
* on disk actually has an ID3v2 tag.
|
||||
*
|
||||
* \note The Tag <b>is still</b> owned by the FLAC::File and should not be
|
||||
* deleted by the user. It will be deleted when the file (object) is
|
||||
* destroyed.
|
||||
*
|
||||
* \see hasID3v2Tag()
|
||||
*/
|
||||
ID3v2::Tag *ID3v2Tag(bool create = false);
|
||||
|
||||
/*!
|
||||
* Returns a pointer to the ID3v1 tag of the file.
|
||||
*
|
||||
* If \a create is false (the default) this will return a null pointer
|
||||
* if there is no valid ID3v1 tag. If \a create is true it will create
|
||||
* an ID3v1 tag if one does not exist.
|
||||
* If \a create is \c false (the default) this returns a null pointer
|
||||
* if there is no valid APE tag. If \a create is \c true it will create
|
||||
* an APE tag if one does not exist and returns a valid pointer.
|
||||
*
|
||||
* \note This may return a valid pointer regardless of whether or not the
|
||||
* file on disk has an ID3v1 tag. Use hasID3v1Tag() to check if the file
|
||||
* on disk actually has an ID3v1 tag.
|
||||
*
|
||||
* \note The Tag <b>is still</b> owned by the FLAC::File and should not be
|
||||
* deleted by the user. It will be deleted when the file (object) is
|
||||
* destroyed.
|
||||
*
|
||||
* \see hasID3v1Tag()
|
||||
*/
|
||||
ID3v1::Tag *ID3v1Tag(bool create = false);
|
||||
|
||||
/*!
|
||||
* Returns a pointer to the XiphComment for the file.
|
||||
*
|
||||
* If \a create is false (the default) this will return a null pointer
|
||||
* if there is no valid XiphComment. If \a create is true it will create
|
||||
* a XiphComment if one does not exist.
|
||||
* If \a create is \c false (the default) this returns a null pointer
|
||||
* if there is no valid XiphComment. If \a create is \c true it will create
|
||||
* a XiphComment if one does not exist and returns a valid pointer.
|
||||
*
|
||||
* \note This may return a valid pointer regardless of whether or not the
|
||||
* file on disk has a XiphComment. Use hasXiphComment() to check if the
|
||||
* file on disk actually has a XiphComment.
|
||||
*
|
||||
* \note The Tag <b>is still</b> owned by the FLAC::File and should not be
|
||||
* deleted by the user. It will be deleted when the file (object) is
|
||||
* destroyed.
|
||||
*
|
||||
* \see hasXiphComment()
|
||||
*/
|
||||
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
|
||||
*/
|
||||
void setID3v2FrameFactory(const ID3v2::FrameFactory *factory);
|
||||
|
||||
/*!
|
||||
* Returns the block of data used by FLAC::Properties for parsing the
|
||||
* stream properties.
|
||||
*
|
||||
* \deprecated This method will not be public in a future release.
|
||||
*/
|
||||
ByteVector streamInfoData(); // BIC: remove
|
||||
|
||||
/*!
|
||||
* Returns the length of the audio-stream, used by FLAC::Properties for
|
||||
* calculating the bitrate.
|
||||
*
|
||||
* \deprecated This method will not be public in a future release.
|
||||
*/
|
||||
long streamLength(); // BIC: remove
|
||||
|
||||
/*!
|
||||
* Returns a list of pictures attached to the FLAC file.
|
||||
*/
|
||||
List<Picture *> pictureList();
|
||||
|
||||
/*!
|
||||
* Removes an attached picture. If \a del is true the picture's memory
|
||||
* will be freed; if it is false, it must be deleted by the user.
|
||||
* Removes an attached picture. If \a del is \c true the picture's memory
|
||||
* will be freed; if it is \c false, it must be deleted by the user.
|
||||
*/
|
||||
void removePicture(Picture *picture, bool del = true);
|
||||
|
||||
@@ -238,21 +296,60 @@ namespace TagLib {
|
||||
*/
|
||||
void addPicture(Picture *picture);
|
||||
|
||||
private:
|
||||
File(const File &);
|
||||
File &operator=(const File &);
|
||||
/*!
|
||||
* 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);
|
||||
|
||||
void read(bool readProperties, Properties::ReadStyle propertiesStyle);
|
||||
/*!
|
||||
* Returns whether or not the file on disk actually has a XiphComment.
|
||||
*
|
||||
* \see xiphComment()
|
||||
*/
|
||||
bool hasXiphComment() const;
|
||||
|
||||
/*!
|
||||
* Returns whether or not the file on disk actually has an ID3v1 tag.
|
||||
*
|
||||
* \see ID3v1Tag()
|
||||
*/
|
||||
bool hasID3v1Tag() const;
|
||||
|
||||
/*!
|
||||
* Returns whether or not the file on disk actually has an ID3v2 tag.
|
||||
*
|
||||
* \see ID3v2Tag()
|
||||
*/
|
||||
bool hasID3v2Tag() const;
|
||||
|
||||
/*!
|
||||
* Returns whether or not the given \a stream can be opened as a FLAC
|
||||
* file.
|
||||
*
|
||||
* \note This method is designed to do a quick check. The result may
|
||||
* not necessarily be correct.
|
||||
*/
|
||||
static bool isSupported(IOStream *stream);
|
||||
|
||||
private:
|
||||
void read(bool readProperties);
|
||||
void scan();
|
||||
long findID3v2();
|
||||
long findID3v1();
|
||||
ByteVector xiphCommentData() const;
|
||||
long findPaddingBreak(long nextPageOffset, long targetOffset, bool *isLast);
|
||||
|
||||
class FilePrivate;
|
||||
FilePrivate *d;
|
||||
TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE
|
||||
std::unique_ptr<FilePrivate> d;
|
||||
};
|
||||
}
|
||||
}
|
||||
} // namespace FLAC
|
||||
} // namespace TagLib
|
||||
|
||||
#endif
|
||||
|
||||
@@ -23,29 +23,14 @@
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#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;
|
||||
|
||||
@@ -26,20 +26,21 @@
|
||||
#ifndef TAGLIB_FLACMETADATABLOCK_H
|
||||
#define TAGLIB_FLACMETADATABLOCK_H
|
||||
|
||||
#include "tlist.h"
|
||||
#include "tbytevector.h"
|
||||
#include "taglib_export.h"
|
||||
|
||||
namespace TagLib {
|
||||
|
||||
namespace FLAC {
|
||||
|
||||
//! FLAC metadata block
|
||||
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,52 +23,37 @@
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#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
|
||||
{
|
||||
@@ -82,10 +67,10 @@ bool FLAC::Picture::parse(const ByteVector &data)
|
||||
return false;
|
||||
}
|
||||
|
||||
int pos = 0;
|
||||
d->type = FLAC::Picture::Type(data.mid(pos, 4).toUInt());
|
||||
unsigned int pos = 0;
|
||||
d->type = static_cast<FLAC::Picture::Type>(data.toUInt(pos));
|
||||
pos += 4;
|
||||
uint mimeTypeLength = data.mid(pos, 4).toUInt();
|
||||
unsigned int mimeTypeLength = data.toUInt(pos);
|
||||
pos += 4;
|
||||
if(pos + mimeTypeLength + 24 > data.size()) {
|
||||
debug("Invalid picture block.");
|
||||
@@ -93,7 +78,7 @@ bool FLAC::Picture::parse(const ByteVector &data)
|
||||
}
|
||||
d->mimeType = String(data.mid(pos, mimeTypeLength), String::UTF8);
|
||||
pos += mimeTypeLength;
|
||||
uint descriptionLength = data.mid(pos, 4).toUInt();
|
||||
unsigned int descriptionLength = data.toUInt(pos);
|
||||
pos += 4;
|
||||
if(pos + descriptionLength + 20 > data.size()) {
|
||||
debug("Invalid picture block.");
|
||||
@@ -101,15 +86,15 @@ bool FLAC::Picture::parse(const ByteVector &data)
|
||||
}
|
||||
d->description = String(data.mid(pos, descriptionLength), String::UTF8);
|
||||
pos += descriptionLength;
|
||||
d->width = data.mid(pos, 4).toUInt();
|
||||
d->width = data.toUInt(pos);
|
||||
pos += 4;
|
||||
d->height = data.mid(pos, 4).toUInt();
|
||||
d->height = data.toUInt(pos);
|
||||
pos += 4;
|
||||
d->colorDepth = data.mid(pos, 4).toUInt();
|
||||
d->colorDepth = data.toUInt(pos);
|
||||
pos += 4;
|
||||
d->numColors = data.mid(pos, 4).toUInt();
|
||||
d->numColors = data.toUInt(pos);
|
||||
pos += 4;
|
||||
uint dataLength = data.mid(pos, 4).toUInt();
|
||||
unsigned int dataLength = data.toUInt(pos);
|
||||
pos += 4;
|
||||
if(pos + dataLength > data.size()) {
|
||||
debug("Invalid picture block.");
|
||||
@@ -218,4 +203,3 @@ void FLAC::Picture::setData(const ByteVector &data)
|
||||
{
|
||||
d->data = data;
|
||||
}
|
||||
|
||||
|
||||
@@ -29,68 +29,28 @@
|
||||
#include "tlist.h"
|
||||
#include "tstring.h"
|
||||
#include "tbytevector.h"
|
||||
#include "tpicturetype.h"
|
||||
#include "taglib_export.h"
|
||||
#include "flacmetadatablock.h"
|
||||
|
||||
namespace TagLib {
|
||||
|
||||
namespace FLAC {
|
||||
|
||||
//! FLAC picture
|
||||
class TAGLIB_EXPORT Picture : public MetadataBlock
|
||||
{
|
||||
public:
|
||||
|
||||
/*!
|
||||
/*
|
||||
* 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.
|
||||
@@ -112,7 +72,7 @@ namespace TagLib {
|
||||
* Sets the mime type of the image. This should in most cases be
|
||||
* "image/png" or "image/jpeg".
|
||||
*/
|
||||
void setMimeType(const String &m);
|
||||
void setMimeType(const String &mimeType);
|
||||
|
||||
/*!
|
||||
* Returns a text description of the image.
|
||||
@@ -121,10 +81,10 @@ namespace TagLib {
|
||||
String description() const;
|
||||
|
||||
/*!
|
||||
* Sets a textual description of the image to \a desc.
|
||||
* Sets a textual description of the image to \a description.
|
||||
*/
|
||||
|
||||
void setDescription(const String &desc);
|
||||
void setDescription(const String &description);
|
||||
|
||||
/*!
|
||||
* Returns the width of the image.
|
||||
@@ -134,7 +94,7 @@ namespace TagLib {
|
||||
/*!
|
||||
* Sets the width of the image.
|
||||
*/
|
||||
void setWidth(int w);
|
||||
void setWidth(int width);
|
||||
|
||||
/*!
|
||||
* Returns the height of the image.
|
||||
@@ -144,7 +104,7 @@ namespace TagLib {
|
||||
/*!
|
||||
* Sets the height of the image.
|
||||
*/
|
||||
void setHeight(int h);
|
||||
void setHeight(int height);
|
||||
|
||||
/*!
|
||||
* Returns the color depth (in bits-per-pixel) of the image.
|
||||
@@ -154,7 +114,7 @@ namespace TagLib {
|
||||
/*!
|
||||
* Sets the color depth (in bits-per-pixel) of the image.
|
||||
*/
|
||||
void setColorDepth(int depth);
|
||||
void setColorDepth(int colorDepth);
|
||||
|
||||
/*!
|
||||
* Returns the number of colors used on the image..
|
||||
@@ -179,30 +139,25 @@ 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.
|
||||
*/
|
||||
bool parse(const ByteVector &rawData);
|
||||
bool parse(const ByteVector &data);
|
||||
|
||||
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,37 +23,22 @@
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#include <tstring.h>
|
||||
#include <tdebug.h>
|
||||
|
||||
#include "flacproperties.h"
|
||||
#include "flacfile.h"
|
||||
|
||||
#include "tstring.h"
|
||||
#include "tdebug.h"
|
||||
|
||||
using namespace TagLib;
|
||||
|
||||
class FLAC::Properties::PropertiesPrivate
|
||||
{
|
||||
public:
|
||||
PropertiesPrivate(ByteVector d, long st, ReadStyle s) :
|
||||
data(d),
|
||||
streamLength(st),
|
||||
style(s),
|
||||
length(0),
|
||||
bitrate(0),
|
||||
sampleRate(0),
|
||||
sampleWidth(0),
|
||||
channels(0),
|
||||
sampleFrames(0) {}
|
||||
|
||||
ByteVector data;
|
||||
long streamLength;
|
||||
ReadStyle style;
|
||||
int length;
|
||||
int bitrate;
|
||||
int sampleRate;
|
||||
int sampleWidth;
|
||||
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;
|
||||
};
|
||||
|
||||
@@ -61,24 +46,16 @@ public:
|
||||
// public members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
FLAC::Properties::Properties(ByteVector data, long streamLength, ReadStyle style) : AudioProperties(style)
|
||||
FLAC::Properties::Properties(const ByteVector &data, offset_t streamLength, ReadStyle style) :
|
||||
AudioProperties(style),
|
||||
d(std::make_unique<PropertiesPrivate>())
|
||||
{
|
||||
d = new PropertiesPrivate(data, streamLength, style);
|
||||
read();
|
||||
read(data, streamLength);
|
||||
}
|
||||
|
||||
FLAC::Properties::Properties(File *file, ReadStyle style) : AudioProperties(style)
|
||||
{
|
||||
d = new PropertiesPrivate(file->streamInfoData(), file->streamLength(), style);
|
||||
read();
|
||||
}
|
||||
FLAC::Properties::~Properties() = default;
|
||||
|
||||
FLAC::Properties::~Properties()
|
||||
{
|
||||
delete d;
|
||||
}
|
||||
|
||||
int FLAC::Properties::length() const
|
||||
int FLAC::Properties::lengthInMilliseconds() const
|
||||
{
|
||||
return d->length;
|
||||
}
|
||||
@@ -93,9 +70,9 @@ int FLAC::Properties::sampleRate() const
|
||||
return d->sampleRate;
|
||||
}
|
||||
|
||||
int FLAC::Properties::sampleWidth() const
|
||||
int FLAC::Properties::bitsPerSample() const
|
||||
{
|
||||
return d->sampleWidth;
|
||||
return d->bitsPerSample;
|
||||
}
|
||||
|
||||
int FLAC::Properties::channels() const
|
||||
@@ -117,14 +94,14 @@ ByteVector FLAC::Properties::signature() const
|
||||
// private members
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void FLAC::Properties::read()
|
||||
void FLAC::Properties::read(const ByteVector &data, offset_t streamLength)
|
||||
{
|
||||
if(d->data.size() < 18) {
|
||||
if(data.size() < 18) {
|
||||
debug("FLAC::Properties::read() - FLAC properties must contain at least 18 bytes.");
|
||||
return;
|
||||
}
|
||||
|
||||
int pos = 0;
|
||||
unsigned int pos = 0;
|
||||
|
||||
// Minimum block size (in samples)
|
||||
pos += 2;
|
||||
@@ -138,32 +115,28 @@ void FLAC::Properties::read()
|
||||
// Maximum frame size (in bytes)
|
||||
pos += 3;
|
||||
|
||||
uint flags = d->data.mid(pos, 4).toUInt(true);
|
||||
const unsigned int flags = data.toUInt(pos, true);
|
||||
pos += 4;
|
||||
|
||||
d->sampleRate = flags >> 12;
|
||||
d->channels = ((flags >> 9) & 7) + 1;
|
||||
d->sampleWidth = ((flags >> 4) & 31) + 1;
|
||||
d->sampleRate = flags >> 12;
|
||||
d->channels = ((flags >> 9) & 7) + 1;
|
||||
d->bitsPerSample = ((flags >> 4) & 31) + 1;
|
||||
|
||||
// The last 4 bits are the most significant 4 bits for the 36 bit
|
||||
// stream length in samples. (Audio files measured in days)
|
||||
|
||||
unsigned long long hi = flags & 0xf;
|
||||
unsigned long long lo = d->data.mid(pos, 4).toUInt(true);
|
||||
const unsigned long long hi = flags & 0xf;
|
||||
const unsigned long long lo = data.toUInt(pos, true);
|
||||
pos += 4;
|
||||
|
||||
d->sampleFrames = (hi << 32) | lo;
|
||||
|
||||
if(d->sampleRate > 0)
|
||||
d->length = int(d->sampleFrames / d->sampleRate);
|
||||
if(d->sampleFrames > 0 && d->sampleRate > 0) {
|
||||
const auto length = static_cast<double>(d->sampleFrames) * 1000.0 / d->sampleRate;
|
||||
d->length = static_cast<int>(length + 0.5);
|
||||
d->bitrate = static_cast<int>(static_cast<double>(streamLength) * 8.0 / length + 0.5);
|
||||
}
|
||||
|
||||
// Uncompressed bitrate:
|
||||
|
||||
//d->bitrate = ((d->sampleRate * d->channels) / 1000) * d->sampleWidth;
|
||||
|
||||
// Real bitrate:
|
||||
|
||||
d->bitrate = d->length > 0 ? ((d->streamLength * 8UL) / d->length) / 1000 : 0;
|
||||
|
||||
d->signature = d->data.mid(pos, 32);
|
||||
if(data.size() >= pos + 16)
|
||||
d->signature = data.mid(pos, 16);
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#ifndef TAGLIB_FLACPROPERTIES_H
|
||||
#define TAGLIB_FLACPROPERTIES_H
|
||||
|
||||
#include "tbytevector.h"
|
||||
#include "taglib_export.h"
|
||||
#include "audioproperties.h"
|
||||
|
||||
@@ -33,12 +34,10 @@ namespace TagLib {
|
||||
|
||||
namespace FLAC {
|
||||
|
||||
class File;
|
||||
|
||||
//! An implementation of audio property reading for FLAC
|
||||
|
||||
/*!
|
||||
* This reads the data from an FLAC stream found in the AudioProperties
|
||||
* This reads the data from a FLAC stream found in the AudioProperties
|
||||
* API.
|
||||
*/
|
||||
|
||||
@@ -49,55 +48,63 @@ 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;
|
||||
|
||||
// Reimplementations.
|
||||
|
||||
virtual int length() const;
|
||||
virtual int bitrate() const;
|
||||
virtual int sampleRate() const;
|
||||
virtual int channels() const;
|
||||
Properties(const Properties &) = delete;
|
||||
Properties &operator=(const Properties &) = delete;
|
||||
|
||||
/*!
|
||||
* Returns the sample width as read from the FLAC identification
|
||||
* header.
|
||||
* Returns the length of the file in milliseconds.
|
||||
*
|
||||
* \see lengthInSeconds()
|
||||
*/
|
||||
int sampleWidth() const;
|
||||
int lengthInMilliseconds() const override;
|
||||
|
||||
/*!
|
||||
* Return the number of sample frames
|
||||
* Returns the average bit rate of the file in kb/s.
|
||||
*/
|
||||
int bitrate() const override;
|
||||
|
||||
/*!
|
||||
* Returns the sample rate in Hz.
|
||||
*/
|
||||
int sampleRate() const override;
|
||||
|
||||
/*!
|
||||
* Returns the number of audio channels.
|
||||
*/
|
||||
int channels() const override;
|
||||
|
||||
/*!
|
||||
* Returns the number of bits per audio sample as read from the FLAC
|
||||
* identification header.
|
||||
*/
|
||||
int bitsPerSample() const;
|
||||
|
||||
/*!
|
||||
* Return the number of sample frames.
|
||||
*/
|
||||
unsigned long long sampleFrames() const;
|
||||
|
||||
/*!
|
||||
* Returns the MD5 signature of the uncompressed audio stream as read
|
||||
* from the stream info header header.
|
||||
* from the stream info header.
|
||||
*/
|
||||
ByteVector signature() const;
|
||||
|
||||
private:
|
||||
Properties(const Properties &);
|
||||
Properties &operator=(const Properties &);
|
||||
|
||||
void read();
|
||||
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,13 +23,6 @@
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <taglib.h>
|
||||
#include <tdebug.h>
|
||||
#include <tstring.h>
|
||||
#include "flacunknownmetadatablock.h"
|
||||
|
||||
using namespace TagLib;
|
||||
@@ -37,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
|
||||
{
|
||||
@@ -80,4 +67,3 @@ ByteVector FLAC::UnknownMetadataBlock::render() const
|
||||
{
|
||||
return d->data;
|
||||
}
|
||||
|
||||
|
||||
@@ -32,19 +32,21 @@
|
||||
#include "flacmetadatablock.h"
|
||||
|
||||
namespace TagLib {
|
||||
|
||||
namespace FLAC {
|
||||
|
||||
//! Unknown FLAC metadata block
|
||||
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 +66,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,19 @@
|
||||
* *
|
||||
* 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"
|
||||
|
||||
using namespace TagLib;
|
||||
using namespace IT;
|
||||
@@ -32,7 +36,7 @@ class IT::File::FilePrivate
|
||||
{
|
||||
public:
|
||||
FilePrivate(AudioProperties::ReadStyle propertiesStyle)
|
||||
: tag(), properties(propertiesStyle)
|
||||
: properties(propertiesStyle)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -43,39 +47,28 @@ public:
|
||||
IT::File::File(FileName file, bool readProperties,
|
||||
AudioProperties::ReadStyle propertiesStyle) :
|
||||
Mod::FileBase(file),
|
||||
d(new FilePrivate(propertiesStyle))
|
||||
d(std::make_unique<FilePrivate>(propertiesStyle))
|
||||
{
|
||||
read(readProperties);
|
||||
if(isOpen())
|
||||
read(readProperties);
|
||||
}
|
||||
|
||||
IT::File::File(IOStream *stream, bool readProperties,
|
||||
AudioProperties::ReadStyle propertiesStyle) :
|
||||
Mod::FileBase(stream),
|
||||
d(new FilePrivate(propertiesStyle))
|
||||
d(std::make_unique<FilePrivate>(propertiesStyle))
|
||||
{
|
||||
read(readProperties);
|
||||
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;
|
||||
@@ -94,9 +87,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;
|
||||
@@ -105,9 +98,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;
|
||||
|
||||
@@ -116,28 +109,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((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);
|
||||
|
||||
@@ -145,17 +138,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))
|
||||
@@ -174,7 +167,7 @@ bool IT::File::save()
|
||||
if(messageOffset + messageLength >= fileSize) {
|
||||
// append new message
|
||||
seek(54);
|
||||
writeU16L(message.size());
|
||||
writeU16L(static_cast<unsigned short>(message.size()));
|
||||
writeU32L(messageOffset);
|
||||
seek(messageOffset);
|
||||
writeBlock(message);
|
||||
@@ -232,7 +225,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');
|
||||
@@ -250,15 +243,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;
|
||||
@@ -270,10 +263,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);
|
||||
|
||||
@@ -288,8 +281,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);
|
||||
@@ -321,7 +314,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,60 +23,73 @@
|
||||
#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"
|
||||
|
||||
namespace TagLib {
|
||||
|
||||
//! An implementation of Impulse Tracker metadata
|
||||
|
||||
/*!
|
||||
* This is an implementation of Impulse Tracker metadata.
|
||||
*/
|
||||
|
||||
namespace IT {
|
||||
|
||||
//! An implementation of TagLib::File with IT specific methods
|
||||
|
||||
/*!
|
||||
* This implements and provides an interface for IT 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 IT files.
|
||||
*/
|
||||
|
||||
class TAGLIB_EXPORT File : public Mod::FileBase {
|
||||
public:
|
||||
/*!
|
||||
* Contructs a Impulse Tracker file from \a file. If \a readProperties
|
||||
* is true the file's audio properties will also be read using
|
||||
* \a propertiesStyle. If false, \a propertiesStyle is ignored.
|
||||
* Constructs an Impulse Tracker file from \a file.
|
||||
*
|
||||
* \note In the current implementation, both \a readProperties and
|
||||
* \a propertiesStyle are ignored. The audio properties are always
|
||||
* read.
|
||||
*/
|
||||
File(FileName file, bool readProperties = true,
|
||||
AudioProperties::ReadStyle propertiesStyle =
|
||||
AudioProperties::Average);
|
||||
|
||||
/*!
|
||||
* Contructs a Impulse Tracker file from \a stream. If \a readProperties
|
||||
* is true the file's audio properties will also be read using
|
||||
* \a propertiesStyle. If false, \a propertiesStyle is ignored.
|
||||
* Constructs an Impulse Tracker file from \a stream.
|
||||
*
|
||||
* \note In the current implementation, both \a readProperties and
|
||||
* \a propertiesStyle are ignored. The audio properties are always
|
||||
* read.
|
||||
*
|
||||
* \note TagLib will *not* take ownership of the stream, the caller is
|
||||
* responsible for deleting it after the File object.
|
||||
*/
|
||||
File(IOStream *stram, bool readProperties = true,
|
||||
File(IOStream *stream, bool readProperties = true,
|
||||
AudioProperties::ReadStyle propertiesStyle =
|
||||
AudioProperties::Average);
|
||||
|
||||
/*!
|
||||
* 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.
|
||||
@@ -84,19 +97,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,74 +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::bitrate() const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int IT::Properties::sampleRate() const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
IT::Properties::~Properties() = default;
|
||||
|
||||
int IT::Properties::channels() const
|
||||
{
|
||||
return d->channels;
|
||||
}
|
||||
|
||||
ushort IT::Properties::lengthInPatterns() const
|
||||
unsigned short IT::Properties::lengthInPatterns() const
|
||||
{
|
||||
return d->lengthInPatterns;
|
||||
}
|
||||
@@ -104,67 +71,67 @@ bool IT::Properties::stereo() const
|
||||
return d->flags & Stereo;
|
||||
}
|
||||
|
||||
ushort IT::Properties::instrumentCount() const
|
||||
unsigned short IT::Properties::instrumentCount() const
|
||||
{
|
||||
return d->instrumentCount;
|
||||
}
|
||||
|
||||
ushort IT::Properties::sampleCount() const
|
||||
unsigned short IT::Properties::sampleCount() const
|
||||
{
|
||||
return d->sampleCount;
|
||||
}
|
||||
|
||||
ushort IT::Properties::patternCount() const
|
||||
unsigned short IT::Properties::patternCount() const
|
||||
{
|
||||
return d->patternCount;
|
||||
}
|
||||
|
||||
ushort IT::Properties::version() const
|
||||
unsigned short IT::Properties::version() const
|
||||
{
|
||||
return d->version;
|
||||
}
|
||||
|
||||
ushort IT::Properties::compatibleVersion() const
|
||||
unsigned short IT::Properties::compatibleVersion() const
|
||||
{
|
||||
return d->compatibleVersion;
|
||||
}
|
||||
|
||||
ushort IT::Properties::flags() const
|
||||
unsigned short IT::Properties::flags() const
|
||||
{
|
||||
return d->flags;
|
||||
}
|
||||
|
||||
ushort IT::Properties::special() const
|
||||
unsigned short IT::Properties::special() const
|
||||
{
|
||||
return d->special;
|
||||
}
|
||||
|
||||
uchar IT::Properties::globalVolume() const
|
||||
unsigned char IT::Properties::globalVolume() const
|
||||
{
|
||||
return d->globalVolume;
|
||||
}
|
||||
|
||||
uchar IT::Properties::mixVolume() const
|
||||
unsigned char IT::Properties::mixVolume() const
|
||||
{
|
||||
return d->mixVolume;
|
||||
}
|
||||
|
||||
uchar IT::Properties::tempo() const
|
||||
unsigned char IT::Properties::tempo() const
|
||||
{
|
||||
return d->tempo;
|
||||
}
|
||||
|
||||
uchar IT::Properties::bpmSpeed() const
|
||||
unsigned char IT::Properties::bpmSpeed() const
|
||||
{
|
||||
return d->bpmSpeed;
|
||||
}
|
||||
|
||||
uchar IT::Properties::panningSeparation() const
|
||||
unsigned char IT::Properties::panningSeparation() const
|
||||
{
|
||||
return d->panningSeparation;
|
||||
}
|
||||
|
||||
uchar IT::Properties::pitchWheelDepth() const
|
||||
unsigned char IT::Properties::pitchWheelDepth() const
|
||||
{
|
||||
return d->pitchWheelDepth;
|
||||
}
|
||||
@@ -174,72 +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,20 +15,23 @@
|
||||
* *
|
||||
* 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
|
||||
#define TAGLIB_ITPROPERTIES_H
|
||||
|
||||
#include "taglib.h"
|
||||
#include "audioproperties.h"
|
||||
|
||||
namespace TagLib {
|
||||
namespace IT {
|
||||
//! An implementation of audio property reading for IT
|
||||
class TAGLIB_EXPORT Properties : public AudioProperties {
|
||||
friend class File;
|
||||
public:
|
||||
/*! Flag bits. */
|
||||
enum {
|
||||
@@ -49,53 +52,51 @@ namespace TagLib {
|
||||
};
|
||||
|
||||
Properties(AudioProperties::ReadStyle propertiesStyle);
|
||||
virtual ~Properties();
|
||||
~Properties() override;
|
||||
|
||||
int length() 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
|
||||
|
||||
70
taglib/matroska/ebml/ebmlbinaryelement.cpp
Normal file
70
taglib/matroska/ebml/ebmlbinaryelement.cpp
Normal file
@@ -0,0 +1,70 @@
|
||||
/***************************************************************************
|
||||
* 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 "ebmlbinaryelement.h"
|
||||
#include "ebmlutils.h"
|
||||
#include "tfile.h"
|
||||
#include "tdebug.h"
|
||||
|
||||
using namespace TagLib;
|
||||
|
||||
EBML::BinaryElement::BinaryElement(Id id, int sizeLength, offset_t dataSize):
|
||||
Element(id, sizeLength, dataSize)
|
||||
{
|
||||
}
|
||||
|
||||
EBML::BinaryElement::BinaryElement(Id id, int sizeLength, offset_t dataSize, offset_t):
|
||||
Element(id, sizeLength, dataSize)
|
||||
{
|
||||
}
|
||||
|
||||
EBML::BinaryElement::BinaryElement(Id id):
|
||||
Element(id, 0, 0)
|
||||
{
|
||||
}
|
||||
|
||||
const ByteVector& EBML::BinaryElement::getValue() const
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
void EBML::BinaryElement::setValue(const ByteVector& val)
|
||||
{
|
||||
value = val;
|
||||
}
|
||||
|
||||
bool EBML::BinaryElement::read(File &file)
|
||||
{
|
||||
value = file.readBlock(dataSize);
|
||||
if(value.size() != dataSize) {
|
||||
debug("Failed to read binary element");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
ByteVector EBML::BinaryElement::render()
|
||||
{
|
||||
ByteVector buffer = renderId();
|
||||
dataSize = value.size();
|
||||
buffer.append(renderVINT(dataSize, 0));
|
||||
buffer.append(value);
|
||||
return buffer;
|
||||
}
|
||||
51
taglib/matroska/ebml/ebmlbinaryelement.h
Normal file
51
taglib/matroska/ebml/ebmlbinaryelement.h
Normal file
@@ -0,0 +1,51 @@
|
||||
/***************************************************************************
|
||||
* 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_EBMLBINARYELEMENT_H
|
||||
#define TAGLIB_EBMLBINARYELEMENT_H
|
||||
#ifndef DO_NOT_DOCUMENT
|
||||
|
||||
#include "ebmlelement.h"
|
||||
#include "tbytevector.h"
|
||||
|
||||
namespace TagLib {
|
||||
class File;
|
||||
|
||||
namespace EBML {
|
||||
class BinaryElement : public Element
|
||||
{
|
||||
public:
|
||||
BinaryElement(Id id, int sizeLength, offset_t dataSize);
|
||||
BinaryElement(Id id, int sizeLength, offset_t dataSize, offset_t);
|
||||
explicit BinaryElement(Id id);
|
||||
|
||||
const ByteVector &getValue() const;
|
||||
void setValue(const ByteVector &val);
|
||||
bool read(File &file) override;
|
||||
ByteVector render() override;
|
||||
|
||||
private:
|
||||
ByteVector value;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
||||
213
taglib/matroska/ebml/ebmlelement.cpp
Normal file
213
taglib/matroska/ebml/ebmlelement.cpp
Normal file
@@ -0,0 +1,213 @@
|
||||
/***************************************************************************
|
||||
* This library is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU Lesser General Public License version *
|
||||
* 2.1 as published by the Free Software Foundation. *
|
||||
* *
|
||||
* This library is distributed in the hope that it will be useful, but *
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
||||
* Lesser General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Lesser General Public *
|
||||
* License along with this library; if not, write to the Free Software *
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
|
||||
* 02110-1301 USA *
|
||||
* *
|
||||
* Alternatively, this file is available under the Mozilla Public *
|
||||
* License Version 1.1. You may obtain a copy of the License at *
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#include "ebmlelement.h"
|
||||
#include "ebmlvoidelement.h"
|
||||
#include "ebmlmasterelement.h"
|
||||
#include "ebmlbinaryelement.h"
|
||||
#include "ebmlfloatelement.h"
|
||||
#include "ebmlmkseekhead.h"
|
||||
#include "ebmlmksegment.h"
|
||||
#include "ebmlmktags.h"
|
||||
#include "ebmlmkattachments.h"
|
||||
#include "ebmlmkchapters.h"
|
||||
#include "ebmlmktracks.h"
|
||||
#include "ebmlstringelement.h"
|
||||
#include "ebmluintelement.h"
|
||||
#include "ebmlutils.h"
|
||||
#include "tfile.h"
|
||||
#include "tdebug.h"
|
||||
#include "tutils.h"
|
||||
|
||||
using namespace TagLib;
|
||||
|
||||
#define RETURN_ELEMENT_FOR_CASE(eid) \
|
||||
case (eid): return make_unique_element<eid>(id, sizeLength, dataSize, offset)
|
||||
|
||||
std::unique_ptr<EBML::Element> EBML::Element::factory(File &file)
|
||||
{
|
||||
// Get the element ID
|
||||
const offset_t offset = file.tell();
|
||||
unsigned int uintId = readId(file);
|
||||
if(!uintId) {
|
||||
debug("Failed to parse EMBL ElementID");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Get the size length and data length
|
||||
const auto &[sizeLength, dataSize] = readVINT(file);
|
||||
if(!sizeLength)
|
||||
return nullptr;
|
||||
|
||||
// Return the subclass
|
||||
// The enum switch without default will give us a warning if an ID is missing
|
||||
auto id = static_cast<Id>(uintId);
|
||||
switch(id) {
|
||||
RETURN_ELEMENT_FOR_CASE(Id::EBMLHeader);
|
||||
RETURN_ELEMENT_FOR_CASE(Id::DocType);
|
||||
RETURN_ELEMENT_FOR_CASE(Id::DocTypeVersion);
|
||||
RETURN_ELEMENT_FOR_CASE(Id::MkSegment);
|
||||
RETURN_ELEMENT_FOR_CASE(Id::MkInfo);
|
||||
RETURN_ELEMENT_FOR_CASE(Id::MkTracks);
|
||||
RETURN_ELEMENT_FOR_CASE(Id::MkTags);
|
||||
RETURN_ELEMENT_FOR_CASE(Id::MkAttachments);
|
||||
RETURN_ELEMENT_FOR_CASE(Id::MkTag);
|
||||
RETURN_ELEMENT_FOR_CASE(Id::MkTagTargets);
|
||||
RETURN_ELEMENT_FOR_CASE(Id::MkSimpleTag);
|
||||
RETURN_ELEMENT_FOR_CASE(Id::MkAttachedFile);
|
||||
RETURN_ELEMENT_FOR_CASE(Id::MkSeek);
|
||||
RETURN_ELEMENT_FOR_CASE(Id::MkTrackEntry);
|
||||
RETURN_ELEMENT_FOR_CASE(Id::MkAudio);
|
||||
RETURN_ELEMENT_FOR_CASE(Id::MkTagName);
|
||||
RETURN_ELEMENT_FOR_CASE(Id::MkTagString);
|
||||
RETURN_ELEMENT_FOR_CASE(Id::MkAttachedFileName);
|
||||
RETURN_ELEMENT_FOR_CASE(Id::MkAttachedFileDescription);
|
||||
RETURN_ELEMENT_FOR_CASE(Id::MkTagLanguage);
|
||||
RETURN_ELEMENT_FOR_CASE(Id::MkAttachedFileMediaType);
|
||||
RETURN_ELEMENT_FOR_CASE(Id::MkCodecID);
|
||||
RETURN_ELEMENT_FOR_CASE(Id::MkTagTargetTypeValue);
|
||||
RETURN_ELEMENT_FOR_CASE(Id::MkTagTrackUID);
|
||||
RETURN_ELEMENT_FOR_CASE(Id::MkTagsLanguageDefault);
|
||||
RETURN_ELEMENT_FOR_CASE(Id::MkAttachedFileUID);
|
||||
RETURN_ELEMENT_FOR_CASE(Id::MkSeekPosition);
|
||||
RETURN_ELEMENT_FOR_CASE(Id::MkTimestampScale);
|
||||
RETURN_ELEMENT_FOR_CASE(Id::MkBitDepth);
|
||||
RETURN_ELEMENT_FOR_CASE(Id::MkChannels);
|
||||
RETURN_ELEMENT_FOR_CASE(Id::MkAttachedFileData);
|
||||
RETURN_ELEMENT_FOR_CASE(Id::MkSeekID);
|
||||
RETURN_ELEMENT_FOR_CASE(Id::MkDuration);
|
||||
RETURN_ELEMENT_FOR_CASE(Id::MkTitle);
|
||||
RETURN_ELEMENT_FOR_CASE(Id::MkSamplingFrequency);
|
||||
RETURN_ELEMENT_FOR_CASE(Id::MkSeekHead);
|
||||
RETURN_ELEMENT_FOR_CASE(Id::VoidElement);
|
||||
RETURN_ELEMENT_FOR_CASE(Id::MkCluster);
|
||||
RETURN_ELEMENT_FOR_CASE(Id::MkCodecState);
|
||||
RETURN_ELEMENT_FOR_CASE(Id::MkTagBinary);
|
||||
RETURN_ELEMENT_FOR_CASE(Id::MkCues);
|
||||
RETURN_ELEMENT_FOR_CASE(Id::MkCuePoint);
|
||||
RETURN_ELEMENT_FOR_CASE(Id::MkCueTime);
|
||||
RETURN_ELEMENT_FOR_CASE(Id::MkCueTrackPositions);
|
||||
RETURN_ELEMENT_FOR_CASE(Id::MkCueTrack);
|
||||
RETURN_ELEMENT_FOR_CASE(Id::MkCueClusterPosition);
|
||||
RETURN_ELEMENT_FOR_CASE(Id::MkCueRelativePosition);
|
||||
RETURN_ELEMENT_FOR_CASE(Id::MkCueDuration);
|
||||
RETURN_ELEMENT_FOR_CASE(Id::MkCueBlockNumber);
|
||||
RETURN_ELEMENT_FOR_CASE(Id::MkCueCodecState);
|
||||
RETURN_ELEMENT_FOR_CASE(Id::MkCueReference);
|
||||
RETURN_ELEMENT_FOR_CASE(Id::MkCueRefTime);
|
||||
RETURN_ELEMENT_FOR_CASE(Id::MkChapters);
|
||||
RETURN_ELEMENT_FOR_CASE(Id::MkEditionEntry);
|
||||
RETURN_ELEMENT_FOR_CASE(Id::MkEditionUID);
|
||||
RETURN_ELEMENT_FOR_CASE(Id::MkEditionFlagDefault);
|
||||
RETURN_ELEMENT_FOR_CASE(Id::MkEditionFlagOrdered);
|
||||
RETURN_ELEMENT_FOR_CASE(Id::MkChapterAtom);
|
||||
RETURN_ELEMENT_FOR_CASE(Id::MkChapterUID);
|
||||
RETURN_ELEMENT_FOR_CASE(Id::MkChapterTimeStart);
|
||||
RETURN_ELEMENT_FOR_CASE(Id::MkChapterTimeEnd);
|
||||
RETURN_ELEMENT_FOR_CASE(Id::MkChapterFlagHidden);
|
||||
RETURN_ELEMENT_FOR_CASE(Id::MkChapterDisplay);
|
||||
RETURN_ELEMENT_FOR_CASE(Id::MkChapString);
|
||||
RETURN_ELEMENT_FOR_CASE(Id::MkChapLanguage);
|
||||
}
|
||||
return std::make_unique<Element>(id, sizeLength, dataSize);
|
||||
}
|
||||
|
||||
unsigned int EBML::Element::readId(File &file)
|
||||
{
|
||||
auto buffer = file.readBlock(1);
|
||||
if(buffer.size() != 1) {
|
||||
debug("Failed to read VINT size");
|
||||
return 0;
|
||||
}
|
||||
const unsigned int numBytes = VINTSizeLength<4>(*buffer.begin());
|
||||
if(!numBytes)
|
||||
return 0;
|
||||
if(numBytes > 1)
|
||||
buffer.append(file.readBlock(numBytes - 1));
|
||||
if(buffer.size() != numBytes) {
|
||||
debug("Failed to read VINT data");
|
||||
return 0;
|
||||
}
|
||||
return buffer.toUInt(true);
|
||||
}
|
||||
|
||||
EBML::Element::Element(Id id, int sizeLength, offset_t dataSize):
|
||||
id(id), sizeLength(sizeLength), dataSize(dataSize)
|
||||
{
|
||||
}
|
||||
|
||||
EBML::Element::Element(Id id, int sizeLength, offset_t dataSize, offset_t):
|
||||
id(id), sizeLength(sizeLength), dataSize(dataSize)
|
||||
{
|
||||
}
|
||||
|
||||
EBML::Element::~Element() = default;
|
||||
|
||||
bool EBML::Element::read(File& file)
|
||||
{
|
||||
skipData(file);
|
||||
return true;
|
||||
}
|
||||
|
||||
void EBML::Element::skipData(File &file)
|
||||
{
|
||||
file.seek(dataSize, File::Position::Current);
|
||||
}
|
||||
|
||||
EBML::Element::Id EBML::Element::getId() const
|
||||
{
|
||||
return id;
|
||||
}
|
||||
|
||||
offset_t EBML::Element::headSize() const
|
||||
{
|
||||
return idSize(id) + sizeLength;
|
||||
}
|
||||
|
||||
offset_t EBML::Element::getSize() const
|
||||
{
|
||||
return headSize() + dataSize;
|
||||
}
|
||||
|
||||
int EBML::Element::getSizeLength() const
|
||||
{
|
||||
return sizeLength;
|
||||
}
|
||||
|
||||
int64_t EBML::Element::getDataSize() const
|
||||
{
|
||||
return dataSize;
|
||||
}
|
||||
|
||||
ByteVector EBML::Element::render()
|
||||
{
|
||||
ByteVector buffer = renderId();
|
||||
buffer.append(renderVINT(0, 0));
|
||||
return buffer;
|
||||
}
|
||||
|
||||
ByteVector EBML::Element::renderId() const
|
||||
{
|
||||
const int numBytes = idSize(id);
|
||||
static const auto byteOrder = Utils::systemByteOrder();
|
||||
const auto uintId = static_cast<uint32_t>(id);
|
||||
uint32_t data = byteOrder == Utils::LittleEndian ? Utils::byteSwap(uintId) : uintId;
|
||||
return ByteVector(reinterpret_cast<char *>(&data) + (4 - numBytes), numBytes);
|
||||
}
|
||||
249
taglib/matroska/ebml/ebmlelement.h
Normal file
249
taglib/matroska/ebml/ebmlelement.h
Normal file
@@ -0,0 +1,249 @@
|
||||
/***************************************************************************
|
||||
* This library is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU Lesser General Public License version *
|
||||
* 2.1 as published by the Free Software Foundation. *
|
||||
* *
|
||||
* This library is distributed in the hope that it will be useful, but *
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
||||
* Lesser General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Lesser General Public *
|
||||
* License along with this library; if not, write to the Free Software *
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
|
||||
* 02110-1301 USA *
|
||||
* *
|
||||
* Alternatively, this file is available under the Mozilla Public *
|
||||
* License Version 1.1. You may obtain a copy of the License at *
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef TAGLIB_EBMLELEMENT_H
|
||||
#define TAGLIB_EBMLELEMENT_H
|
||||
#ifndef DO_NOT_DOCUMENT
|
||||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include "taglib.h"
|
||||
|
||||
namespace TagLib
|
||||
{
|
||||
class File;
|
||||
class ByteVector;
|
||||
|
||||
namespace EBML {
|
||||
|
||||
class Element
|
||||
{
|
||||
public:
|
||||
enum class Id : unsigned int
|
||||
{
|
||||
EBMLHeader = 0x1A45DFA3,
|
||||
DocType = 0x4282,
|
||||
DocTypeVersion = 0x4287,
|
||||
VoidElement = 0xEC,
|
||||
MkSegment = 0x18538067,
|
||||
MkTags = 0x1254C367,
|
||||
MkTag = 0x7373,
|
||||
MkTagTargets = 0x63C0,
|
||||
MkTagTargetTypeValue = 0x68CA,
|
||||
MkTagTrackUID = 0x63C5,
|
||||
MkSimpleTag = 0x67C8,
|
||||
MkTagName = 0x45A3,
|
||||
MkTagLanguage = 0x447A,
|
||||
MkTagBinary = 0x4485,
|
||||
MkTagString = 0x4487,
|
||||
MkTagsTagLanguage = 0x447A,
|
||||
MkTagsLanguageDefault = 0x4484,
|
||||
MkAttachments = 0x1941A469,
|
||||
MkAttachedFile = 0x61A7,
|
||||
MkAttachedFileDescription = 0x467E,
|
||||
MkAttachedFileName = 0x466E,
|
||||
MkAttachedFileMediaType = 0x4660,
|
||||
MkAttachedFileData = 0x465C,
|
||||
MkAttachedFileUID = 0x46AE,
|
||||
MkSeekHead = 0x114D9B74,
|
||||
MkSeek = 0x4DBB,
|
||||
MkSeekID = 0x53AB,
|
||||
MkSeekPosition = 0x53AC,
|
||||
MkCluster = 0x1F43B675,
|
||||
MkCodecState = 0xA4,
|
||||
MkCues = 0x1C53BB6B,
|
||||
MkCuePoint = 0xBB,
|
||||
MkCueTime = 0xB3,
|
||||
MkCueTrackPositions = 0xB7,
|
||||
MkCueTrack = 0xF7,
|
||||
MkCueClusterPosition = 0xF1,
|
||||
MkCueRelativePosition = 0xF0,
|
||||
MkCueDuration = 0xB2,
|
||||
MkCueBlockNumber = 0x5378,
|
||||
MkCueCodecState = 0xEA,
|
||||
MkCueReference = 0xDB,
|
||||
MkCueRefTime = 0x96,
|
||||
MkInfo = 0x1549A966,
|
||||
MkTimestampScale = 0x2AD7B1,
|
||||
MkDuration = 0x4489,
|
||||
MkTitle = 0x7BA9,
|
||||
MkTracks = 0x1654AE6B,
|
||||
MkTrackEntry = 0xAE,
|
||||
MkCodecID = 0x86,
|
||||
MkAudio = 0xE1,
|
||||
MkSamplingFrequency = 0xB5,
|
||||
MkBitDepth = 0x6264,
|
||||
MkChannels = 0x9F,
|
||||
MkChapters = 0x1043A770,
|
||||
MkEditionEntry = 0x45B9,
|
||||
MkEditionUID = 0x45BC,
|
||||
MkEditionFlagDefault = 0x45DB,
|
||||
MkEditionFlagOrdered = 0x45DD,
|
||||
MkChapterAtom = 0xB6,
|
||||
MkChapterUID = 0x73C4,
|
||||
MkChapterTimeStart = 0x91,
|
||||
MkChapterTimeEnd = 0x92,
|
||||
MkChapterFlagHidden = 0x98,
|
||||
MkChapterDisplay = 0x80,
|
||||
MkChapString = 0x85,
|
||||
MkChapLanguage = 0x437C,
|
||||
};
|
||||
|
||||
Element(Id id, int sizeLength, offset_t dataSize);
|
||||
Element(Id id, int sizeLength, offset_t dataSize, offset_t);
|
||||
virtual ~Element();
|
||||
|
||||
virtual bool read(File &file);
|
||||
void skipData(File &file);
|
||||
Id getId() const;
|
||||
offset_t headSize() const;
|
||||
offset_t getSize() const;
|
||||
int getSizeLength() const;
|
||||
int64_t getDataSize() const;
|
||||
ByteVector renderId() const;
|
||||
virtual ByteVector render();
|
||||
static std::unique_ptr<Element> factory(File &file);
|
||||
static unsigned int readId(File &file);
|
||||
|
||||
protected:
|
||||
Id id;
|
||||
int sizeLength;
|
||||
offset_t dataSize;
|
||||
};
|
||||
|
||||
// Template specializations to ensure that elements for the different IDs
|
||||
// are consistently created and cast. They provide a mapping between IDs
|
||||
// and Element subclasses. The switch in factory() makes sure that the
|
||||
// template for all IDs are instantiated, i.e. that every ID has its Element
|
||||
// subclass mapped.
|
||||
class MasterElement;
|
||||
class UIntElement;
|
||||
class BinaryElement;
|
||||
class FloatElement;
|
||||
class MkSegment;
|
||||
class MkInfo;
|
||||
class MkTracks;
|
||||
class MkTags;
|
||||
class MkAttachments;
|
||||
class MkSeekHead;
|
||||
class MkChapters;
|
||||
class MkCues;
|
||||
class VoidElement;
|
||||
class UTF8StringElement;
|
||||
class Latin1StringElement;
|
||||
|
||||
template <Element::Id ID>
|
||||
struct GetElementTypeById;
|
||||
|
||||
template <> struct GetElementTypeById<Element::Id::EBMLHeader> { using type = MasterElement; };
|
||||
template <> struct GetElementTypeById<Element::Id::DocType> { using type = Latin1StringElement; };
|
||||
template <> struct GetElementTypeById<Element::Id::DocTypeVersion> { using type = UIntElement; };
|
||||
template <> struct GetElementTypeById<Element::Id::MkSegment> { using type = MkSegment; };
|
||||
template <> struct GetElementTypeById<Element::Id::MkInfo> { using type = MkInfo; };
|
||||
template <> struct GetElementTypeById<Element::Id::MkTracks> { using type = MkTracks; };
|
||||
template <> struct GetElementTypeById<Element::Id::MkTags> { using type = MkTags; };
|
||||
template <> struct GetElementTypeById<Element::Id::MkAttachments> { using type = MkAttachments; };
|
||||
template <> struct GetElementTypeById<Element::Id::MkTag> { using type = MasterElement; };
|
||||
template <> struct GetElementTypeById<Element::Id::MkTagTargets> { using type = MasterElement; };
|
||||
template <> struct GetElementTypeById<Element::Id::MkSimpleTag> { using type = MasterElement; };
|
||||
template <> struct GetElementTypeById<Element::Id::MkAttachedFile> { using type = MasterElement; };
|
||||
template <> struct GetElementTypeById<Element::Id::MkSeek> { using type = MasterElement; };
|
||||
template <> struct GetElementTypeById<Element::Id::MkTrackEntry> { using type = MasterElement; };
|
||||
template <> struct GetElementTypeById<Element::Id::MkAudio> { using type = MasterElement; };
|
||||
template <> struct GetElementTypeById<Element::Id::MkCuePoint> { using type = MasterElement; };
|
||||
template <> struct GetElementTypeById<Element::Id::MkCueTrackPositions> { using type = MasterElement; };
|
||||
template <> struct GetElementTypeById<Element::Id::MkCueReference> { using type = MasterElement; };
|
||||
template <> struct GetElementTypeById<Element::Id::MkCluster> { using type = MasterElement; };
|
||||
template <> struct GetElementTypeById<Element::Id::MkCues> { using type = MkCues; };
|
||||
template <> struct GetElementTypeById<Element::Id::MkTagName> { using type = UTF8StringElement; };
|
||||
template <> struct GetElementTypeById<Element::Id::MkTagString> { using type = UTF8StringElement; };
|
||||
template <> struct GetElementTypeById<Element::Id::MkAttachedFileName> { using type = UTF8StringElement; };
|
||||
template <> struct GetElementTypeById<Element::Id::MkAttachedFileDescription> { using type = UTF8StringElement; };
|
||||
template <> struct GetElementTypeById<Element::Id::MkTagLanguage> { using type = Latin1StringElement; };
|
||||
template <> struct GetElementTypeById<Element::Id::MkAttachedFileMediaType> { using type = Latin1StringElement; };
|
||||
template <> struct GetElementTypeById<Element::Id::MkCodecID> { using type = Latin1StringElement; };
|
||||
template <> struct GetElementTypeById<Element::Id::MkTagTargetTypeValue> { using type = UIntElement; };
|
||||
template <> struct GetElementTypeById<Element::Id::MkTagTrackUID> { using type = UIntElement; };
|
||||
template <> struct GetElementTypeById<Element::Id::MkAttachedFileUID> { using type = UIntElement; };
|
||||
template <> struct GetElementTypeById<Element::Id::MkSeekPosition> { using type = UIntElement; };
|
||||
template <> struct GetElementTypeById<Element::Id::MkTimestampScale> { using type = UIntElement; };
|
||||
template <> struct GetElementTypeById<Element::Id::MkBitDepth> { using type = UIntElement; };
|
||||
template <> struct GetElementTypeById<Element::Id::MkChannels> { using type = UIntElement; };
|
||||
template <> struct GetElementTypeById<Element::Id::MkCueTime> { using type = UIntElement; };
|
||||
template <> struct GetElementTypeById<Element::Id::MkCueTrack> { using type = UIntElement; };
|
||||
template <> struct GetElementTypeById<Element::Id::MkCueClusterPosition> { using type = UIntElement; };
|
||||
template <> struct GetElementTypeById<Element::Id::MkCueRelativePosition> { using type = UIntElement; };
|
||||
template <> struct GetElementTypeById<Element::Id::MkCueDuration> { using type = UIntElement; };
|
||||
template <> struct GetElementTypeById<Element::Id::MkCueBlockNumber> { using type = UIntElement; };
|
||||
template <> struct GetElementTypeById<Element::Id::MkCueCodecState> { using type = UIntElement; };
|
||||
template <> struct GetElementTypeById<Element::Id::MkCueRefTime> { using type = UIntElement; };
|
||||
template <> struct GetElementTypeById<Element::Id::MkTagsLanguageDefault> { using type = UIntElement; };
|
||||
template <> struct GetElementTypeById<Element::Id::MkAttachedFileData> { using type = BinaryElement; };
|
||||
template <> struct GetElementTypeById<Element::Id::MkSeekID> { using type = BinaryElement; };
|
||||
template <> struct GetElementTypeById<Element::Id::MkTagBinary> { using type = BinaryElement; };
|
||||
template <> struct GetElementTypeById<Element::Id::MkCodecState> { using type = BinaryElement; };
|
||||
template <> struct GetElementTypeById<Element::Id::MkDuration> { using type = FloatElement; };
|
||||
template <> struct GetElementTypeById<Element::Id::MkTitle> { using type = UTF8StringElement; };
|
||||
template <> struct GetElementTypeById<Element::Id::MkSamplingFrequency> { using type = FloatElement; };
|
||||
template <> struct GetElementTypeById<Element::Id::MkSeekHead> { using type = MkSeekHead; };
|
||||
template <> struct GetElementTypeById<Element::Id::MkChapters> { using type = MkChapters; };
|
||||
template <> struct GetElementTypeById<Element::Id::MkEditionEntry> { using type = MasterElement; };
|
||||
template <> struct GetElementTypeById<Element::Id::MkEditionUID> { using type = UIntElement; };
|
||||
template <> struct GetElementTypeById<Element::Id::MkEditionFlagDefault> { using type = UIntElement; };
|
||||
template <> struct GetElementTypeById<Element::Id::MkEditionFlagOrdered> { using type = UIntElement; };
|
||||
template <> struct GetElementTypeById<Element::Id::MkChapterAtom> { using type = MasterElement; };
|
||||
template <> struct GetElementTypeById<Element::Id::MkChapterUID> { using type = UIntElement; };
|
||||
template <> struct GetElementTypeById<Element::Id::MkChapterTimeStart> { using type = UIntElement; };
|
||||
template <> struct GetElementTypeById<Element::Id::MkChapterTimeEnd> { using type = UIntElement; };
|
||||
template <> struct GetElementTypeById<Element::Id::MkChapterFlagHidden> { using type = UIntElement; };
|
||||
template <> struct GetElementTypeById<Element::Id::MkChapterDisplay> { using type = MasterElement; };
|
||||
template <> struct GetElementTypeById<Element::Id::MkChapString> { using type = UTF8StringElement; };
|
||||
template <> struct GetElementTypeById<Element::Id::MkChapLanguage> { using type = Latin1StringElement; };
|
||||
template <> struct GetElementTypeById<Element::Id::VoidElement> { using type = VoidElement; };
|
||||
|
||||
template <Element::Id ID, typename T=typename GetElementTypeById<ID>::type>
|
||||
const T *element_cast(const std::unique_ptr<Element> &ptr)
|
||||
{
|
||||
return static_cast<const T *>(ptr.get());
|
||||
}
|
||||
|
||||
template <Element::Id ID, typename T=typename GetElementTypeById<ID>::type>
|
||||
std::unique_ptr<T> element_cast(std::unique_ptr<Element> &&ptr)
|
||||
{
|
||||
return std::unique_ptr<T>(static_cast<T *>(ptr.release()));
|
||||
}
|
||||
|
||||
template <Element::Id ID, typename T=typename GetElementTypeById<ID>::type>
|
||||
std::unique_ptr<T> make_unique_element(Element::Id id, int sizeLength, offset_t dataSize, offset_t offset)
|
||||
{
|
||||
return std::make_unique<T>(id, sizeLength, dataSize, offset);
|
||||
}
|
||||
|
||||
template <Element::Id ID, typename T=typename GetElementTypeById<ID>::type>
|
||||
std::unique_ptr<T> make_unique_element()
|
||||
{
|
||||
return std::make_unique<T>(ID, 0, 0, 0);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
||||
109
taglib/matroska/ebml/ebmlfloatelement.cpp
Normal file
109
taglib/matroska/ebml/ebmlfloatelement.cpp
Normal file
@@ -0,0 +1,109 @@
|
||||
/***************************************************************************
|
||||
copyright : (C) 2025 by Urs Fleisch
|
||||
email : ufleisch@users.sourceforge.net
|
||||
***************************************************************************/
|
||||
|
||||
/***************************************************************************
|
||||
* This library is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU Lesser General Public License version *
|
||||
* 2.1 as published by the Free Software Foundation. *
|
||||
* *
|
||||
* This library is distributed in the hope that it will be useful, but *
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
||||
* Lesser General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Lesser General Public *
|
||||
* License along with this library; if not, write to the Free Software *
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
|
||||
* 02110-1301 USA *
|
||||
* *
|
||||
* Alternatively, this file is available under the Mozilla Public *
|
||||
* License Version 1.1. You may obtain a copy of the License at *
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#include "ebmlfloatelement.h"
|
||||
#include "ebmlutils.h"
|
||||
#include "tbytevector.h"
|
||||
#include "tfile.h"
|
||||
#include "tdebug.h"
|
||||
|
||||
using namespace TagLib;
|
||||
|
||||
EBML::FloatElement::FloatElement(Id id, int sizeLength, offset_t dataSize):
|
||||
Element(id, sizeLength, dataSize)
|
||||
{
|
||||
}
|
||||
|
||||
EBML::FloatElement::FloatElement(Id id, int sizeLength, offset_t dataSize, offset_t):
|
||||
Element(id, sizeLength, dataSize)
|
||||
{
|
||||
}
|
||||
|
||||
EBML::FloatElement::FloatElement(Id id):
|
||||
FloatElement(id, 0, 0)
|
||||
{
|
||||
}
|
||||
|
||||
EBML::FloatElement::FloatVariantType EBML::FloatElement::getValue() const
|
||||
{ return value; }
|
||||
|
||||
double EBML::FloatElement::getValueAsDouble(double defaultValue) const
|
||||
{
|
||||
if(std::holds_alternative<double>(value)) {
|
||||
// get_if() used instead of get() to support restricted compilers
|
||||
return *std::get_if<double>(&value);
|
||||
}
|
||||
if(std::holds_alternative<float>(value)) {
|
||||
// get_if() used instead of get() to support restricted compilers
|
||||
return *std::get_if<float>(&value);
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
void EBML::FloatElement::setValue(FloatVariantType val)
|
||||
{
|
||||
value = val;
|
||||
}
|
||||
|
||||
bool EBML::FloatElement::read(File &file)
|
||||
{
|
||||
const ByteVector buffer = file.readBlock(dataSize);
|
||||
if(buffer.size() != dataSize) {
|
||||
debug("Failed to read EBML Float element");
|
||||
return false;
|
||||
}
|
||||
|
||||
if(dataSize == 0) {
|
||||
value = std::monostate();
|
||||
}
|
||||
else if(dataSize == 4) {
|
||||
value = buffer.toFloat32BE(0);
|
||||
}
|
||||
else if(dataSize == 8) {
|
||||
value = buffer.toFloat64BE(0);
|
||||
}
|
||||
else {
|
||||
debug("Invalid size for EBML Float element");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
ByteVector EBML::FloatElement::render()
|
||||
{
|
||||
ByteVector data;
|
||||
if(std::holds_alternative<double>(value)) {
|
||||
// get_if() used instead of get() to support restricted compilers
|
||||
data = ByteVector::fromFloat64BE(*std::get_if<double>(&value));
|
||||
}
|
||||
else if(std::holds_alternative<float>(value)) {
|
||||
// get_if() used instead of get() to support restricted compilers
|
||||
data = ByteVector::fromFloat32BE(*std::get_if<float>(&value));
|
||||
}
|
||||
ByteVector buffer = renderId();
|
||||
buffer.append(renderVINT(data.size(), 0));
|
||||
buffer.append(data);
|
||||
return buffer;
|
||||
}
|
||||
59
taglib/matroska/ebml/ebmlfloatelement.h
Normal file
59
taglib/matroska/ebml/ebmlfloatelement.h
Normal file
@@ -0,0 +1,59 @@
|
||||
/***************************************************************************
|
||||
copyright : (C) 2025 by Urs Fleisch
|
||||
email : ufleisch@users.sourceforge.net
|
||||
***************************************************************************/
|
||||
|
||||
/***************************************************************************
|
||||
* This library is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU Lesser General Public License version *
|
||||
* 2.1 as published by the Free Software Foundation. *
|
||||
* *
|
||||
* This library is distributed in the hope that it will be useful, but *
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of *
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
|
||||
* Lesser General Public License for more details. *
|
||||
* *
|
||||
* You should have received a copy of the GNU Lesser General Public *
|
||||
* License along with this library; if not, write to the Free Software *
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA *
|
||||
* 02110-1301 USA *
|
||||
* *
|
||||
* Alternatively, this file is available under the Mozilla Public *
|
||||
* License Version 1.1. You may obtain a copy of the License at *
|
||||
* http://www.mozilla.org/MPL/ *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef TAGLIB_EBMLFLOATELEMENT_H
|
||||
#define TAGLIB_EBMLFLOATELEMENT_H
|
||||
#ifndef DO_NOT_DOCUMENT
|
||||
|
||||
#include <variant>
|
||||
#include "ebmlelement.h"
|
||||
|
||||
namespace TagLib {
|
||||
class File;
|
||||
|
||||
namespace EBML {
|
||||
class FloatElement : public Element
|
||||
{
|
||||
public:
|
||||
using FloatVariantType = std::variant<std::monostate, float, double>;
|
||||
|
||||
FloatElement(Id id, int sizeLength, offset_t dataSize);
|
||||
FloatElement(Id id, int sizeLength, offset_t dataSize, offset_t);
|
||||
explicit FloatElement(Id id);
|
||||
|
||||
FloatVariantType getValue() const;
|
||||
double getValueAsDouble(double defaultValue = 0.0) const;
|
||||
void setValue(FloatVariantType val);
|
||||
bool read(File &file) override;
|
||||
ByteVector render() override;
|
||||
|
||||
private:
|
||||
FloatVariantType value;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif
|
||||
127
taglib/matroska/ebml/ebmlmasterelement.cpp
Normal file
127
taglib/matroska/ebml/ebmlmasterelement.cpp
Normal file
@@ -0,0 +1,127 @@
|
||||
/***************************************************************************
|
||||
* 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 "ebmlmasterelement.h"
|
||||
#include "ebmlvoidelement.h"
|
||||
#include "ebmlutils.h"
|
||||
#include "tfile.h"
|
||||
|
||||
using namespace TagLib;
|
||||
|
||||
EBML::MasterElement::MasterElement(Id id, int sizeLength, offset_t dataSize, offset_t offset):
|
||||
Element(id, sizeLength, dataSize), offset(offset)
|
||||
{
|
||||
}
|
||||
|
||||
EBML::MasterElement::MasterElement(Id id):
|
||||
Element(id, 0, 0), offset(0)
|
||||
{
|
||||
}
|
||||
|
||||
EBML::MasterElement::~MasterElement() = default;
|
||||
|
||||
offset_t EBML::MasterElement::getOffset() const
|
||||
{
|
||||
return offset;
|
||||
}
|
||||
|
||||
void EBML::MasterElement::appendElement(std::unique_ptr<Element> &&element)
|
||||
{
|
||||
elements.push_back(std::move(element));
|
||||
}
|
||||
|
||||
std::list<std::unique_ptr<EBML::Element>>::iterator EBML::MasterElement::begin()
|
||||
{
|
||||
return elements.begin();
|
||||
}
|
||||
|
||||
std::list<std::unique_ptr<EBML::Element>>::iterator EBML::MasterElement::end()
|
||||
{
|
||||
return elements.end();
|
||||
}
|
||||
|
||||
std::list<std::unique_ptr<EBML::Element>>::const_iterator EBML::MasterElement::begin() const
|
||||
{
|
||||
return elements.begin();
|
||||
}
|
||||
|
||||
std::list<std::unique_ptr<EBML::Element>>::const_iterator EBML::MasterElement::end() const
|
||||
{
|
||||
return elements.end();
|
||||
}
|
||||
|
||||
std::list<std::unique_ptr<EBML::Element>>::const_iterator EBML::MasterElement::cbegin() const
|
||||
{
|
||||
return elements.cbegin();
|
||||
}
|
||||
|
||||
std::list<std::unique_ptr<EBML::Element>>::const_iterator EBML::MasterElement::cend() const
|
||||
{
|
||||
return elements.cend();
|
||||
}
|
||||
|
||||
offset_t EBML::MasterElement::getPadding() const
|
||||
{
|
||||
return padding;
|
||||
}
|
||||
|
||||
void EBML::MasterElement::setPadding(offset_t numBytes)
|
||||
{
|
||||
padding = numBytes;
|
||||
}
|
||||
|
||||
offset_t EBML::MasterElement::getMinRenderSize() const
|
||||
{
|
||||
return minRenderSize;
|
||||
}
|
||||
|
||||
void EBML::MasterElement::setMinRenderSize(offset_t minimumSize)
|
||||
{
|
||||
minRenderSize = minimumSize;
|
||||
}
|
||||
|
||||
bool EBML::MasterElement::read(File &file)
|
||||
{
|
||||
const offset_t maxOffset = file.tell() + dataSize;
|
||||
std::unique_ptr<Element> element;
|
||||
while((element = findNextElement(file, maxOffset))) {
|
||||
if(!element->read(file))
|
||||
return false;
|
||||
elements.push_back(std::move(element));
|
||||
}
|
||||
return file.tell() == maxOffset;
|
||||
}
|
||||
|
||||
ByteVector EBML::MasterElement::render()
|
||||
{
|
||||
ByteVector buffer = renderId();
|
||||
ByteVector data;
|
||||
for(const auto &element : elements)
|
||||
data.append(element->render());
|
||||
dataSize = data.size();
|
||||
buffer.append(renderVINT(dataSize, 0));
|
||||
buffer.append(data);
|
||||
if(minRenderSize) {
|
||||
if(const auto bufferSize = buffer.size();
|
||||
minRenderSize >= bufferSize + MIN_VOID_ELEMENT_SIZE)
|
||||
buffer.append(VoidElement::renderSize(minRenderSize - bufferSize));
|
||||
}
|
||||
return buffer;
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user